【发布时间】:2021-09-14 10:23:08
【问题描述】:
我目前正在尝试为数据管道创建一个封装各种配置方面(以 json 提供)的类和子类;我还在学习scala。我正在使用 Play 框架库来解析 json 字符串输入 - https://www.playframework.com/documentation/2.8.x/ScalaJson
我有一些目前可以运行的代码,但其中有几个方面感觉不对,而且这感觉不是正确的方法。
应用程序的工作流需要获取一个 json 字符串,针对各种相似但略有不同的结构对其进行解析和验证,然后使这些值可用于下游处理的其他位(例如,如果文件类型是 csv 文件,因此设置一些配置,如果它是一个 json 文件,请改为这样做);但重要的是要注意这是一个动态过程。在我看来,这似乎是案例类的完美用例,但我感觉我误解了它们的用途。
所以我有一个密封的(以确保所有匹配项都是已知的)抽象类 FileConfig,目前有两个子类,DelimitedConfig 和 JsonConfig。 DelimitedConfig 类还使用了一个额外的案例类DelimitedFileTypeDetails,这本质上是两者之间的主要区别,但是随着我的继续,还会添加其他偏差;而且我也有所有三个类的伴随对象,以便利用播放框架格式方法:
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]
}
在这些之后,我还有一个函数可以使用播放框架的 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的抽象成员即可。
标签: json scala playframework subclass case-class