简体   繁体   English

如何使用JSON4S反序列化Scala树

[英]How to deserialize a scala tree with JSON4S

Serialization works fine but I have nothing for deserialization. 序列化工作正常,但是我不需要反序列化。 I found interesting solution for abstract class here How to serialize sealed abstract class with Json4s in Scala? 我在这里找到了抽象类的有趣解决方案。 如何在Scala中使用Json4s序列化密封的抽象类? but it doesn't deal with trees. 但它不涉及树木。

This the code of my test with a standard JSON4S : 这是我使用标准JSON4S测试的代码:

import org.json4s._
import org.json4s.native.JsonMethods._
import org.json4s.native.Serialization.{ read, write }
import org.json4s.native.Serialization

abstract class Tree
case class Node(nameN: String, trees: List[Tree]) extends Tree
case class Leaf(nameL: String) extends Tree

object Tree extends App {
  implicit val formats = Serialization.formats(NoTypeHints)

  // object creation to test the serialization
  val root =
    Node(
      "Grand Pavois project",
      List(
        Node(
          "studies",
          List(
            Leaf("preliminary studies"),
            Leaf("detailled studies")
          )
        ),
        Node(
          "realization",
          List(
            Leaf("ground"),
            Leaf("building"),
            Leaf("roof")
          )
        ),
        Node(
          "delivery",
          List(
            Leaf("quality inspection"),
            Leaf("customer delivery")
          )
        )
      )
    )

  val serialized = write(root) // object creation and serialization
  println(s"serialized: $serialized") // print the result, this is OK

  // and now what about deserialization?
  // string creation for deserialization
  // ( it is the same as serialized above, I do like that to trace for the demo)
  val rootString = """
{
  "nameN": "Grand Pavois project",
  "trees": [
    {
      "nameN": "studies",
      "trees": [
        {
          "nameL": "preliminary studies"
        },
        {
          "nameL": "detailled studies"
        }
      ]
    },
    {
      "nameN": "realization",
      "trees": [
        {
          "nameL": "ground"
        },
        {
          "nameL": "building"
        },
        {
          "nameL": "roof"
        }
      ]
    },
    {
      "nameN": "delivery",
      "trees": [
        {
          "nameL": "quality inspection"
        },
        {
          "nameL": "customer delivery"
        }
      ]
    }
  ]
}
"""
//standard deserialization below that produce an error :
// "Parsed JSON values do not match with class constructor"
val rootFromString = read[Tree](rootString)
}

Now I guess the solution is with a custom deserializer probably a recusive one but how to define it? 现在我猜想解决方案是使用自定义解串器,可能是一种可追溯的解决方案,但是如何定义呢? That is the question. 就是那个问题。 Thanks for your help. 谢谢你的帮助。

This solution doesn't use a custom deserializer, but instead creates a type that matches both Node and Leaf and then converts to the appropriate type later. 此解决方案不使用自定义反序列化器,而是创建一个同时匹配NodeLeaf的类型,然后在以后转换为适当的类型。

case class JsTree(nameN: Option[String], nameL: Option[String], trees: Option[List[JsTree]])

def toTree(node: JsTree): Tree = node match {
  case JsTree(Some(name), None, Some(trees)) =>
    Node(name, trees.map(toTree))
  case JsTree(None, Some(name), None) =>
    Leaf(name)
  case _ =>
    throw new IllegalArgumentException
}

val rootFromString = toTree(read[JsTree](rootString))

The JsTree class will match both Node and Leaf values because it has option fields that match all the fields in both classes. JsTree类将匹配NodeLeaf值,因为它具有与两个类中的所有字段都匹配的选项字段。 The toTree method recursively converts the JsTree to the appropriate Tree subclass based on which fields are actually present. toTree方法递归转换JsTree到适当的Tree子类基于哪个字段是实际存在。

Update: Custom serializer 更新:自定义序列化程序

Here is the solution using a custom serializer: 这是使用自定义序列化程序的解决方案:

import org.json4s.JsonDSL._

class TreeSerializer extends CustomSerializer[Tree](format => ({
  case obj: JObject =>
    implicit val formats: Formats = format

    if ((obj \ "trees") == JNothing) {
      Leaf(
        (obj \ "nameL").extract[String]
      )
    } else {
      Node(
        (obj \ "nameN").extract[String],
        (obj \ "trees").extract[List[Tree]]
      )
    }
}, {
  case node: Node =>
    JObject("nameN" -> JString(node.nameN), "trees" -> node.trees.map(Extraction.decompose))
  case leaf: Leaf =>
    "nameL" -> leaf.nameL
}))

Use it like this: 像这样使用它:

implicit val formats: Formats = DefaultFormats + new TreeSerializer

read[Tree](rootString)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM