简体   繁体   English

Jackson 根据属性名称反序列化

[英]Jackson deserialize based on property name

I have the following two types of JSON objects:我有以下两种类型的 JSON 对象:

{"foo": "String value"}

and

{"bar": "String value"}

Both of them represent a specialized type of the same base object.它们都代表同一基础对象的特殊类型。 How can I use Jackson for deserializing them ?我如何使用 Jackson 反序列化它们? The type information is only represented by the keys themselves and not the value for any key (almost all examples use the value of the key for determining the type : https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization )类型信息仅由键本身表示,而不是任何键的值(几乎所有示例都使用键的值来确定类型: https : //github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

Jackson doesn't offer an out of the box solution for that, but it doesn't mean that you are out of luck. Jackson 没有为此提供开箱即用的解决方案,但这并不意味着您不走运。


Assuming that your classes implement a common interface or extend a common class, as shown below:假设你的类实现了一个通用接口或者扩展了一个通用类,如下图:

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
}

You can create a custom deserializer based on the property name.您可以根据属性名称创建自定义反序列化器。 It allows you to define a unique property that will be used to look up the class to perform the deserialization to:它允许您定义一个唯一的属性,该属性将用于查找类以执行反序列化:

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;
    }
}

Then instantiate and configure the deserializer:然后实例化并配置解串器:

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

Add it to a module:将其添加到模块中:

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

Register the module and perform the deserialization as usual:注册模块并照常执行反序列化:

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>>() { });

With fasterxml jackson , you can do this:使用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()

It is Kotlin, but you will get the idea.它是 Kotlin,但你会明白的。

Note the @JsonCreator is used.请注意使用了@JsonCreator the annotated creator function has single argument (which is one kind of the two signatures required by JsonCreator ), JSON is deserialized as a Map instance and passed to the creator .带注释的creator函数只有一个参数(这是JsonCreator所需的两个签名JsonCreator ),JSON 被反序列化为Map实例并传递给creator From here, you can create your class instance.从这里,您可以创建您的类实例。

----------------UPDATE------------------------- - - - - - - - - 更新 - - - - - - - - - - - - -

You can also use JsonNode for the creator function for nested and complex JSON.您还可以将JsonNode用作嵌套和复杂 JSON 的creator函数。

private fun creator(json: JsonNode): FooOrBar?

I might be too late to answer, but still posting my findings.我可能来不及回答,但仍然发布我的发现。 I encountered exact similar situation, and after going through documentation I came across following doc :我遇到了完全相似的情况,在阅读了文档后,我遇到了以下文档

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

@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT) @JsonSubTypes({com.myemp.Impl1.class, com.myempl.Impl2.class}) @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT) @JsonSubTypes({com.myemp.Impl1.class, com.myempl.Impl2.class})

Following is the setup that I used to get it working: (Using same example used by @cassiomolin in above post)以下是我用来让它工作的设置:(使用上面帖子中@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
}

You can skip writing custom deserializer unless you have any specific need to handle.除非您有任何特定需要处理,否则您可以跳过编写自定义解串器。

You'll have to tell jackson what class you expect:您必须告诉 jackson 您期望的课程:

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

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

Otherwise it may be worth using XML in this case if you strong types are necessary for your design.否则,如果您的设计需要强类型,那么在这种情况下使用 XML 可能是值得的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM