【翻译】使用Jackson反序列化接口

打印 上一主题 下一主题

主题 1965|帖子 1965|积分 5895

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

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如下:
  1. {
  2.   "myInterface": {
  3.     "a": "Z",
  4.     "b": "Y"
  5.   }
  6. }
复制代码
要转换成的POJO如下:
  1. public class MyPojo {
  2.     private MyInterface myInterface;
  3.     // getters and setters
  4. }
复制代码
 MyInterface 类型只有一些getter方法
  1. public interface MyInterface {
  2.     String getA();
  3.     String getB();
  4. }
复制代码
这个例子相当刻意,但它展示了一种解决问题的通用思路,即直接告诉Jackson使用接口的哪个实现类。
状况1:能掌控接口且只有一个实现类

这是最简朴的环境,由于接口只有一种实现类。使用接口可以制止接口与实现的紧耦合。这种方法要求您确定确实不必要多个实现类。
可以在接口上使用解释告诉Jackson如何反序列化它。
  1. package com.andrewtarry.jackson.single;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;@JsonDeserialize(as = MyInterfaceImpl.class)public interface MyInterface {
  2.     String getA();
  3.     String getB();
  4. }
复制代码
这段代码告诉Jackson将其反序列化为 MyInterfaceImpl 类的实例(要求 MyInterfaceImpl 是 MyInterface 的实现类)。
状况2:能掌控接口且有多个实现类

在代码中引入多态后,环境变得有点复杂。如果有多个可选的实现类,必要让Jackson知道使用哪个实现类。
  1. 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 {
  2.     String getA();
  3.     String getB();
  4. }
复制代码
使用 @JsonSubType 注解可以支持多个实现类。在 @JsonTypeInfo 注解中指示Jackson从json中读取 type 属性,并据此选择实现类。
这个方法的好处是可以轻松地控制反序列化的实现类,但我们不会将反序列化逻辑的细节暴露到API中(译者注:存疑,使用这种方法反序列化明明就必要把实现类的类型写在JSON中,怎么能说没有暴露反序列化逻辑的细节呢?)。JSON必要像下面这样。
  1. {
  2.   "myInterface": {
  3.     "a": "Z",
  4.     "b": "Y",
  5.     "type": "other"
  6.   }
  7. }
复制代码
 type 值只被Jackson使用,在API中使用它大概是个糟糕的方法。差异的实现类会产生差异的API调用结果,乃至大概要求用户来设置正确的实现类。
如果你的API只在应用内部使用,或者 type 属性是一个现有的、易于明确的值,那么这种方法是合适的。对于暴露到外部的API,这种方法不合适。
状况3:无法掌控接口

有一种环境没法向接口添加注解。你大概必要将JSON反序列化为第三方POJO,而且无法解释接口。在这种环境下,我们必须自定义反序列化的代码。
Jackson答应自定义反序列化器,可以不必要解释就将JSON反序列化为任何类型的数据。
  1. package com.andrewtarry.jackson.custom;
  2. import com.fasterxml.jackson.core.JsonParser;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.databind.DeserializationContext;
  5. import com.fasterxml.jackson.databind.JsonNode;
  6. import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
  7. import java.io.IOException;
  8. public class MyInterfaceDeserialize extends StdDeserializer<MyInterface> {
  9.     public MyInterfaceDeserialize() {
  10.         this(null);
  11.     }
  12.     protected MyInterfaceDeserialize(Class<?> vc) {
  13.         super(vc);
  14.     }
  15.     @Override
  16.     public MyInterface deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
  17.         JsonNode node = p.readValueAsTree();
  18.         String a = node.get("a").asText();
  19.         String b = node.get("b").asText();
  20.         MyInterfaceImpl impl = new MyInterfaceImpl();
  21.         impl.setA(a);
  22.         impl.setB(b);
  23.         return impl;
  24.     }
  25. }
复制代码
然后向Jackson注册这个反序列化器。
  1. SimpleModule deserialization = new SimpleModule();
  2. deserialization.addDeserializer(MyInterface.class, new MyInterfaceDeserialize());
  3. objectMapper.registerModule(deserialization);
复制代码
现在Jackson处理 MyInterface 都会使用自定义的反序列化器。这种方法可以将代码与json完全解耦,并解析其他方法无法解析的数据类型。只要我们想,乃至可以在这里返回一个匿名类,而不用创建实现类。
这种方法的缺点是必要写更多代码,在大型项目中会增长复杂性。我以为自定义反序列化是最后的手段,为了让项目保持简朴,我们应该先尝试使用解释,如果解释无法解决问题,再使用自定义代码。
总结

将Java和json结合起来使用大概会遇到一些困难,由于一个使用强类型和运行时无法变更布局的数据布局,而另一个则十分机动。处理此问题的方法因项目而异,我在这里提供几种将json转换为Java接口的方法。
API的计划不应该依赖于实现技术和接口的最佳实践,也不应该为了使用API而放弃Java中的多态(译者注:看不懂,自己去看原文吧)。上面就是一些解决API和Java代码不完全匹配的方案。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

徐锦洪

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表