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

有没有使用 Jackson JSON 处理器进行自定义字段级别序列化的方法?比如说,我想上这门课

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

序列化为以下 JSON:

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

注意 age = 25被编码为 号码,而 FaviteNumber = 123被编码为 string。杰克逊将 int编码成一个数字。在这种情况下,我希望 FaviteNumber 被编码为字符串。

145500 次浏览

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

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

您可以按以下方式实现自定义序列化程序:

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的自动装箱。

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

例如:

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

@ JsonView的帮助下,我们可以决定序列化哪些模型类的字段满足最小的条件(我们必须定义这些条件) ,就像我们可以有一个具有10个属性的核心类,但只有5个属性可以序列化,这只是客户端所需要的

Define our Views by simply creating following class:

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

Annotated model class with views:

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
...
..
}

Now we have to write custom json converter by simply extending HttpMessageConverter class from spring as:

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


}

现在需要告诉 spring 使用这个自定义的 json 转换,只需将其放入 patcher-servlet. xml 中即可

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

That's how you will able to decide which fields to get serialize.

Jackson-annotions 提供了 @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"}

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

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

您可以在 Mixin 中内联地创建自定义序列化程序。然后用它注释字段。见下面的例子,附加“ 别的东西”到 lang 字段。这是一种 < strong > hackish -如果您的序列化程序需要类似于一个存储库或 Spring 注入的任何东西,这将是一个问题。也许最好使用自定义反序列化器/序列化器而不是 Mixin。

package com.test;


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.test.Argument;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//Serialize only fields explicitly mentioned by this mixin.
@JsonAutoDetect(
fieldVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
getterVisibility = Visibility.NONE,
isGetterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE
)
@JsonPropertyOrder({"lang", "name", "value"})
public abstract class V2ArgumentMixin {


@JsonProperty("name")
private String name;


@JsonSerialize(using = LangCustomSerializer.class, as=String.class)
@JsonProperty("lang")
private String lang;


@JsonProperty("value")
private Object value;




  

public static class LangCustomSerializer extends JsonSerializer<String> {


@Override
public void serialize(String value,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)
throws IOException, JsonProcessingException {
jsonGenerator.writeObject(value.toString() + "  - something else");
}
}
}