Java 使用Jackson反序列化为字符串或对象

Java 使用Jackson反序列化为字符串或对象,java,jackson,Java,Jackson,我有一个对象有时看起来像这样: { "foo" : "bar", "fuzz" : "bla" } { "foo" : { "value" : "bar", "baz": "asdf" }, "fuzz" : { "thing" : "bla", "blip" : "asdf" } } 有时看起来是这样的: { "foo" : "bar", "fuzz" : "bla" } { "foo" : { "value" : "bar", "baz": "

我有一个对象有时看起来像这样:

{
   "foo" : "bar",
   "fuzz" : "bla"
}
{
   "foo" : { "value" : "bar", "baz": "asdf" },
   "fuzz" : { "thing" : "bla", "blip" : "asdf" }
}
有时看起来是这样的:

{
   "foo" : "bar",
   "fuzz" : "bla"
}
{
   "foo" : { "value" : "bar", "baz": "asdf" },
   "fuzz" : { "thing" : "bla", "blip" : "asdf" }
}
这些类看起来像:

public class Foo {
   String value;
   String baz;
}

public class Fuzz {
   String thing;
   String blip;
}

第一种情况是第二种情况的简写。我想一直反序列化到第二个案例中

此外,这在我们的代码中是一种非常常见的模式,因此我希望能够以一种通用的方式进行序列化,因为还有其他类似于上面的
Foo
的类具有相同的模式,即使用字符串作为更复杂对象的语法糖

我想使用它的代码应该是这样的


public class Thing { 
  @JsonProperty("fuzz")
  Fuzz fuzz;

  @JsonProperty("foo")
  Foo foo;
}


如何编写一个自定义反序列化程序(或其他模块)来处理这两种情况?

要使其通用,我们需要能够指定要在对象中为
JSON原语设置的名称。注释方法具有一定的灵活性。让我们定义简单注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface JsonPrimitiveName {
    String value();
}
名称表示:如果原语出现在
JSON
中,请使用
value()
获取给定原语的属性名称。它将
JSON原语
POJO
字段绑定。处理
JSON对象
JSON原语
的简单反序列化程序:

class PrimitiveOrPojoJsonDeserializer extends JsonDeserializer implements ContextualDeserializer {

    private String primitiveName;
    private JavaType type;

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(type);
        if (p.currentToken() == JsonToken.START_OBJECT) {
            return deserializer.deserialize(p, ctxt);
        } else if (p.currentToken() == JsonToken.VALUE_STRING) {
            BeanDeserializer beanDeserializer = (BeanDeserializer) deserializer;
            try {
                Object instance = beanDeserializer.getValueInstantiator().getDefaultCreator().call();
                SettableBeanProperty property = beanDeserializer.findProperty(primitiveName);
                property.deserializeAndSet(p, ctxt, instance);
                return instance;
            } catch (Exception e) {
                throw JsonMappingException.from(p, e.getMessage());
            }
        }

        return null;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        JsonPrimitiveName annotation = property.getAnnotation(JsonPrimitiveName.class);

        PrimitiveOrPojoJsonDeserializer deserializer = new PrimitiveOrPojoJsonDeserializer();
        deserializer.primitiveName = annotation.value();
        deserializer.type = property.getType();

        return deserializer;
    }
}
我假设所有类都是
POJO
-s,并遵循所有规则-具有
getter
setter
和默认构造函数。如果构造函数不存在,您需要以某种方式更改此
beanDeserializer.getValueInstantiator().getDefaultCreator().call()
行,以满足您的需求

示例应用程序:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.readValue(jsonFile, Root.class));
    }
}
打印缩短的
JSON

Root{foo=Foo{value='bar', baz='null'}, fuzz=Fuzz{thing='bla', blip='null'}}
对于完整的
JSON
有效负载:

Root{foo=Foo{value='bar', baz='asdf'}, fuzz=Fuzz{thing='bla', blip='asdf'}}

在第一个示例中,“foo”是一个JsonNodeType.STRING。您希望将此字符串值反序列化到类型为“Foo”的哪个字段中?“值”或“baz”?这个。是太棒了,太棒了!在我们的例子中,我们要求构造函数获取参数并使用
@JsonProperty
注释这些参数,而不是使用
getDefaultCreator
。在那种情况下,我怎么能使用它呢?@Doug,在这种情况下,你需要对
Object[]
使用
call
方法:
call(Object[]args)
。您需要提供所有必需的参数。数组的大小必须与构造函数定义匹配。如果不是所有类型都相同,则需要从注释向反序列化程序提供额外的数据。但是为了保持简单,我建议创建
no-arg
构造函数。