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

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

问题描述

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

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 primitive对象中设置的名称.某些灵活性提供了注释方法.让我们定义简单的注释:

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

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

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

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

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

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