将 JSON 映射到 Scala 对象的最有效方法是什么?
What is the most efficient way to map JSON to Scala Objects?
我想用 Play 库解析一些 "complex" JSON。
import play.api.libs.json._
让我们假设我从服务器获得了一个包含许多 JSON 对象的数组,我无法对其进行修改。每个条目看起来都与这个相似:
{
"id": 1,
"urn": "urn:article:5",
"key": "post",
"foo": "useless"
}
最后我想将这个结构映射到具有以下值的 Scala 对象中:
- id: Int = 1
- urn: 字符串 = "urn:user:5"
- type: String = "article_post" // 逻辑:urn.split(":")(1) + "_" + key
最有效的方法是什么?将 JSON 框架内的结构映射到我的需要,或者应该尽快使用中间案例 classes 将它们映射到我的自定义结构中?
我翻了一下official documentation,不过他们好像没有讨论这个问题。
目前我会创建一个中间案例 class,它只是从 JSON 中提取相关属性(id、urn、key)并在接下来的步骤中将这些对象映射到自定义我想要的结构。不知何故,我觉得这不是要走的路。
您可以在自定义 Reads/Writes 序列化程序中添加这种逻辑。请参阅我关于如何添加自定义 JSON Writer for Seq of Tuple 的回答。在那种情况下,我做一个 Writes
,在你的情况下,你会创建一个 Reads
,因为你正在阅读 JSON 并转换为 class 的实例,很可能是一个案例class.
例如
case class Wombat(id: Int, urn: String, `type`: String)
implicit val myWombatReads = new Reads[Wombat] {
def reads(js: JsValue): JsResult[Wombat] = {
val id = (js \ "id").as[Int]
val urn = (js \ "urn").as[String]
val key = (js \ "key").as[String]
JsSuccess(Wombat(id, urn, urn.split(":")(1) + "_" + key))
}
}
我没有测试您的自定义逻辑,但您明白了。您还可以添加验证器等。
你需要使用Json transformer
import play.api.libs.functional.syntax._
import play.api.libs.json.Reads._
import play.api.libs.json._
val t = (
(__ \ "id").json.copyFrom( (__ \ "id").json.pick) and
(__ \ "urn").json.copyFrom( (__ \ "urn").json.pick) and
( __ \ "type").json.copyFrom(
(__ \ "urn").read[String].flatMap(urn =>
(__ \ "key").read[String].map(key =>
JsString(urn.split(":")(1) + "_" + key)
)
)
)
).reduce
你例子的转换结果:
json.transform(t)
scala> json: play.api.libs.json.JsValue = {"id":1,"urn":"urn:article:5","key":"post","foo":"useless"}
scala> res2: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"id":1,"urn":"urn:article:5","type":"article_post"},)
您还可以添加一些验证。 coast-to-coast desig.
的好文章
我想用 Play 库解析一些 "complex" JSON。
import play.api.libs.json._
让我们假设我从服务器获得了一个包含许多 JSON 对象的数组,我无法对其进行修改。每个条目看起来都与这个相似:
{
"id": 1,
"urn": "urn:article:5",
"key": "post",
"foo": "useless"
}
最后我想将这个结构映射到具有以下值的 Scala 对象中:
- id: Int = 1
- urn: 字符串 = "urn:user:5"
- type: String = "article_post" // 逻辑:urn.split(":")(1) + "_" + key
最有效的方法是什么?将 JSON 框架内的结构映射到我的需要,或者应该尽快使用中间案例 classes 将它们映射到我的自定义结构中?
我翻了一下official documentation,不过他们好像没有讨论这个问题。
目前我会创建一个中间案例 class,它只是从 JSON 中提取相关属性(id、urn、key)并在接下来的步骤中将这些对象映射到自定义我想要的结构。不知何故,我觉得这不是要走的路。
您可以在自定义 Reads/Writes 序列化程序中添加这种逻辑。请参阅我关于如何添加自定义 JSON Writer for Seq of Tuple 的回答。在那种情况下,我做一个 Writes
,在你的情况下,你会创建一个 Reads
,因为你正在阅读 JSON 并转换为 class 的实例,很可能是一个案例class.
例如
case class Wombat(id: Int, urn: String, `type`: String)
implicit val myWombatReads = new Reads[Wombat] {
def reads(js: JsValue): JsResult[Wombat] = {
val id = (js \ "id").as[Int]
val urn = (js \ "urn").as[String]
val key = (js \ "key").as[String]
JsSuccess(Wombat(id, urn, urn.split(":")(1) + "_" + key))
}
}
我没有测试您的自定义逻辑,但您明白了。您还可以添加验证器等。
你需要使用Json transformer
import play.api.libs.functional.syntax._
import play.api.libs.json.Reads._
import play.api.libs.json._
val t = (
(__ \ "id").json.copyFrom( (__ \ "id").json.pick) and
(__ \ "urn").json.copyFrom( (__ \ "urn").json.pick) and
( __ \ "type").json.copyFrom(
(__ \ "urn").read[String].flatMap(urn =>
(__ \ "key").read[String].map(key =>
JsString(urn.split(":")(1) + "_" + key)
)
)
)
).reduce
你例子的转换结果:
json.transform(t)
scala> json: play.api.libs.json.JsValue = {"id":1,"urn":"urn:article:5","key":"post","foo":"useless"}
scala> res2: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"id":1,"urn":"urn:article:5","type":"article_post"},)
您还可以添加一些验证。 coast-to-coast desig.
的好文章