马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
作者:Andrew Tarry
原文链接:Deserializing an interface with Jackson
原文发表时间:2020-05-27 15:10 +0100
原文更新时间:2023-01-31 11:22 +0200
在将Json和Java对象相互转换的库中,我最喜好的是Jackson。它可以自动把对象映射到POJO。但反序列化接口必要多写些代码。Jackson能从POJO中读取类型,但使用接口时,Jackson无法自动找到接口的实现,因而无法反序列化。
我将在下面的例子中展示如何将一个简朴的json反序列化为POJO。json如下:- {
- "myInterface": {
- "a": "Z",
- "b": "Y"
- }
- }
复制代码 要转换成的POJO如下:- public class MyPojo {
- private MyInterface myInterface;
- // getters and setters
- }
复制代码 MyInterface 类型只有一些getter方法- public interface MyInterface {
- String getA();
- String getB();
- }
复制代码 这个例子相当刻意,但它展示了一种解决问题的通用思路,即直接告诉Jackson使用接口的哪个实现类。
状况1:能掌控接口且只有一个实现类
这是最简朴的环境,由于接口只有一种实现类。使用接口可以制止接口与实现的紧耦合。这种方法要求您确定确实不必要多个实现类。
可以在接口上使用解释告诉Jackson如何反序列化它。- package com.andrewtarry.jackson.single;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;@JsonDeserialize(as = MyInterfaceImpl.class)public interface MyInterface {
- String getA();
- String getB();
- }
复制代码 这段代码告诉Jackson将其反序列化为 MyInterfaceImpl 类的实例(要求 MyInterfaceImpl 是 MyInterface 的实现类)。
状况2:能掌控接口且有多个实现类
在代码中引入多态后,环境变得有点复杂。如果有多个可选的实现类,必要让Jackson知道使用哪个实现类。- package com.andrewtarry.jackson.multiple;import com.fasterxml.jackson.annotation.JsonSubTypes;import com.fasterxml.jackson.annotation.JsonTypeInfo;@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", defaultImpl = MyInterfaceImpl.class)@JsonSubTypes({ @JsonSubTypes.Type(value = MyInterfaceImpl.class, name = "standard"), @JsonSubTypes.Type(value = MyOtherInterfaceImpl.class, name = "other")})public interface MyInterface {
- String getA();
- String getB();
- }
复制代码 使用 @JsonSubType 注解可以支持多个实现类。在 @JsonTypeInfo 注解中指示Jackson从json中读取 type 属性,并据此选择实现类。
这个方法的好处是可以轻松地控制反序列化的实现类,但我们不会将反序列化逻辑的细节暴露到API中(译者注:存疑,使用这种方法反序列化明明就必要把实现类的类型写在JSON中,怎么能说没有暴露反序列化逻辑的细节呢?)。JSON必要像下面这样。- {
- "myInterface": {
- "a": "Z",
- "b": "Y",
- "type": "other"
- }
- }
复制代码 type 值只被Jackson使用,在API中使用它大概是个糟糕的方法。差异的实现类会产生差异的API调用结果,乃至大概要求用户来设置正确的实现类。
如果你的API只在应用内部使用,或者 type 属性是一个现有的、易于明确的值,那么这种方法是合适的。对于暴露到外部的API,这种方法不合适。
状况3:无法掌控接口
有一种环境没法向接口添加注解。你大概必要将JSON反序列化为第三方POJO,而且无法解释接口。在这种环境下,我们必须自定义反序列化的代码。
Jackson答应自定义反序列化器,可以不必要解释就将JSON反序列化为任何类型的数据。- package com.andrewtarry.jackson.custom;
- import com.fasterxml.jackson.core.JsonParser;
- import com.fasterxml.jackson.core.JsonProcessingException;
- import com.fasterxml.jackson.databind.DeserializationContext;
- import com.fasterxml.jackson.databind.JsonNode;
- import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
- import java.io.IOException;
- public class MyInterfaceDeserialize extends StdDeserializer<MyInterface> {
- public MyInterfaceDeserialize() {
- this(null);
- }
- protected MyInterfaceDeserialize(Class<?> vc) {
- super(vc);
- }
- @Override
- public MyInterface deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
- JsonNode node = p.readValueAsTree();
- String a = node.get("a").asText();
- String b = node.get("b").asText();
- MyInterfaceImpl impl = new MyInterfaceImpl();
- impl.setA(a);
- impl.setB(b);
- return impl;
- }
- }
复制代码 然后向Jackson注册这个反序列化器。- SimpleModule deserialization = new SimpleModule();
- deserialization.addDeserializer(MyInterface.class, new MyInterfaceDeserialize());
- objectMapper.registerModule(deserialization);
复制代码 现在Jackson处理 MyInterface 都会使用自定义的反序列化器。这种方法可以将代码与json完全解耦,并解析其他方法无法解析的数据类型。只要我们想,乃至可以在这里返回一个匿名类,而不用创建实现类。
这种方法的缺点是必要写更多代码,在大型项目中会增长复杂性。我以为自定义反序列化是最后的手段,为了让项目保持简朴,我们应该先尝试使用解释,如果解释无法解决问题,再使用自定义代码。
总结
将Java和json结合起来使用大概会遇到一些困难,由于一个使用强类型和运行时无法变更布局的数据布局,而另一个则十分机动。处理此问题的方法因项目而异,我在这里提供几种将json转换为Java接口的方法。
API的计划不应该依赖于实现技术和接口的最佳实践,也不应该为了使用API而放弃Java中的多态(译者注:看不懂,自己去看原文吧)。上面就是一些解决API和Java代码不完全匹配的方案。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |