Scala:在匹配语句后返回基类时访问案例类字段



>我目前正在尝试创建一个类和子类,以封装数据管道的各种配置方面(以 json 形式提供);我也在学习 scala。 我正在使用Play框架库来解析json字符串输入 - https://www.playframework.com/documentation/2.8.x/ScalaJson

我有一些当前有效的代码,但它有几个方面感觉不对,这感觉不像是正确的方法。

应用程序的工作流需要获取一个json字符串,根据各种相似但略有不同的结构对其进行解析和验证,然后使这些值可供其他下游处理位访问(例如,如果文件类型是csv文件,请设置一些配置,如果是json文件,请改为这样做);但重要的是要注意,这是一个动态过程。 在我看来,这是案例类的完美用例,但我有一种感觉,我误解了它们的使用。

所以我有一个密封的(为了确保所有匹配都是已知的)抽象类FileConfig,目前有两个子类,DelimitConfig和JsonConfig。 DelimitConfig 类还使用了额外的案例类 DelimitFileTypeDetails,这在这一点上本质上是两者之间的主要区别,但随着我的继续,还会添加其他偏差;我还有所有三个类的配套对象,以便利用 play 框架格式方法:

sealed abstract class FileConfig
case class DelimitedFileTypeDetails (delimiter: String, hasColumnHeaders: Boolean, rowsToSkip: Int)
object DelimitedFileTypeDetails {
implicit val format = Json.format[DelimitedFileTypeDetails]
}
case class DelimitedConfig (deltaLakeDatabricksMountPoint: String,
restrictedDeltaLakeDatabricksMountPoint: String,
notebookToRun: String,
rowHashExclusionColumns: List[String],
deltaDatabase: String,
mergeKeySQLClause: String,
mergeUpdateFilterSQLClause: String,
fileToMerge: String,
containsPIIData: Boolean,
piiEntityNames: List[String],
transformsClass: String,
transformsMethod: String,
extractStartTime: String,
fileType: String,
fileTypeDetails: DelimitedFileTypeDetails
) extends FileConfig {
require(fileType == "Delimited", "fileType must be 'Delimited' for class DelimitedConfig")
}
object DelimitedConfig {
implicit val format = Json.format[DelimitedConfig]
}
case class JsonConfig (deltaLakeDatabricksMountPoint: String,
restrictedDeltaLakeDatabricksMountPoint: String,
notebookToRun: String,
rowHashExclusionColumns: List[String],
deltaDatabase: String,
mergeKeySQLClause: String,
mergeUpdateFilterSQLClause: String,
fileToMerge: String,
containsPIIData: Boolean,
piiEntityNames: List[String],
transformsClass: String,
transformsMethod: String,
extractStartTime: String,
fileType: String) extends FileConfig {
require(fileType == "Json", "fileType must be 'Json' for class JsonConfig")
}
object JsonConfig {
implicit val format = Json.format[JsonConfig]
}
object FileConfig {
implicit val format = Json.format[FileConfig]
}

在这些之后,我还有一个函数,然后使用 play 框架的 validate 方法对任何提供的 json 字符串执行匹配;这将是问题一 - 这感觉真的很笨拙,有没有办法修改对象的嵌套评估? 本质上,它检查它是否满足第一个子类,如果不是(JsError 是结果),则检查下一个子类,依此类推:

object FileConfigParser {
/**
* Parse the provided json to ensure it matches the specified format.
*
* @param jsonString - a string representation of the json
* @return - FileConfig of the parsed json
*/
def parseFileConfig(jsonString: String): FileConfig = {
val json = Json.parse(jsonString)
json.validate[DelimitedConfig] match {
case s: JsSuccess[DelimitedConfig] => s.get.asInstanceOf[DelimitedConfig]
case e: JsError => {
json.validate[JsonConfig] match {
case s: JsSuccess[JsonConfig] => s.get.asInstanceOf[JsonConfig]
case e: JsError => {
throw new Error("Invalid json provided")
}
}
}
}
}
}

这一切都工作正常,当我运行函数时,验证成功;但是,使用 parseFileConfig 函数的结果,我无法访问类字段,直到我在主对象中运行进一步匹配:-

val jsonString = """{
"sourceDatabricksMountPoint": "/mnt/rawlayer/myData/",
"deltaLakeDatabricksMountPoint": "/mnt/delta/",
"restrictedDeltaLakeDatabricksMountPoint": "/mnt/sensitive_delta/",
"notebookToRun":"/Ingestion/LoadDeltaLake",
"rowHashExclusionColumns": ["RowLastUpdatedTime"],
"deltaDatabase": "myDb",
"restrictedDeltaDatabase": "NICE_WFM_RESTRICTED",
"deltaTableName": "myTable",
"mergeKeySQLClause": "s.date=t.date AND s.ID=t.ID",
"mergeUpdateFilterSQLClause":"s.RowHash <> t.RowHash",
"fileToMerge": "myFile.txt",
"fileType":"Delimited",
"fileTypeDetails": {"delimiter": "|",
"hasColumnHeaders": true,
"rowsToSkip": 1
},
"containsPIIData": true,
"piiEntityNames": ["name", "surname"],
"transformsMethod": "convertDateTimeColumns",
"transformsClass": "com.example.transforms.CustomTransform",
"extractStartTime":"2020-12-10T00:00:00.000000"
}"""
val fileConfig: FileConfig = parseFileConfig(jsonString)
println(fileConfig.getClass())
// if you uncomment this it doesn't work
// println(fileConfig.fileType)
// but this does work
val fileType = fileConfig match {
case d: DelimitedConfig => d.fileType
case j: JsonConfig => j.fileType
}
println(fileType)

必须对主代码中的每个字段运行匹配感觉有点乏味,有没有办法在类中执行此操作,或者让字段从类中访问? 或。。。。我只是做错了这一切,并且有更好的方法吗?

提前感谢,

马 特

如果每个FileConfig都有一个给定类型的成员(例如fileType),您可以将该成员放在FileConfig

sealed abstract class FileConfig {
def fileType: String
}

然后在扩展FileConfig的各种案例类中,您将override val fileType: String.

也就是说,我会重新考虑让fileType成为case class中的一个领域。 如果每个DelimitedConfig都必须fileType"Delimited",那么将其作为def然后定义一个Format[FileConfig]可能是有意义的,该在反序列化时查看fileType字段,并在序列化时将其注入JSON。 自从我使用 Play JSON 以来已经有一段时间了,所以我不记得那里的技术。

最新更新