如何 "reads" 给定 Json 键名以大写字母开头的对象的 Scala Case Class
How to "reads" into a Scala Case Class given a Json object with key names that start with a capital letter
本题基于 Scala 2.12.12
scalaVersion := "2.12.12"
使用播放-json
"com.typesafe.play" %% "play-json" % "2.9.1"
如果我有一个如下所示的 Json 对象:
{
"UpperCaseKey": "some value",
"AnotherUpperCaseKey": "some other value"
}
我知道我可以像这样创建案例 class:
case class Yuck(UpperCaseKey: String, AnotherUpperCaseKey: String)
然后用这个追赶者跟进:
implicit val jsYuck = Json.format[Yuck]
当然,这会给我 reads[Yuck]
和 writes[Yuck]
往返 Json。
我问这个是因为我有一个用例,我不是决定键大小写的人,我收到了一个 Json 对象,该对象充满了以开头的键一个大写字母。
在这个用例中,我将不得不读取和转换数百万个,因此性能是一个问题。
我研究了@JsonAnnotations 和 Scala 的转换器。前者似乎没有太多在现场级别的 Scala 中使用的文档,而后者似乎是很多样板文件,如果我只知道如何使用,另一种方法可能非常简单...
请记住,当您回答这个问题时,某些键将被命名为:
XXXYyyyyZzzzzz
因此预定义的 Snake/Camel 大小写转换将不起作用。
编写自定义转换似乎是一种选择,但不确定如何使用 Scala 来实现。
有没有办法任意请求 Json 读取将获取密钥 "XXXYyyyZzzz"
并将其匹配到 Scala 案例 class 中标记为 "xxxYyyyZzzz"
的字段?
需要说明的是,我可能还需要将名为 "AbCdEf"
的 Json 键转换或至少知道如何转换为标有 "fghi"
.
的字段
只需使用提供的 PascalCase
.
case class Yuck(
upperCaseKey: String,
anotherUpperCaseKey: String)
object Yuck {
import play.api.libs.json._
implicit val jsonFormat: OFormat[Yuck] = {
implicit val cfg = JsonConfiguration(naming = JsonNaming.PascalCase)
Json.format
}
}
play.api.libs.json.Json.parse("""{
"UpperCaseKey": "some value",
"AnotherUpperCaseKey": "some other value"
}""").validate[Yuck]
// => JsSuccess(Yuck(some value,some other value),)
play.api.libs.json.Json.toJson(Yuck(
upperCaseKey = "foo",
anotherUpperCaseKey = "bar"))
// => JsValue = {"UpperCaseKey":"foo","AnotherUpperCaseKey":"bar"}
我认为 play-json 支持这种场景的唯一方法是定义你自己的 Format
。
假设我们有:
case class Yuck(xxxYyyyZzzz: String, fghi: String)
所以我们可以在伴生对象上定义Format
:
object Yuck {
implicit val format: Format[Yuck] = {
((__ \ "XXXYyyyZzzz").format[String] and (__ \ "AbCdEf").format[String]) (Yuck.apply(_, _), yuck => (yuck.xxxYyyyZzzz, yuck.fghi))
}
}
然后是:
val jsonString = """{ "XXXYyyyZzzz": "first value", "AbCdEf": "second value" }"""
val yuck = Json.parse(jsonString).validate[Yuck]
println(yuck)
yuck.map(yuckResult => Json.toJson(yuckResult)).foreach(println)
将输出:
JsSuccess(Yuck(first value,second value),)
{"XXXYyyyZzzz":"first value","AbCdEf":"second value"}
正如我们所见,XXXYyyyZzzz
被映射到 xxxYyyyZzzz
,AbCdEf
被映射到 fghi
。
代码 运行 在 Scastie。
您还有另一个选择,就是使用 JsonNaming
,正如@cchantep 在评论中所建议的那样。如果你定义:
object Yuck {
val keysMap = Map("xxxYyyyZzzz" -> "XXXYyyyZzzz", "fghi" -> "AbCdEf")
implicit val config = JsonConfiguration(JsonNaming(keysMap))
implicit val fotmat = Json.format[Yuck]
}
运行相同的代码会输出相同的结果。代码运行在 Scastie.
本题基于 Scala 2.12.12
scalaVersion := "2.12.12"
使用播放-json
"com.typesafe.play" %% "play-json" % "2.9.1"
如果我有一个如下所示的 Json 对象:
{
"UpperCaseKey": "some value",
"AnotherUpperCaseKey": "some other value"
}
我知道我可以像这样创建案例 class:
case class Yuck(UpperCaseKey: String, AnotherUpperCaseKey: String)
然后用这个追赶者跟进:
implicit val jsYuck = Json.format[Yuck]
当然,这会给我 reads[Yuck]
和 writes[Yuck]
往返 Json。
我问这个是因为我有一个用例,我不是决定键大小写的人,我收到了一个 Json 对象,该对象充满了以开头的键一个大写字母。
在这个用例中,我将不得不读取和转换数百万个,因此性能是一个问题。
我研究了@JsonAnnotations 和 Scala 的转换器。前者似乎没有太多在现场级别的 Scala 中使用的文档,而后者似乎是很多样板文件,如果我只知道如何使用,另一种方法可能非常简单...
请记住,当您回答这个问题时,某些键将被命名为:
XXXYyyyyZzzzzz
因此预定义的 Snake/Camel 大小写转换将不起作用。
编写自定义转换似乎是一种选择,但不确定如何使用 Scala 来实现。
有没有办法任意请求 Json 读取将获取密钥 "XXXYyyyZzzz"
并将其匹配到 Scala 案例 class 中标记为 "xxxYyyyZzzz"
的字段?
需要说明的是,我可能还需要将名为 "AbCdEf"
的 Json 键转换或至少知道如何转换为标有 "fghi"
.
只需使用提供的 PascalCase
.
case class Yuck(
upperCaseKey: String,
anotherUpperCaseKey: String)
object Yuck {
import play.api.libs.json._
implicit val jsonFormat: OFormat[Yuck] = {
implicit val cfg = JsonConfiguration(naming = JsonNaming.PascalCase)
Json.format
}
}
play.api.libs.json.Json.parse("""{
"UpperCaseKey": "some value",
"AnotherUpperCaseKey": "some other value"
}""").validate[Yuck]
// => JsSuccess(Yuck(some value,some other value),)
play.api.libs.json.Json.toJson(Yuck(
upperCaseKey = "foo",
anotherUpperCaseKey = "bar"))
// => JsValue = {"UpperCaseKey":"foo","AnotherUpperCaseKey":"bar"}
我认为 play-json 支持这种场景的唯一方法是定义你自己的 Format
。
假设我们有:
case class Yuck(xxxYyyyZzzz: String, fghi: String)
所以我们可以在伴生对象上定义Format
:
object Yuck {
implicit val format: Format[Yuck] = {
((__ \ "XXXYyyyZzzz").format[String] and (__ \ "AbCdEf").format[String]) (Yuck.apply(_, _), yuck => (yuck.xxxYyyyZzzz, yuck.fghi))
}
}
然后是:
val jsonString = """{ "XXXYyyyZzzz": "first value", "AbCdEf": "second value" }"""
val yuck = Json.parse(jsonString).validate[Yuck]
println(yuck)
yuck.map(yuckResult => Json.toJson(yuckResult)).foreach(println)
将输出:
JsSuccess(Yuck(first value,second value),)
{"XXXYyyyZzzz":"first value","AbCdEf":"second value"}
正如我们所见,XXXYyyyZzzz
被映射到 xxxYyyyZzzz
,AbCdEf
被映射到 fghi
。
代码 运行 在 Scastie。
您还有另一个选择,就是使用 JsonNaming
,正如@cchantep 在评论中所建议的那样。如果你定义:
object Yuck {
val keysMap = Map("xxxYyyyZzzz" -> "XXXYyyyZzzz", "fghi" -> "AbCdEf")
implicit val config = JsonConfiguration(JsonNaming(keysMap))
implicit val fotmat = Json.format[Yuck]
}
运行相同的代码会输出相同的结果。代码运行在 Scastie.