【问题标题】:How to create a custom deserializer in Jackson for a generic type?如何在 Jackson 中为泛型类型创建自定义反序列化器?
【发布时间】:2016-07-09 15:39:21
【问题描述】:

想象以下场景:

class <T> Foo<T> {
    ....
}

class Bar {
    Foo<Something> foo;
}

我想为 Foo 编写一个自定义的 Jackson 反序列化器。为了做到这一点(例如,为了反序列化具有Foo&lt;Something&gt; 属性的Bar 类),我需要知道Foo&lt;T&gt; 的具体类型,在反序列化时用于Bar(例如我需要要知道T 在那种特殊情况下是Something)。

如何编写这样的反序列化器?应该可以做到,因为 Jackson 使用类型化的集合和地图来做到这一点。

说明:

似乎有两个部分可以解决问题:

1) 获取Bar 中声明的属性类型foo 并使用它来反序列化Foo&lt;Somehting&gt;

2) 在反序列化时找出我们正在反序列化类 Bar 内的属性 foo 以便成功完成步骤 1)

如何完成1和2?

【问题讨论】:

  • 可以使用反射发现foodeclared类型是Foo&lt;Something&gt;。就是这样。我不知道杰克逊会说这是否是达到你目的的手段。 Field#getGenericType

标签: java json generics jackson deserialization


【解决方案1】:

对于我的用例,上述解决方案都不起作用,所以我不得不编写一个自定义模块。你可以找到我的实现on GitHub

我想写一个反序列化器,自动从列表中删除空白字符串。

【讨论】:

    【解决方案2】:

    这是您可以访问/解析自定义 Jackson 反序列化器的 {targetClass} 的方式。当然你需要为此实现 ContextualDeserializer 接口。

    public class WPCustomEntityDeserializer extends JsonDeserializer<Object> 
                  implements ContextualDeserializer {
    
        private Class<?> targetClass;
    
        @Override
        public Object deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
    
            ObjectCodec oc = jp.getCodec();
            JsonNode node = oc.readTree(jp);
    
            //Your code here to customize deserialization
            // You can access {target class} as targetClass (defined class field here)
            //This should build some {deserializedClasObject}
    
            return deserializedClasObject;
    
        }   
    
        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property){
            //Find here the targetClass to be deserialized  
            String targetClassName=ctxt.getContextualType().toCanonical();
            try {
                targetClass = Class.forName(targetClassName);
            } catch (ClassNotFoundException e) {            
                e.printStackTrace();
            }
            return this;
        }
    }
    

    【讨论】:

      【解决方案3】:

      你可以为你的泛型类型实现一个自定义的JsonDeserializer,它也实现了ContextualDeserializer

      例如,假设我们有以下包含泛型值的简单包装器类型:

      public static class Wrapper<T> {
          public T value;
      }
      

      我们现在想要反序列化如下所示的 JSON:

      {
          "name": "Alice",
          "age": 37
      }
      

      进入一个看起来像这样的类的实例:

      public static class Person {
          public Wrapper<String> name;
          public Wrapper<Integer> age;
      }
      

      实现ContextualDeserializer 允许我们根据字段的泛型类型参数为Person 类中的每个字段创建特定的反序列化器。这允许我们将名称反序列化为字符串,将年龄反序列化为整数。

      完整的反序列化器如下所示:

      public static class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {
          private JavaType valueType;
      
          @Override
          public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
              JavaType wrapperType = property.getType();
              JavaType valueType = wrapperType.containedType(0);
              WrapperDeserializer deserializer = new WrapperDeserializer();
              deserializer.valueType = valueType;
              return deserializer;
          }
      
          @Override
          public Wrapper<?> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
              Wrapper<?> wrapper = new Wrapper<>();
              wrapper.value = ctxt.readValue(parser, valueType);
              return wrapper;
          }
      }
      

      最好先在这里查看createContextual,因为Jackson 将首先调用它。我们从BeanProperty(例如Wrapper&lt;String&gt;)中读取字段的类型,然后提取第一个泛型类型参数(例如String)。然后我们创建一个新的反序列化器并将内部类型存储为valueType

      在这个新创建的反序列化器上调用 deserialize 后,我们可以简单地要求 Jackson 将值反序列化为内部类型而不是整个包装器类型,并返回包含反序列化值的新 Wrapper

      为了注册这个自定义反序列化器,我们需要创建一个包含它的模块,并注册该模块:

      SimpleModule module = new SimpleModule()
              .addDeserializer(Wrapper.class, new WrapperDeserializer());
      
      ObjectMapper objectMapper = new ObjectMapper();
      objectMapper.registerModule(module);
      

      如果我们尝试从上面反序列化示例 JSON,我们可以看到它按预期工作:

      Person person = objectMapper.readValue(json, Person.class);
      System.out.println(person.name.value);  // prints Alice
      System.out.println(person.age.value);   // prints 37
      

      Jackson documentation 中有更多关于上下文反序列化器如何工作的详细信息。

      【讨论】:

      • 感谢您的这篇文章。非常有助于实现我想要的。我对根值进行了一些修改,以防谷歌的其他人出现。 gist.github.com/darylteo/a7be65b539c0d8d3ca0de94d96763f33
      • 谢谢,真的很有帮助 上下文类型本身可以是通用的,在这种情况下属性将为空,您需要使用 ctxt.getContextualType 创建 JsonDeserializer
      • 当 Wrapper 中的值是私有的,并且由带有参数的构造函数设置时会发生什么?
      • 如果我有一个人员列表 public class People { public List people; } 我该怎么办?
      • 谢谢,这真的有助于为 Quantity 和 Quantity 类型编写自定义反序列化器。帮助确定使用哪种类型。
      【解决方案4】:

      如果目标本身是泛型类型,则属性将为 null,为此您需要从 DeserializationContext 中获取 valueTtype:

      @Override
      public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
          if (property == null) { //  context is generic
              JMapToListParser parser = new JMapToListParser();
              parser.valueType = ctxt.getContextualType().containedType(0);
              return parser;
          } else {  //  property is generic
              JavaType wrapperType = property.getType();
              JavaType valueType = wrapperType.containedType(0);
              JMapToListParser parser = new JMapToListParser();
              parser.valueType = valueType;
              return parser;
          }
      }
      

      【讨论】:

      • 对于任何在接受的答案上获得 NPE 的人来说,这个解决方案可能就是您所需要的。谢谢 - 我不知道你刚刚为我节省了多少时间。
      • 绝对非常有帮助。我实际上是在 Kotlin 中使用它,并且有一些警告。例如,泛型类型信息可能会不完整地传递,具体取决于您读取值的方式:mapper.readValue&lt;Generic&lt;T&gt;&gt; 保留内部类型 T,而 Java 方法 mapper.readValue(json, Generic::class.java) 则没有。不知何故明显,但导致了一些 NPE。这个练习教会了我很多关于 JVM 中泛型的知识。
      • @TheOperator 你有例子吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-28
      • 2017-12-23
      • 1970-01-01
      • 2014-04-05
      • 2012-10-18
      • 1970-01-01
      • 2012-07-24
      相关资源
      最近更新 更多