喷雾式JSON反序列化嵌套对象

10

如何在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将数据序列化为字段映射(包括嵌套对象)?

3个回答

18

下面的示例演示了JSON->抽象语法树->Scala Case类之间的相互转换,同时支持自定义字段名称和可选Case类成员。该示例源自于版本1.2.5的spray-json文档,位于https://github.com/spray/spray-json

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]) - user3103600
你能告诉我你已经尝试过什么吗?spray-json 的文档有帮助吗? - Dave Swartz
我请求您接受我的答案来回答原始问题,然后按照http://meta.stackexchange.com/a/39224的指导,在单独的帖子中提出后续问题。 - Dave Swartz
1
我认为你只是在玩弄这个代码以理解spray-json,这也被你的问题"如何在spray-json中正确反序列化嵌套对象?"所加强。我建议你将来使用更具体的问题。例如,"我有以下类。代码无法编译并产生以下错误... 为了明确起见,我需要处理null,并想要使用自定义字段名称进行序列化/反序列化。"我现在会使用自定义字段名称和处理null更新答案。FYI,我只是在阅读文档。 - Dave Swartz
1
文档真的没有什么帮助。特别是对于Scala新手来说。不管怎样,我会尝试这个片段并标记为已接受。谢谢。 - user3103600
显示剩余5条评论

1

关于您的另一个问题——如何在包装类型中重复使用JSON转换:

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

我会将这个改为:

我会改成这样:

"person-field" -> p.toJson

这样,你可以让Person案例类自我JSON化,而不是引入包装对象中的另一种方式。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])

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

0
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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接