在Scala中遍历JSON并添加指定新字段的技术问询
使用Circe Optics批量新增JSON字段(含数组场景)
需求说明
给定原始JSON:
{ "A": { "NestedArray": [ { "AlreadyHere2": "x" }, { "AlreadyHere2": "x" }, { "AlreadyHere2": "x" } ] } }
需要根据指定的JSON路径列表,为每个路径对应的位置添加值为CONSTANT的字段,最终生成目标JSON:
{ "A": { "A_1": "CONSTANT", "A_2": "CONSTANT", "NestedArray": [ { "AlreadyHere2": "x", "arrayValue": "CONSTANT" }, { "AlreadyHere2": "x", "arrayValue": "CONSTANT" }, { "AlreadyHere2": "x", "arrayValue": "CONSTANT" } ] } }
指定路径示例:
val list = List("A.A_1", "A.A_2", "A.NestedArray.arrayValue")
要求实现需支持后续新增路径,不能局限于当前JSON结构,需同时处理普通字段新增和数组元素批量新增的场景。
实现方案
1. 引入Circe依赖
在Scala项目的build.sbt中添加以下依赖:
libraryDependencies ++= Seq( "io.circe" %% "circe-core" % "0.14.6", "io.circe" %% "circe-optics" % "0.14.6", "io.circe" %% "circe-parser" % "0.14.6" )
2. 核心代码实现
import io.circe._ import io.circe.parser._ import io.circe.optics.JsonPath._ object JsonFieldAdder { private val CONSTANT_VALUE = Json.fromString("CONSTANT") def addFieldsToJson(originalJsonStr: String, paths: List[String]): Either[ParsingFailure, Json] = { parse(originalJsonStr).map { baseJson => paths.foldLeft(baseJson) { (currentJson, path) => val pathSegments = path.split('.').toList pathSegments match { // 处理普通对象字段:路径最后一段是要新增的字段名,前面是对象层级 case parentSegments :+ newField => // 构建父对象的路径定位器 val parentPath = parentSegments.foldLeft(root) { (path, segment) => path.selectDynamic(segment) } // 判断父路径是否指向数组 val isArrayTarget = currentJson.hcursor .downField(parentSegments.head) .downFields(parentSegments.tail: _*) .focus .exists(_.isArray) if (isArrayTarget) { // 数组场景:遍历数组每个元素,添加字段 parentPath.each.json.modify(_.mapObject(_.add(newField, CONSTANT_VALUE)))(currentJson) } else { // 普通对象场景:给父对象添加新字段 parentPath.json.modify(_.mapObject(_.add(newField, CONSTANT_VALUE)))(currentJson) } } } } } // 测试用例 def main(args: Array[String]): Unit = { val rawJson = """{ "A": { "NestedArray": [ { "AlreadyHere2": "x" }, { "AlreadyHere2": "x" }, { "AlreadyHere2": "x" } ] } }""" val targetPaths = List("A.A_1", "A.A_2", "A.NestedArray.arrayValue") addFieldsToJson(rawJson, targetPaths).foreach(println) } }
3. 关键逻辑说明
- 路径解析:将每个路径按
.拆分为层级段,拆分出父对象路径和要新增的字段名 - 动态类型判断:通过Circe的
hcursor判断父路径指向的是数组还是普通对象,适配不同场景 - 普通字段新增:定位到目标父对象后,调用
mapObject直接添加新字段 - 数组元素批量处理:使用
each操作符遍历数组所有元素,为每个元素统一添加指定字段 - 扩展性:后续新增的路径只要符合
父层级.字段名格式,无论父层级是普通对象还是数组,都能自动适配处理
内容的提问来源于stack exchange,提问作者Jackson Li
相关产品推荐
相关产品推荐

