使用 Jackson 反序列化为字符串或对象 [英] Deserialize to String or Object using Jackson

查看:72
本文介绍了使用 Jackson 反序列化为字符串或对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an object that sometimes looks like this:

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

其中第一种情况是第二种情况的简写.我想总是反序列化到第二种情况.

where the first cases are shorthand for the second ones. I would like to always deserialize into the second case.

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

Further - this is a pretty common pattern in our code, so I would like to be able to do the serialization in a generic manner, as there are other classes similar to Foo above that have the same pattern of using String as a syntactic sugar for a more complex object.

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

I'd imagine the code to use it would look something like this


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

  @JsonProperty("foo")
  Foo foo;
}

如何编写一个自定义的解串器(或其他一些模块)来处理这两种情况?

How do I write a custom deserializer (or some other module) that generically handles both cases?

推荐答案

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

To make it generic we need to be able to specify name which we would like to set in object for JSON primitive. Some flexibility gives annotation approach. Let's define simple annotation:

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

Name 表示:如果原语出现在 JSON 中,请使用 value() 获取给定原语的属性名称.它将 JSON 原语POJO 字段绑定在一起.处理 JSON objectJSON 原语 的简单反序列化器:

Name means: in case primitive will appear in JSON use value() to get property name for given primitive. It binds JSON primitive with POJO field. Simple deserialiser which handles JSON object and JSON 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字段如下:

Now we need to annotate POJO fields as below:

class Root {

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

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

    // getters, setters
}

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

I assume that all classes are POJO-s and follow all rules - have getters, setters and default constructor. In case constructor does not exist you need to change this beanDeserializer.getValueInstantiator().getDefaultCreator().call() line somehow which fits your requirements.

示例应用:

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 有效负载:

And for full JSON payload:

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

这篇关于使用 Jackson 反序列化为字符串或对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆