某些字段的杰克逊 JSON 自定义序列化
Posted
技术标签:
【中文标题】某些字段的杰克逊 JSON 自定义序列化【英文标题】:Jackson JSON custom serialization for certain fields 【发布时间】:2012-08-16 07:43:36 【问题描述】:有没有办法使用 Jackson JSON 处理器进行自定义字段级序列化?例如,我想上课
public class Person
public String name;
public int age;
public int favoriteNumber;
序列化为以下 JSON:
"name": "Joe", "age": 25, "favoriteNumber": "123"
请注意,age=25 被编码为 数字,而 favoriteNumber=123 被编码为 字符串。开箱即用的杰克逊将int
编组到一个号码。在这种情况下,我希望将 favoriteNumber 编码为字符串。
【问题讨论】:
我写了一篇关于How to Write a Custom Serializer with Jackson 的帖子,可能对某些人有帮助。 【参考方案1】:您可以按如下方式实现自定义序列化程序:
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 应该为您处理从 int
到 Integer
的自动装箱。
【讨论】:
Jackson-databind(至少 2.1.3)已经包含特殊的 ToStringSerializer,看我的回答。 @KevinBowersox 你能帮我解决一下我的deserializing problem吗? 注意as=String.class
由于 using
参数而被忽略,此处不需要。 Note: if using() is also used it has precedence (since it directly specified serializer, whereas this would only be used to locate the serializer) and value of this annotation property is ignored.
就我而言,由于我使用的类型,它甚至在as=String.class
部分失败了。 @kevin-bowersox,我建议根据@GarethLatty 所说的更新您的评论。
在 kotlin 中,您可以通过用反引号括起来来使用保留关键字。 @kit【参考方案2】:
Jackson-databind(至少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:
【讨论】:
String 需要转换为 int 的相反情况呢?我没有看到 ToIntSerializer.class。 @jEremyB 你可能需要写一个custom deserializer ToStringSerializer 有效,但 FloatSerializer 带来了以下信息:无法写入内容:java.lang.Integer cannot be cast to java.lang.Float【参考方案3】:为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"
【讨论】:
【参考方案4】: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"
【讨论】:
【参考方案5】:如果你不想用注解污染你的模型并且想要执行一些自定义操作,你可以使用 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");
【讨论】:
【参考方案6】:在 @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);
现在需要告诉 spring 使用这个自定义的 json 转换,只需将它放在 dispatcher-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>
这就是您可以决定要序列化哪些字段的方式。
【讨论】:
【参考方案7】:您可以在 mixin 中内联创建自定义序列化程序。然后用它注释一个字段。请参阅下面的示例,该示例将“ - something else ”附加到 lang 字段。这有点 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");
【讨论】:
以上是关于某些字段的杰克逊 JSON 自定义序列化的主要内容,如果未能解决你的问题,请参考以下文章
如何在杰克逊序列化中自定义日期,@JsonSerialize 不起作用
如何在没有jackson注释的情况下忽略反序列化的某些字段?
Spring boot,Jackson Json 在序列化和反序列化时出现问题
Newtonsoft.Json高级用法 1.忽略某些属性 2.默认值的处理 3.空值的处理 4.支持非公共成员 5.日期处理 6.自定义序列化的