假设我们有这个JSON:
[
{
"__typename": "Car",
"id": "123",
"name": "Toyota Prius",
"numDoors": 4
},
{
"__typename": "Boat",
"id": "4567",
"name": "U.S.S. Constitution",
"propulsion": "SAIL"
}
]
(列表中可能还有更多元素;这只显示两个元素)
我有Car
和Boat
个POJO,它们使用Vehicle
基类作为公共字段:
public abstract class Vehicle {
public final String id;
public final String name;
}
public class Car extends Vehicle {
public final Integer numDoors;
}
public class Boat extends Vehicle {
public final String propulsion;
}
解析此JSON的结果应该是List<Vehicle>
。问题是没有JSON解析器开箱即知,__typename
是如何区分Boat
和Car
的。
使用Gson,我可以创建JsonDeserializer<Vehicle>
来检查__typename
字段,确定这是Car
还是Boat
,然后使用deserialize()
在提供的JsonDeserializationContext
上将特定的JSON对象解析为适当的类型。这很好。
但是,我正在构建的特定内容应该支持可插入的JSON解析器,我认为我会尝试使用Moshi作为替代解析器。但是,目前Moshi文档中没有很好地解决这个特殊问题,我很难弄清楚如何最好地解决它。
与JsonDeserializer<T>
is JsonAdapter<T>
最接近的类似物。但是,fromJson()
会传递JsonReader
,其中包含破坏性API。要了解__typename
是什么,我必须能够从JsonReader
事件中手动解析所有内容。虽然我知道正确的具体类型后,我可以调用adapter()
on the Moshi
instance来尝试调用现有的Moshi解析逻辑,但我将消耗JsonReader
的数据并破坏其提供完整对象描述的能力。
JsonDeserializer<Vehicle>
的另一个类似物是@FromJson
-annotated method,它返回Vehicle
。但是,我无法确定传递给方法的简单方法。我唯一能想到的是创建另一个代表所有可能字段的联合的POJO:
public class SemiParsedKindOfVehicle {
public final String id;
public final String name;
public final Integer numDoors;
public final String propulsion;
public final String __typename;
}
然后,从理论上讲,如果我在@FromJson Vehicle rideLikeTheWind(SemiParsedKindOfVehicle rawVehicle)
注册为类型适配器的类上有Moshi
,则Moshi可能能够将我的JSON对象解析为SemiParsedKindOfVehicle
个实例,致电rideLikeTheWind()
。在那里,我会查找__typename
,识别类型,并自己完全构建Car
或Boat
,然后返回该对象。
虽然可行,但这比Gson方法要复杂得多,而我的Car
/ Boat
场景就在我需要处理的可能数据结构的简单结尾。
还有另一种方法可以用Moshi处理这个问题吗?
答案 0 :(得分:4)
更新2019-05-25 :newer answer是您最好的选择。由于历史原因,我将离开原来的解决方案。
我没有考虑到的一件事是您可以使用泛型类型创建类型适配器,例如Map<String, Object>
。鉴于此,您可以创建一个查找VehicleAdapter
的{{1}}。它将负责完整填充__typename
和Car
个实例(或者,可选地,将其委托给Boat
和Car
上的Boat
的构造函数输入)。因此,这仍然不如Gson的方法那么方便。另外,你必须有一个无所事事的Map<String, Object>
方法,否则Moshi拒绝你的类型适配器。但是,否则,它可以工作,正如JUnit4测试类所证明的那样:
@ToJson
答案 1 :(得分:1)
moshi-adapters
附加库包含a PolymorphicJsonAdapterFactory
class。虽然该库的JavaDocs似乎没有发布,但源代码中确实包含其用法的详细说明。
在我的问题中,示例的设置为:
private val moshi = Moshi.Builder()
.add(
PolymorphicJsonAdapterFactory.of(Vehicle::class.java, "__typename")
.withSubtype(Car::class.java, "Car")
.withSubtype(Boat::class.java, "Boat")
)
.build()
现在,我们的Moshi
对象知道如何根据JSON中的List<Vehicle>
属性将类似__typename
的内容转换为JSON,并将其与"Car"
进行比较,并且"Boat"
分别创建Car
和Boat
类。