繁体   English   中英

Jackson 根据属性名称反序列化

[英]Jackson deserialize based on property name

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

{"foo": "String value"}

{"bar": "String value"}

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

Jackson 没有为此提供开箱即用的解决方案,但这并不意味着您不走运。


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

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
}

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

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

使用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所需的两个签名JsonCreator ),JSON 被反序列化为Map实例并传递给creator 从这里,您可以创建您的类实例。

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

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

private fun creator(json: JsonNode): FooOrBar?

我可能来不及回答,但仍然发布我的发现。 我遇到了完全相似的情况,在阅读了文档后,我遇到了以下文档

// 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})

以下是我用来让它工作的设置:(使用上面帖子中@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
}

除非您有任何特定需要处理,否则您可以跳过编写自定义解串器。

您必须告诉 jackson 您期望的课程:

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

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

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

暂无
暂无

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

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