如何避免为 JSON 宏初始阶段的子 类 定义隐式 reads/writes(将嵌套的 JSON 结构转换为 Scala)
How to avoid definition of implicit reads/writes for the sub-classes for JSON Macro Inception (to convert nested JSON structure to Scala)
我有一个要求,其中传入的 JSON 对象很复杂并且大部分是嵌套的 ex:
"users": {
"utype": "PERSON",
"language":"en_FR",
"credentials": [
{
"handle": "xyz@abc.com",
"password": "123456",
"handle_type": "EMAIL"
}
],
"person_details": {
"primary": "true",
"names": [
{
"name_type": "OFFICIAL",
"title": "MR",
"given": "abc",
"family": "zat",
"middle": "pqs",
"suffix":"anathan"
}
],
"addresses": [
{
"ad_type": "HOME",
"line1": "Residential 2211 North 1st Street",
"line2": "Bldg 17",
"city": "test",
"county": "Shefield",
"state" : "NY",
"country_code": "xx",
"postal_code": "95131"
}
]
}
}
为了解析这个结构,我使用下面的案例 类
case class PersonUser (
user_type:String,
language_code:String,
credentials:List[Credential],
person_details:PersonDetails
)
case class Credential(handle:String, password:String,handle_type:String)
case class PersonDetails(
primary_user:Boolean,
names: List[Name],
addresses:List[Address]
)
case class Name(
name_type: String,
title: String,
given: String,
family: String,
middle: String,
suffix:String
)
case class Address(
address_type: String,
line1: String,
line2: String,
city: String,
county: String,
state : String,
country_code: String,
postal_code: String
)
为了将 JSON 结构转换为 Scala,我使用了 JSON Inception:
implicit val testReads = Json.reads[PersonUser]
我还必须在子 类 - Credential、PersonDetails、Name 和 Address 中指定类似的 reads implicits。下面给出了这样的例子:
case class Credential(handle:String, password:String,handle_type:String)
object Credential{
implicit val reads = Json.reads[Credential]
}
现在问题来了,如果我的 JSON 结构真的很大,有很多子结构,那么我需要定义很多 Scala case 类。为每个 case 类 定义伴随对象和隐式读取真的很麻烦(例如:如果我有 8 个 case 类 来完全表示 JSON 结构,我将不得不再定义 8 个伴生对象)。有什么办法可以避免这种额外的工作吗?
不,无法避免为每个要(反)序列化的 class 定义一个 Format
实例。
你也可以看看其他库,至少 Genson 不需要你定义大量的隐式。您上面显示的代码默认情况下应该与 Genson 一起使用。
import com.owlike.genson.defaultGenson_
val personUser: PersonUser = fromJson[PersonUser](json)
val json = toJson(personUser)
Genson还有很多其他的功能,我让你自己判断。
这个问题已经回答了,但我想我会解释为什么会这样。
- 如果宏为嵌套案例 classes 生成了必要的读取,这将无法为那些嵌套的 classes 定义您自己的自定义读取。结果,宏只对最微不足道的情况有用,需要切换到手动实现整个层次结构的读取,只是为了为一个深度嵌套的情况定义自定义行为 class。现在宏的实现方式,你可以随意随意更改其中的任何部分。
- 是的,头顶上有一个小样板文件。但从好的方面来说,编译器会告诉你你的结构是否可以被反序列化,这意味着你可以及早捕获错误,使重构更安全,也意味着没有那么隐含的魔法,你知道它是如何工作的唯一方法是阅读文档。通过强类型化一切,没有魔法,没有惊喜,没有隐含的知识,这导致改进的可维护性。
我有一个要求,其中传入的 JSON 对象很复杂并且大部分是嵌套的 ex:
"users": {
"utype": "PERSON",
"language":"en_FR",
"credentials": [
{
"handle": "xyz@abc.com",
"password": "123456",
"handle_type": "EMAIL"
}
],
"person_details": {
"primary": "true",
"names": [
{
"name_type": "OFFICIAL",
"title": "MR",
"given": "abc",
"family": "zat",
"middle": "pqs",
"suffix":"anathan"
}
],
"addresses": [
{
"ad_type": "HOME",
"line1": "Residential 2211 North 1st Street",
"line2": "Bldg 17",
"city": "test",
"county": "Shefield",
"state" : "NY",
"country_code": "xx",
"postal_code": "95131"
}
]
}
}
为了解析这个结构,我使用下面的案例 类
case class PersonUser (
user_type:String,
language_code:String,
credentials:List[Credential],
person_details:PersonDetails
)
case class Credential(handle:String, password:String,handle_type:String)
case class PersonDetails(
primary_user:Boolean,
names: List[Name],
addresses:List[Address]
)
case class Name(
name_type: String,
title: String,
given: String,
family: String,
middle: String,
suffix:String
)
case class Address(
address_type: String,
line1: String,
line2: String,
city: String,
county: String,
state : String,
country_code: String,
postal_code: String
)
为了将 JSON 结构转换为 Scala,我使用了 JSON Inception:
implicit val testReads = Json.reads[PersonUser]
我还必须在子 类 - Credential、PersonDetails、Name 和 Address 中指定类似的 reads implicits。下面给出了这样的例子:
case class Credential(handle:String, password:String,handle_type:String)
object Credential{
implicit val reads = Json.reads[Credential]
}
现在问题来了,如果我的 JSON 结构真的很大,有很多子结构,那么我需要定义很多 Scala case 类。为每个 case 类 定义伴随对象和隐式读取真的很麻烦(例如:如果我有 8 个 case 类 来完全表示 JSON 结构,我将不得不再定义 8 个伴生对象)。有什么办法可以避免这种额外的工作吗?
不,无法避免为每个要(反)序列化的 class 定义一个 Format
实例。
你也可以看看其他库,至少 Genson 不需要你定义大量的隐式。您上面显示的代码默认情况下应该与 Genson 一起使用。
import com.owlike.genson.defaultGenson_
val personUser: PersonUser = fromJson[PersonUser](json)
val json = toJson(personUser)
Genson还有很多其他的功能,我让你自己判断。
这个问题已经回答了,但我想我会解释为什么会这样。
- 如果宏为嵌套案例 classes 生成了必要的读取,这将无法为那些嵌套的 classes 定义您自己的自定义读取。结果,宏只对最微不足道的情况有用,需要切换到手动实现整个层次结构的读取,只是为了为一个深度嵌套的情况定义自定义行为 class。现在宏的实现方式,你可以随意随意更改其中的任何部分。
- 是的,头顶上有一个小样板文件。但从好的方面来说,编译器会告诉你你的结构是否可以被反序列化,这意味着你可以及早捕获错误,使重构更安全,也意味着没有那么隐含的魔法,你知道它是如何工作的唯一方法是阅读文档。通过强类型化一切,没有魔法,没有惊喜,没有隐含的知识,这导致改进的可维护性。