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

标签 java jackson

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

{
   "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 的类具有使用 String 作为更复杂对象的语法糖的相同模式。

我想象使用它的代码看起来像这样


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

  @JsonProperty("foo")
  Foo foo;
}

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

最佳答案

为了使其通用,我们需要能够指定我们希望在对象中为 JSON primitive 设置的名称。一些灵 active 提供了注释方法。让我们定义简单的注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface JsonPrimitiveName {
    String value();
}

名称意味着:如果原语将出现在 JSON 中,请使用 value() 获取给定原语的属性名称。它将 JSON primitivePOJO 字段绑定(bind)。处理 JSON objectJSON primitive 的简单反序列化器:

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字段如下:

class Root {

    @JsonPrimitiveName("value")
    @JsonDeserialize(using = PrimitiveOrPojoJsonDeserializer.class)
    private Foo foo;

    @JsonPrimitiveName("thing")
    @JsonDeserialize(using = PrimitiveOrPojoJsonDeserializer.class)
    private Fuzz fuzz;

    // getters, setters
}

我假设所有类都是 POJO 并遵循所有规则 - 具有 gettersetter 和默认构造函数。如果构造函数不存在,您需要以某种方式更改此 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'}}

关于java - 使用 Jackson 反序列化为字符串或对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55433367/

相关文章:

java - 需要显示一个可以动态(无限)添加到(java)的树状结构?

java - JUnit 测试类中的设计依赖项

java - 使用 ReferenceTypeDeserializer 与 Jackson & Spring 反序列化通用类型

java - 在GCP实例中下载或构建maven项目的方法是什么?

JavaFX - 如何将键盘输入集中在 Hbox 与其他 Pane 中包含的一个 Pane 中

Java 正则表达式匹配 "t",除非它是 "[t"或 "t]"

java - 如何将 JSON null 反序列化为 NullNode 而不是 Java null?

java - Jackson - 无法反序列化带有时区偏移量的日期时间 'unparsed text found at index 23'

java - 将嵌套的 JSON 字符串转换为 java 对象列表

java - 使用 Jackson Exception 构建 RESTFul Web 服务