某些字段的Jackson JSON自定义序列化


93

有没有一种使用Jackson JSON Processor进行自定义字段级序列化的方法?例如,我想上课

public class Person {
    public String name;
    public int age;
    public int favoriteNumber;
}

序列化为以下JSON:

{ "name": "Joe", "age": 25, "favoriteNumber": "123" }

请注意,age = 25被编码为数字,而favoriteNumber = 123被编码为string。杰克逊开箱int即用。在这种情况下,我希望将favoriteNumber编码为字符串。


1
我写了一篇有关如何用Jackson编写自定义序列化程序的文章,这可能对某些人有所帮助。
山姆·贝里

Answers:


106

您可以实现自定义序列化器,如下所示:

public class Person {
    public String name;
    public int age;
    @JsonSerialize(using = IntToStringSerializer.class, as=String.class)
    public int favoriteNumber:
}


public class IntToStringSerializer extends JsonSerializer<Integer> {

    @Override
    public void serialize(Integer tmpInt, 
                          JsonGenerator jsonGenerator, 
                          SerializerProvider serializerProvider) 
                          throws IOException, JsonProcessingException {
        jsonGenerator.writeObject(tmpInt.toString());
    }
}

Java应该为您处理自动装箱从intInteger


3
Jackson-databind(至少2.1.3)已经包含特殊的ToStringSerializer,请参阅我的答案。
werupokz

@KevinBowersox您能帮我解决反序列化问题吗?
JJD 2014年


有没有更可怕的方法可以做到这一点?喜欢Person implements ToJson吗?
jameshfisher 2015年

1
就我而言,as=String.class由于我使用的类型,它甚至在部分上失败了。@ kevin-bowersox,我建议根据@GarethLatty所说的来更新您的评论。
Bert

54

Jackson-databind(至少2.1.3)提供了特殊的ToStringSerializercom.fasterxml.jackson.databind.ser.std.ToStringSerializer

例:

public class Person {
    public String name;
    public int age;
    @JsonSerialize(using = ToStringSerializer.class)
    public int favoriteNumber:
}

3
需要将String转换为int的情况相反呢?我没有看到ToIntSerializer.class。
jEremyB 2015年

@jEremyB您可能必须编写自定义解串器
Drew Stephens'Apr

ToStringSerializer可以工作,但FloatSerializer会显示以下消息:无法编写内容:java.lang.Integer无法转换为java.lang.Float
Arnie Schwarzvogel,

11

添加一个带@JsonProperty注释的getter,它String为该favoriteNumber字段返回a :

public class Person {
    public String name;
    public int age;
    private int favoriteNumber;

    public Person(String name, int age, int favoriteNumber) {
        this.name = name;
        this.age = age;
        this.favoriteNumber = favoriteNumber;
    }

    @JsonProperty
    public String getFavoriteNumber() {
        return String.valueOf(favoriteNumber);
    }

    public static void main(String... args) throws Exception {
        Person p = new Person("Joe", 25, 123);
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(p)); 
        // {"name":"Joe","age":25,"favoriteNumber":"123"}
    }
}

11

jackson-annotations提供@JsonFormat了无需编写定制序列化程序即可处理许多定制的功能。

例如,请求STRING具有数字类型的字段的形状会将数字值输出为字符串

public class Person {
    public String name;
    public int age;
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    public int favoriteNumber;
}

将产生所需的输出

{"name":"Joe","age":25,"favoriteNumber":"123"}

7

如果您不想使用注释污染模型并想要执行一些自定义操作,则可以使用mixins。

ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.setMixInAnnotation(Person.class, PersonMixin.class);
mapper.registerModule(simpleModule);

超越年龄:

public abstract class PersonMixin {
    @JsonSerialize(using = PersonAgeSerializer.class)
    public String age;
}

随着年龄的增长做任何您需要的事情:

public class PersonAgeSerializer extends JsonSerializer<Integer> {
    @Override
    public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(String.valueOf(integer * 52) + " months");
    }
}

2

借助@JsonView,我们可以决定要满足最小条件(必须定义条件)的模型类的字段进行序列化,例如我们可以拥有一个具有10个属性的核心类,但是只有5个属性可以序列化,这对于客户端来说是必需的只要

只需创建以下类即可定义我们的视图:

public class Views
{
    static class Android{};
    static class IOS{};
    static class Web{};
}

带视图的带注释的模型类:

public class Demo 
{
    public Demo() 
    {
    }

@JsonView(Views.IOS.class)
private String iosField;

@JsonView(Views.Android.class)
private String androidField;

@JsonView(Views.Web.class)
private String webField;

 // getters/setters
...
..
}

现在,我们必须通过从spring简单扩展HttpMessageConverter类来编写自定义json转换器:

    public class CustomJacksonConverter implements HttpMessageConverter<Object> 
    {
    public CustomJacksonConverter() 
        {
            super();
        //this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.ClientView.class));
        this.delegate.getObjectMapper().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
        this.delegate.getObjectMapper().setSerializationInclusion(Include.NON_NULL);

    }

    // a real message converter that will respond to methods and do the actual work
    private MappingJackson2HttpMessageConverter delegate = new MappingJackson2HttpMessageConverter();

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return delegate.canRead(clazz, mediaType);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return delegate.canWrite(clazz, mediaType);
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return delegate.getSupportedMediaTypes();
    }

    @Override
    public Object read(Class<? extends Object> clazz,
            HttpInputMessage inputMessage) throws IOException,
            HttpMessageNotReadableException {
        return delegate.read(clazz, inputMessage);
    }

    @Override
    public void write(Object obj, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException 
    {
        synchronized(this) 
        {
            String userAgent = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("userAgent");
            if ( userAgent != null ) 
            {
                switch (userAgent) 
                {
                case "IOS" :
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.IOS.class));
                    break;
                case "Android" :
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.Android.class));
                    break;
                case "Web" :
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( Views.Web.class));
                    break;
                default:
                    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null ));
                    break;
                }
            }
            else
            {
                // reset to default view
                this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null ));
            }
            delegate.write(obj, contentType, outputMessage);
        }
    }

}

现在需要通过简单地将它放在dispatcher-servlet.xml中来告诉spring使用此自定义json转换

<mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean id="jsonConverter" class="com.mactores.org.CustomJacksonConverter" >
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

这样便可以决定要序列化的字段。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.