【问题标题】:Jackson deserialize based on property nameJackson 根据属性名反序列化
【发布时间】:2018-10-31 19:47:33
【问题描述】:

我有以下两种类型的 JSON 对象:

{"foo": "String value"}

{"bar": "String value"}

它们都代表同一个基础对象的特殊类型。如何使用 Jackson 反序列化它们?类型信息仅由键本身表示,而不是任何键的值(几乎所有示例都使用键的值来确定类型:https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

【问题讨论】:

    标签: java json jackson jackson-databind


    【解决方案1】:

    我可能来不及回答,但仍会发布我的发现。 我遇到了完全相同的情况,在查看文档后,我遇到了以下doc

    // Include logical type name (defined in impl classes) as wrapper; 2 annotations
    

    @JsonTypeInfo(使用=Id.NAME,包括=As.WRAPPER_OBJECT) @JsonSubTypes({com.myemp.Impl1.class, com.myempl.Impl2.class})

    以下是我用来让它工作的设置:(使用上面帖子中@cassiomolin 使用的相同示例)

    @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
    @JsonSubTypes({
       @JsonSubTypes.Type(Dog.class),
       @JsonSubTypes.Type(Cat.class)})
    public interface Animal {
    
    }
    
    @JsonTypeName("bark")
    public class Dog implements Animal {
    
       private String bark;
       
       // Default constructor, getters and setters
    }
    
    @JsonTypeName("meow")
    public class Cat implements Animal {
    
       private String meow;
       
       // Default constructor, getters and setters
    }
    

    除非您有任何特殊需要处理,否则您可以跳过编写自定义反序列化程序。

    【讨论】:

      【解决方案2】:

      Jackson 没有为此提供out of the box solution,但这并不意味着您不走运。


      假设你的类实现了一个通用接口或者扩展了一个通用类,如下图:

      public interface Animal {
      
      }
      
      public class Dog implements Animal {
      
         private String bark;
         
         // Default constructor, getters and setters
      }
      
      public class Cat implements Animal {
      
         private String meow;
         
         // Default constructor, getters and setters
      }
      

      您可以根据属性名称创建自定义反序列化程序。它允许您定义一个 unique 属性,该属性将用于查找类以执行反序列化:

      public class PropertyBasedDeserializer<T> extends StdDeserializer<T> {
      
          private Map<String, Class<? extends T>> deserializationClasses;
      
          public PropertyBasedDeserializer(Class<T> baseClass) {
              super(baseClass);
              deserializationClasses = new HashMap<String, Class<? extends T>>();
          }
      
          public void register(String property, Class<? extends T> deserializationClass) {
              deserializationClasses.put(property, deserializationClass);
          }
      
          @Override
          public T deserialize(JsonParser p, DeserializationContext ctxt)
                  throws IOException, JsonProcessingException {
      
              ObjectMapper mapper = (ObjectMapper) p.getCodec();
              JsonNode tree = mapper.readTree(p);
              
              Class<? extends T> deserializationClass = findDeserializationClass(tree);
              if (deserializationClass == null) {
                  throw JsonMappingException.from(ctxt, 
                     "No registered unique properties found for polymorphic deserialization");
              }
      
              return mapper.treeToValue(tree, deserializationClass);
          }
          
          private Class<? extends T> findDeserializationClass(JsonNode tree) {
              
              Iterator<Entry<String, JsonNode>> fields = tree.fields();
              Class<? extends T> deserializationClass = null;
              
              while (fields.hasNext()) {
                  Entry<String, JsonNode> field = fields.next();
                  String property = field.getKey();
                  if (deserializationClasses.containsKey(property)) {
                      deserializationClass = deserializationClasses.get(property);
                      break;  
                  }
              }
              
              return deserializationClass;
          }
      }
      

      然后实例化并配置反序列化器:

      PropertyBasedDeserializer<Animal> deserializer = 
              new PropertyBasedDeserializer<>(Animal.class);
      
      deserializer.register("bark", Dog.class); // If "bark" is present, then it's a Dog
      deserializer.register("meow", Cat.class); // If "meow" is present, then it's a Cat
      

      将其添加到模块中:

      SimpleModule module = new SimpleModule("custom-deserializers", Version.unknownVersion());
      module.addDeserializer(Animal.class, deserializer);
      

      注册模块并照常执行反序列化:

      ObjectMapper mapper = new ObjectMapper();
      mapper.registerModule(module);
      
      String json = "[{\"bark\":\"bowwow\"}, {\"bark\":\"woofWoof\"}, {\"meow\":\"meeeOwww\"}]";
      List<Animal> animals = mapper.readValue(json, new TypeReference<List<Animal>>() { });
      

      【讨论】:

      【解决方案3】:

      使用fasterxml jackson,您可以这样做:

      abstract class FooOrBar {
          companion object {
              @JvmStatic
              @JsonCreator
              private fun creator(json: Map<String, String>): FooOrBar? {
                  return when {
                      json.containsKey("foo") -> Foo(json["foo"] as String)
                      json.containsKey("bar") -> Foo(json["bar"] as String)
                      else -> null
                  }
              }
          }
      }
      
      class Foo(val foo: String) : FooOrBar() // even can use map delegate if you know what it is
      class Bar(val bar: String) : FooOrBar()
      

      它是 Kotlin,但你会明白的。

      注意 使用@JsonCreator。带注释的creator 函数只有一个参数(这是JsonCreator 所需的两个签名中的一种),JSON 被反序列化为Map 实例并传递给creator。从这里,您可以创建您的类实例。

      ----------------更新--------------

      您还可以将JsonNode 用于creator 函数,用于嵌套和复杂的JSON。

      private fun creator(json: JsonNode): FooOrBar?
      

      【讨论】:

        【解决方案4】:

        你必须告诉杰克逊你期望什么课程:

        Foo readValue = mapper.readValue(json, Foo.class);

        Bar readValue = mapper.readValue(json, Bar.class);

        否则,如果您的设计需要强类型,那么在这种情况下可能值得使用 XML。

        【讨论】:

        • “你必须告诉杰克逊你期待什么课程。” 没错。但是您将如何找到在运行时使用的正确类?见我的answer
        猜你喜欢
        • 2016-08-15
        • 1970-01-01
        • 2017-09-15
        • 2012-08-19
        • 2013-04-09
        • 1970-01-01
        • 1970-01-01
        • 2020-01-25
        • 1970-01-01
        相关资源
        最近更新 更多