You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 21:15:07