Spray-json десериализует вложенный объект

Как правильно десериализовать вложенные объекты в 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

Кстати, как спрей-json помогает сериализовать карту полей (с вложенной картой для вложенных объектов)?


person user3103600    schedule 11.02.2014    source источник


Ответы (3)


В приведенном ниже примере демонстрируется JSON -> Абстрактное дерево синтаксиса -> Классы случаев Scala и обратно с настраиваемыми именами полей и поддержкой необязательных членов класса случаев. Пример взят из документации по spray-json по адресу https://github.com/spray/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)
person Dave Swartz    schedule 12.02.2014
comment
Как насчет Option[Color]? Таким образом, класс Team становится классом case Team (имя: String, цвет: Option[Color]) - person user3103600; 13.02.2014
comment
Можете рассказать, что вы уже пробовали? Была ли полезна документация по spray-json? - person Dave Swartz; 13.02.2014
comment
Я хочу настроить имена полей во время сериализации. - person user3103600; 14.02.2014
comment
Я прошу вас принять мой ответ на исходный вопрос, а затем задать последующие вопросы в отдельном сообщении в соответствии с руководством на meta.stackexchange.com/ а/39224. - person Dave Swartz; 14.02.2014
comment
Чувак, я не могу принять это как ответ, потому что это уводит меня еще дальше от того, что у меня уже есть, то есть от настраиваемых имен серийных полей. Если вы прочтете мой отрезанный вопрос, вы заметите следующую строку: Color(name, red.toInt, green.toInt, blue.toInt, null) //gotta replace null with correct deserializer Мне нужно знать, как обрабатывать нулевые значения во время (де)сериализации. - person user3103600; 15.02.2014
comment
Я предположил, что вы просто играли с этим кодом, чтобы понять спрей-json, что было дополнительно подкреплено вашим вопросом «Как правильно десериализовать вложенные объекты в спрей-json?». Я предлагаю в будущем использовать более конкретные вопросы. Например, у меня есть следующие классы. Код не компилируется и выдает следующую ошибку... Чтобы было ясно, мне нужно обрабатывать пустые значения и я хочу использовать настраиваемые имена полей для сериализации/десериализации. Сейчас я обновлю ответ, указав имена настраиваемых полей и обработку нулей. Все, что я делаю, это читаю документы FYI. - person Dave Swartz; 15.02.2014
comment
Я просмотрел документы и понял, что вы должны использовать Option[T], а не null. Но как опустить и/или (де) сериализовать и т. д., неясно. - person user3103600; 15.02.2014
comment
Документация там действительно бесполезна. Специально для новичков в Scala. В любом случае, я попробую этот фрагмент и отмечу, что он принят. Спасибо - person user3103600; 15.02.2014
comment
Документация Spray JSON мало чем отличается от многих других проектов с открытым исходным кодом, которые родились в результате чьего-то хобби или разочарования по поводу другой библиотеки. Документы преобразуют класс case в JSON и из него. Большое дело. В реальном мире вложенный json часто необходимо преобразовать в объекты, которые не являются нашими собственными и, следовательно, не могут быть легко вписаны в типичные примеры hello world. - person Abhijit Sarkar; 30.01.2016
comment
что, если есть несколько вложенных классов (скажем, Color и Size внутри Team)? - person techkuz; 08.08.2020

На ваш оставшийся вопрос - как повторно использовать преобразования JSON в типе упаковки:

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

Я бы изменил это на:

"person-field" -> p.toJson

Таким образом, вы позволяете самому классу case Person JSONify вместо того, чтобы вводить другой способ в объект-обертку. СУХОЙ и проще.

И:

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])

Если есть проблемы, пожалуйста, обратитесь за дополнительной помощью. У меня есть аналогичная структура в моем собственном проекте, но я не пытался запускать их в этом контексте.

person akauppi    schedule 02.07.2015

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
}
person Amar Kumar    schedule 16.04.2021