如何使用Scala Circe-Json编码器生成指定格式JSON并解决嵌套数组对象编码问题
使用Scala Circe生成嵌套结构JSON的解决方案
首先,我来帮你梳理下问题:你想要生成特定嵌套结构的JSON,但目前遇到了两个核心问题:一是Circe找不到隐式编码器导致编译错误,二是你的Case Class设计和目标JSON结构不匹配,没法正确映射嵌套数组和对象。
先解决编译错误:导入Circe自动编码器
你看到的Unspecified value parameter encoder错误,是因为Circe需要隐式的Encoder实例来将Case Class转换为JSON,但你没有导入自动派生编码器的包。解决这个很简单,在代码开头加上:
import io.circe._ import io.circe.generic.auto._ import io.circe.syntax._
同时要确保你的build.sbt里包含Circe的必要依赖:
libraryDependencies ++= Seq( "io.circe" %% "circe-core" % "0.14.6", "io.circe" %% "circe-generic" % "0.14.6", "io.circe" %% "circe-parser" % "0.14.6" )
匹配目标JSON的Case Class设计
目标JSON的结构是多层嵌套的,我们需要逐层设计Case Class来对应。这里有两种方案:类型安全的ADT方案(推荐,避免错误)和简化的Map方案(快速实现)。
方案一:类型安全的ADT方案
这种方案用代数数据类型(ADT)来表示JSON中那些只有单个key的对象,确保类型安全:
import io.circe._ import io.circe.generic.auto._ import io.circe.syntax._ // 对应read.parq中的元素:{"str":""} 或 {"tab":""} sealed trait ReadParqElement case class StrElement(str: String) extends ReadParqElement case class TabElement(tab: String) extends ReadParqElement // 对应read中的parq数组包装 case class ReadParq(parq: List[ReadParqElement]) // 对应read部分 case class Read(read: List[ReadParq]) // 对应transform.transform中的元素:{"col":""} 或 {"col_def":""} sealed trait TransformElement case class ColElement(col: String) extends TransformElement case class ColDefElement(col_def: String) extends TransformElement // 对应transform中的transform数组包装 case class TransformInner(transform: List[TransformElement]) // 对应transform部分 case class Transform(transform: List[TransformInner]) // 对应write.parq中的元素:{"path":""} case class WriteParqPath(path: String) // 对应write中的parq数组包装 case class WriteParq(parq: List[WriteParqPath]) // 对应write部分 case class Write(write: List[WriteParq]) // 对应action1的完整结构 case class Action1(read: Read, transform: Transform, write: Write) // 对应actions部分 case class Actions(actions: Map[String, Action1]) // 最外层根结构 case class Root(actions: Actions) // 为ADT自定义编码器,确保生成单key的JSON对象(而非Circe默认的ADT格式) implicit val readParqElementEncoder: Encoder[ReadParqElement] = Encoder.instance { case StrElement(s) => Json.obj("str" -> Json.fromString(s)) case TabElement(t) => Json.obj("tab" -> Json.fromString(t)) } implicit val transformElementEncoder: Encoder[TransformElement] = Encoder.instance { case ColElement(c) => Json.obj("col" -> Json.fromString(c)) case ColDefElement(cd) => Json.obj("col_def" -> Json.fromString(cd)) } object JsonGenerator extends App { // 构建实例,填充目标值(这里用空字符串对应需求) val read = Read(List(ReadParq(List(StrElement(""), TabElement(""))))) val transform = Transform(List(TransformInner(List(ColElement(""), ColDefElement(""))))) val write = Write(List(WriteParq(List(WriteParqPath(""))))) val action1 = Action1(read, transform, write) val root = Root(Actions(Map("action1" -> action1))) // 生成格式化的JSON并打印 println(root.asJson.spaces2) }
方案二:简化的Map方案
如果追求快速实现,也可以用Map[String, String]来表示那些单key的对象,代价是损失类型安全:
import io.circe._ import io.circe.generic.auto._ import io.circe.syntax._ case class ReadParq(parq: List[Map[String, String]]) case class Read(read: List[ReadParq]) case class TransformInner(transform: List[Map[String, String]]) case class Transform(transform: List[TransformInner]) case class WriteParq(parq: List[Map[String, String]]) case class Write(write: List[WriteParq]) case class Action1(read: Read, transform: Transform, write: Write) case class Actions(actions: Map[String, Action1]) case class Root(actions: Actions) object JsonGenerator extends App { val read = Read(List(ReadParq(List(Map("str" -> ""), Map("tab" -> ""))))) val transform = Transform(List(TransformInner(List(Map("col" -> ""), Map("col_def" -> ""))))) val write = Write(List(WriteParq(List(Map("path" -> ""))))) val action1 = Action1(read, transform, write) val root = Root(Actions(Map("action1" -> action1))) println(root.asJson.spaces2) }
为什么你之前的代码出错?
- 缺少编码器导入:没有导入
circe.generic.auto._,Circe无法自动为你的Case Class生成编码器。 - 类型不匹配:你的
Rparquet接收List[Map[String,String]],但你传入的是List(rpath("hdfs"), rtab("test_table")),这两个是Case Class实例,不是Map,导致类型不兼容。
内容的提问来源于stack exchange,提问作者narman12
相关产品推荐
相关产品推荐

