Scala解析含重复context键的JSON的最优方案咨询
Scala 解析含重复键JSON的最优方案
org.json.JSONException: Duplicate key "context"报错的核心原因是标准JSON规范禁止同一对象层级下出现重复键,默认严格校验的解析库都会直接抛出异常。
方案1:使用Circe(Scala生态首选函数式JSON库)
Circe原生支持自定义重复键处理策略,开启重复键累加配置后可自动把多个context字段收集为列表:
sbt依赖配置:
libraryDependencies ++= Seq( "io.circe" %% "circe-core" % "0.14.6", "io.circe" %% "circe-parser" % "0.14.6", "io.circe" %% "circe-generic" % "0.14.6" )
解析代码示例:
import io.circe._ import io.circe.parser._ import io.circe.generic.semiauto._ // 自定义解析配置:允许重复键,重复值自动累加为数组 val customParserConfig = Parser.defaultConfig.withAllowDuplicateKeys(true) val jsonStr = "替换为你的JSON字符串内容" // 定义数据结构 case class Context(researchID: String, researchtitle: String, telconfdoclinkid: Option[String]) case class Production( meta_id: String, endeca_id: String, Title: String, Subtitle: Option[String], context: List[Context], // 重复context会被解析为列表 uri: String, ssd: String, id: String ) case class Root(Production: Production) // 自动派生解码器 implicit val contextDecoder: Decoder[Context] = deriveDecoder[Context] implicit val productionDecoder: Decoder[Production] = deriveDecoder[Production] implicit val rootDecoder: Decoder[Root] = deriveDecoder[Root] // 执行解析 parse(jsonStr, customParserConfig) match { case Right(json) => json.as[Root] match { case Right(root) => println(root.Production.context) // 直接获取所有context列表 case Left(decodeErr) => println(s"数据解码失败: $decodeErr") } case Left(parseErr) => println(s"JSON结构解析失败: $parseErr") }
方案2:使用Jackson Scala模块
如果项目已依赖Jackson生态,可通过配置实现重复键兼容:
sbt依赖配置:
libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.15.2"
解析代码示例:
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.fasterxml.jackson.databind.ObjectMapper val mapper = new ObjectMapper() mapper.registerModule(DefaultScalaModule) // 开启单值转数组配置,适配重复键合并为数组的场景 mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) // 配置重复键处理器,自动把重复值存入数组 mapper.getFactory.setCodec(mapper) // 定义数据结构,context用List接收 case class Context(researchID: String, researchtitle: String, telconfdoclinkid: Option[String]) case class Production( meta_id: String, endeca_id: String, Title: String, Subtitle: Option[String], context: List[Context], uri: String, ssd: String, id: String ) case class Root(Production: Production) val result = mapper.readValue(jsonStr, classOf[Root]) println(result.Production.context)
方案3:org.json库兼容方案
如果无法替换现有org.json依赖,可开启内置的重复键累加开关:
import org.json.JSONObject import org.json.JSONTokener val tokener = new JSONTokener(jsonStr) // 开启重复键累加模式,遇到重复键自动将值转为数组存储 tokener.setAccumulateValues(true) val rootObj = new JSONObject(tokener) val production = rootObj.getJSONObject("Production") // 此时context为JSONArray类型,遍历取值即可 val contextArr = production.getJSONArray("context") for (i <- 0 until contextArr.length()) { val context = contextArr.getJSONObject(i) println(context.getString("researchID")) }
长期优化建议
重复键不符合标准JSON规范,跨工具、跨语言传输都存在兼容风险,最优方案是推动上游数据源修改结构,直接将多个context字段调整为"context": [{},{},{}]的数组格式,从根源避免兼容问题。
内容的提问来源于stack exchange,提问作者Raghavan
相关产品推荐
相关产品推荐

