播放 Json - 复杂的对象创建
Play Json - Complex object creation
我正在尝试在我的 Play 框架应用程序 (Scala) 中创建 Json reader。问题是,我的 Json 的一部分有点古怪,需要进一步处理才能检索值。例如:
{
"field1":"value1",
"field2":"value/1",
"num2":2
}
大小写 类:
case class Field1(text: String, fields: Field2)
case class Field2(text: String, num: Int, num2: Int)
基本上 Field2
的 text
和 num
字段是通过拆分文本从值 value/1
导出的。这是拆分器函数:
def splitter(path: String, num2: Int): Field2 = {
val split = path.split("\")
Field2(split(0), split(1).toInt, num2)
}
这很简单,实际的拆分器功能要复杂得多。基本上构造此对象的唯一方法 Field2
是将单个字符串传递给一个函数,该函数会吐出所需的对象。
如何为 Field2
(并扩展为 Field1
)创建 reader?
这是我目前的情况:
object Field1 {
implicit val reader = (
(__ \ "field1").read[String] and
(__).read[Field2]
) (Field1.apply _)
}
object Field2 {
implicit val reader = (
splitter((__ \ "field2").read[String], (__ \ "num2"))
) // Obviously incorrect syntax + type mismatch, but this is roughly what I'm trying to accomplish.
}
如果你使用非函数式组合器语法,我认为它会变得更简单一些:
object Field2 {
implicit val reader = Reads[Field2] { json =>
for {
path <- (json \ "field2").validate[String]
num2 <- (json \ "num2").validate[Int]
} yield splitter(path, num2)
}
}
此外,如果您希望拆分器进一步验证输入,请将其 return 设置为 JsResult[Field2]
,如下所示:
def splitter(path: String, num2: Int): JsResult[Field2] = {
val split = path.split("\")
if (split.size != 2) {
JsError(s"$path must be of the form: {field}\{num}")
} else {
Try(Field2(split(0), split(1).toInt, num2)).map(JsSuccess(_)).getOrElse {
JsError(s"${split(1)} is not a valid Int")
}
}
}
然后修改reader:
object Field2 {
implicit val reader = Reads[Field2] { json =>
for {
path <- (json \ "field2").validate[String]
num2 <- (json \ "num2").validate[Int]
field2 <- splitter(path, num2)
} yield field2
}
}
如果您仍然喜欢使用函数式语法并且您不介意拆分器缺乏验证,请试试这个:
def splitter(path: String, num2: Int): Field2 = {
val split = path.split("\")
Field2(split(0), split(1).toInt, num2)
}
implicit val reader = (
(__ \ "field2").read[String] and
(__ \ "num2").read[Int]
)(splitter _)
我不建议这样做,它不安全(split(1)
和 toInt
都可能抛出异常)并且函数式语法可能存在性能问题。参见 https://www.lucidchart.com/techblog/2016/08/29/speeding-up-restful-services-in-play-framework/。
我不知道你为什么需要 case 类,但你也可以根据需要使用
转换 json
(__ \ "field2" \ "num2").json.copyFrom((__ \ "num2").json.pick) and
(__ \ "field2").json.update(
of[String].map { o =>
val split = o.split("/")
Json.obj(
"text" -> split(0),
"num" -> split(1).toInt
)
}
)
).reduce andThen (__ \ "num2").json.prune
scala> j: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"field2":{"num2":2,"text":"value","num":1},"field1":"value1"},/num2)
然后您可以使用您的 Reads[Field1]
我正在尝试在我的 Play 框架应用程序 (Scala) 中创建 Json reader。问题是,我的 Json 的一部分有点古怪,需要进一步处理才能检索值。例如:
{
"field1":"value1",
"field2":"value/1",
"num2":2
}
大小写 类:
case class Field1(text: String, fields: Field2)
case class Field2(text: String, num: Int, num2: Int)
基本上 Field2
的 text
和 num
字段是通过拆分文本从值 value/1
导出的。这是拆分器函数:
def splitter(path: String, num2: Int): Field2 = {
val split = path.split("\")
Field2(split(0), split(1).toInt, num2)
}
这很简单,实际的拆分器功能要复杂得多。基本上构造此对象的唯一方法 Field2
是将单个字符串传递给一个函数,该函数会吐出所需的对象。
如何为 Field2
(并扩展为 Field1
)创建 reader?
这是我目前的情况:
object Field1 {
implicit val reader = (
(__ \ "field1").read[String] and
(__).read[Field2]
) (Field1.apply _)
}
object Field2 {
implicit val reader = (
splitter((__ \ "field2").read[String], (__ \ "num2"))
) // Obviously incorrect syntax + type mismatch, but this is roughly what I'm trying to accomplish.
}
如果你使用非函数式组合器语法,我认为它会变得更简单一些:
object Field2 {
implicit val reader = Reads[Field2] { json =>
for {
path <- (json \ "field2").validate[String]
num2 <- (json \ "num2").validate[Int]
} yield splitter(path, num2)
}
}
此外,如果您希望拆分器进一步验证输入,请将其 return 设置为 JsResult[Field2]
,如下所示:
def splitter(path: String, num2: Int): JsResult[Field2] = {
val split = path.split("\")
if (split.size != 2) {
JsError(s"$path must be of the form: {field}\{num}")
} else {
Try(Field2(split(0), split(1).toInt, num2)).map(JsSuccess(_)).getOrElse {
JsError(s"${split(1)} is not a valid Int")
}
}
}
然后修改reader:
object Field2 {
implicit val reader = Reads[Field2] { json =>
for {
path <- (json \ "field2").validate[String]
num2 <- (json \ "num2").validate[Int]
field2 <- splitter(path, num2)
} yield field2
}
}
如果您仍然喜欢使用函数式语法并且您不介意拆分器缺乏验证,请试试这个:
def splitter(path: String, num2: Int): Field2 = {
val split = path.split("\")
Field2(split(0), split(1).toInt, num2)
}
implicit val reader = (
(__ \ "field2").read[String] and
(__ \ "num2").read[Int]
)(splitter _)
我不建议这样做,它不安全(split(1)
和 toInt
都可能抛出异常)并且函数式语法可能存在性能问题。参见 https://www.lucidchart.com/techblog/2016/08/29/speeding-up-restful-services-in-play-framework/。
我不知道你为什么需要 case 类,但你也可以根据需要使用
转换 json (__ \ "field2" \ "num2").json.copyFrom((__ \ "num2").json.pick) and
(__ \ "field2").json.update(
of[String].map { o =>
val split = o.split("/")
Json.obj(
"text" -> split(0),
"num" -> split(1).toInt
)
}
)
).reduce andThen (__ \ "num2").json.prune
scala> j: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"field2":{"num2":2,"text":"value","num":1},"field1":"value1"},/num2)
然后您可以使用您的 Reads[Field1]