如何使用 JSON4S 反序列化 Scala 树
How to deserialize a scala tree with JSON4S
序列化工作正常,但我没有反序列化的东西。我在这里 找到了抽象 class 的有趣解决方案,但它不处理树。
这是我使用标准 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)
}
现在我想解决方案是使用自定义反序列化器,可能是一个反序列化器,但如何定义它?就是那个问题。感谢您的帮助。
此解决方案不使用自定义反序列化器,而是创建一个匹配 Node
和 Leaf
的类型,然后再转换为适当的类型。
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))
JsTree
class 将匹配 Node
和 Leaf
值,因为它的选项字段与两个 class 中的所有字段匹配。 toTree
方法根据实际存在的字段递归地将 JsTree
转换为适当的 Tree
subclass。
更新:自定义序列化程序
这是使用自定义序列化程序的解决方案:
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
}))
这样使用:
implicit val formats: Formats = DefaultFormats + new TreeSerializer
read[Tree](rootString)
序列化工作正常,但我没有反序列化的东西。我在这里
这是我使用标准 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)
}
现在我想解决方案是使用自定义反序列化器,可能是一个反序列化器,但如何定义它?就是那个问题。感谢您的帮助。
此解决方案不使用自定义反序列化器,而是创建一个匹配 Node
和 Leaf
的类型,然后再转换为适当的类型。
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))
JsTree
class 将匹配 Node
和 Leaf
值,因为它的选项字段与两个 class 中的所有字段匹配。 toTree
方法根据实际存在的字段递归地将 JsTree
转换为适当的 Tree
subclass。
更新:自定义序列化程序
这是使用自定义序列化程序的解决方案:
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
}))
这样使用:
implicit val formats: Formats = DefaultFormats + new TreeSerializer
read[Tree](rootString)