How to deserialize a scala tree with JSON4S

跟風遠走 提交于 2019-12-25 00:23:10

问题


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? but it doesn't deal with trees.

This the code of my test with a standard 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.


回答1:


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.

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. The toTree method recursively converts the JsTree to the appropriate Tree subclass based on which fields are actually present.

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)


来源:https://stackoverflow.com/questions/54322448/how-to-deserialize-a-scala-tree-with-json4s

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!