1. 通过额外的Wrapper实现
public static class Wrapper<T> {
@JsonUnwrapped
private final T t;
private final String extraProperty;
public Wrapper(T t, String extraProperty) {
this.t = t;
this.extraProperty = extraProperty;
}
public String getExtraProperty(){
return "123";
}
}
2. 通过自定义 JsonSerializer
final class MessageSerializer extends JsonSerializer<Message> {
/**
* { message : 'error message', code(optional) : 'error.code', field(optional) :
* 'error field'//form submit }
*/
@Override
public void serialize(Message value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeStartObject();
gen.writeObjectField("code", value.getCode());
gen.writeObjectField("message", getMessage(value, LocaleContextHolder.getLocale()));
gen.writeEndObject();
}
private String getMessage(MessageSourceResolvable resolvable, Locale locale) {
return HtmlUtils.htmlEscape(messageSource.getMessage(resolvable, locale));
}
}
在上面的代码中,虽然可以注册额外的属性,但问题需要我们手动添加其他属性,因为我们不能使用 gen.writeObject(value)
这样的代码,会直接StackoverFlowError
,而且还有一个致命的缺点,就是Message
的子类的属性无法添加,要想实现自动添加属性以及子类属性的输出,我们需要默认的 JsonSerializer
。
获取默认的 JsonSerializer
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (Message.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new MessageSerializer((JsonSerializer<Object>) serializer)
return super.modifySerializer(config, beanDesc, serializer);
}
});
mapper.registerModule(module);
在模块里添加一个BeanSerializerModifier
,如果判断是 Message
或其子类,返回包含默认JsonSerializer
的实例(这样的话我们需要额外注册MessageSerializer
了),在MessageSerializer
中,如下实现即可:
gen.writeStartObject();
defaultSerializer.unwrappingSerializer(null).serialize(value, gen, provider);
gen.writeObjectField("message", getMessage(value, LocaleContextHolder.getLocale()));
gen.writeEndObject();
这里的unwrappingSerializer跟JsonUnwrapped的效果是一样的
如果不想这么麻烦,还可以用另一种方法来获取默认的JsonSerializer
,此方法跟版本关系很大,具体请见 https://stackoverflow.com/questions/14714328/jackson-how-to-add-custom-property-to-the-json-without-modifying-the-pojo#answer-25360636
JavaType type = provider.constructType(value.getClass());
BeanDescription beanDescription = provider.getConfig().introspect(type);
JsonSerializer<Object> serializer = BeanSerializerFactory.instance.findBeanOrAddOnSerializer(provider,type,beanDescription,provider.isEnabled(MapperFeature.USE_STATIC_TYPING));
serializer.unwrappingSerializer(null).serialize(value,gen,provider);
3. 通过JsonAppend annotation(Jackson2.5+)来实现
@JsonAppend(
attrs = {
@JsonAppend.Attr(value = "version",propName = "version2")
}
)
public static final class Test2{
private String name = "123";
public String getName(){
return this.name;
}
}
上面的annotation中增加了一个额外的version2属性 (这里的value只是一个propName的一个替代,并非可以指定默认值,如果同时指定了value和propName,那么以propName优先)
最后输出:
System.out.println(objectMapper.writerFor(Test2.class).withAttribute("version","1.0").withAttribute("version2","2.0").writeValueAsString(new Test2()));
JsonAppend还有一个 props同样可以指定属性,而且更加的灵活,无需额外的withAttribute
,但需要一个额外的 VirtualBeanPropertyWriter
@JsonAppend(
props = {
@JsonAppend.Prop(value = TestWriter.class,type = String.class,name = "version")
}
)
public static final class Test2{
private String name = "123";
public String getName(){
return this.name;
}
}
public static final class TestWriter extends VirtualBeanPropertyWriter{
public TestWriter() {}
private TestWriter(BeanPropertyDefinition propDef, Annotations contextAnnotations, JavaType declaredType) {
super(propDef, contextAnnotations, declaredType);
}
@Override
protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
return "123";
}
@Override
public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) {
return new TestWriter(propDef,declaringClass.getAnnotations(),type);
}
}
这里的TestWriter
必须提供一个无参构造器,withConfig方法虽然调用情况不明,但测试后发现同一个class(子类不算),只会调用一次
如果不希望改变原POJO,可以创建一个额外的类,然后通过ObjectMapper#addMixIn
来注册,例如:
@JsonAppend(attrs = {
@JsonAppend.Attr("version")
})
public static final class MinIn{
}
public static final class Test2{
private String name = "123";
public String getName(){
return this.name;
}
}
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Test2.class, MinIn.class);
System.out.println(mapper.writerFor(Test2.class).withAttribute("version","2.0").writeValueAsString(new Test2()));
对Test2的子类同样有效