【问题标题】:Spray-json deserializing nested objectSpray-json反序列化嵌套对象
【发布时间】:2025-12-10 13:25:02
【问题描述】:

如何在 spray-json 中正确反序列化嵌套对象?

    import spray.json._

    case class Person(name: String)

    case class Color(n: String, r: Int, g: Int, b: Int, p: Person)

    object MyJsonProtocol extends DefaultJsonProtocol {

      implicit object ColorJsonFormat extends RootJsonFormat[Color] {
        def write(c: Color) = JsObject(
          "color-name" -> JsString(c.n),
          "Green" -> JsNumber(c.g),
          "Red" -> JsNumber(c.r),
          "Blue" -> JsNumber(c.b),
          "person-field" -> JsObject("p-name" -> JsString(c.p.name))
        )

        def read(value: JsValue) = {
          value.asJsObject.getFields("color-name", "Red", "Green", "Blue", "person-field") match {
            case Seq(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue), JsObject(person)) =>
              Color(name, red.toInt, green.toInt, blue.toInt, null) //gotta replace null with correct deserializer
            case _ => throw new DeserializationException("Color expected")
          }
        }
      }

    }

    import MyJsonProtocol._

    val jsValue = Color("CadetBlue", 95, 158, 160, Person("guest")).toJson

    jsValue.prettyPrint

    val color = jsValue.convertTo[Color] //person is missing of course

在旁注中,如何将 spray-json 帮助序列化为字段映射(嵌套对象的嵌套映射)?

【问题讨论】:

    标签: json scala spray-json


    【解决方案1】:

    下面的示例演示了 JSON -> 抽象语法树 -> Scala 案例类,并返回自定义字段名称和对可选案例类成员的支持。该示例源自 https://github.com/spray/spray-json 的 spray-json 文档,适用于版本 1.2.5。

    package rando
    
    import spray.json._
    
    case class Color(name: String, red: Int, green: Int, blue: Int)
    
    case class Team(name: String, color: Option[Color])
    
    object MyJsonProtocol extends DefaultJsonProtocol {
      implicit val colorFormat = jsonFormat(Color, "name", "r", "g", "b")
      implicit val teamFormat = jsonFormat(Team, "name", "jersey")
    }
    import MyJsonProtocol._
    
    object GoSox extends App {
      val obj = Team("Red Sox", Some(Color("Red", 255, 0, 0)))
      val ast = obj.toJson
      println(obj)
      println(ast.prettyPrint)
      println(ast.convertTo[Team])
      println("""{ "name": "Red Sox", "jersey": null }""".asJson.convertTo[Team])
      println("""{ "name": "Red Sox" }""".asJson.convertTo[Team])
    }
    

    该示例在执行时输出以下内容:

    Team(Red Sox,Some(Color(Red,255,0,0)))
    {
      "name": "Red Sox",
      "jersey": {
        "name": "Red",
        "r": 255,
        "g": 0,
        "b": 0
      }
    }
    Team(Red Sox,Some(Color(Red,255,0,0)))
    Team(Red Sox,None)
    Team(Red Sox,None)
    

    【讨论】:

    • Option[Color]怎么样?所以 Team 类变成了 case class Team(name: String, color: Option[Color])
    • 你能告诉我你已经尝试过什么吗? Spray-json 文档有用吗?
    • 我想在序列化过程中自定义字段名称。
    • 我以为您只是在玩弄这段代码来理解 spray-json,您的问题“如何在 spray-json 中正确反序列化嵌套对象?”进一步强化了这一点。我建议您将来使用更具体的问题。例如,“我有以下类。代码无法编译并产生以下错误......为了清楚起见,我需要处理空值并希望使用自定义字段名称进行序列化/反序列化。”我现在将使用自定义字段名称和 null 处理来更新答案。我所做的只是阅读文档仅供参考。
    • 那里的文档真的没有帮助。特别适合 Scala 新手。无论如何,我会尝试这个 sn-p 并将其标记为已接受。谢谢
    【解决方案2】:

    关于您剩下的问题 - 如何在包装类型中重用 JSON 转换:

    "person-field" -> JsObject("p-name" -> JsString(c.p.name))
    

    我会将其更改为:

    "person-field" -> p.toJson
    

    这样,您让Person 案例类 JSONify 自身,而不是在包装对象中引入另一种方式。 DRY 更简单。

    还有:

    case Seq(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue), JsObject(person)) => 
      Color(name, red.toInt, green.toInt, blue.toInt, null)
    

    在此处使用.convertTo[Person]

    case Seq(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue), jsv) => 
      Color(name, red.toInt, green.toInt, blue.toInt, jsv.convertTo[Person])
    

    如果有问题,请寻求更多帮助。我在自己的项目中有类似的结构,但没有尝试在这种情况下运行它们。

    【讨论】:

      【解决方案3】:
      import models.{Address, Patient}
      import spray.json._
      
      object Hospital {
      
        object MyJsonProtocol extends DefaultJsonProtocol {
          implicit val address = jsonFormat(Address, "country", "state", "zip")
          implicit val patient = jsonFormat(Patient, "name", "regNumber", "address")
        }
      
        import MyJsonProtocol._
      
        def main(args: Array[String]): Unit = {
          val p1 = Patient(name = "Amar", regNumber = 234, address = Address("IN", "KA", 49))
          println(p1.toJson.sortedPrint)
        }
      
      }
      

      输出

      {
        "address": {
          "country": "IN",
          "state": "KA",
          "zip": 49
        },
        "name": "Amar",
        "regNumber": 234
      }
      

      【讨论】: