Jackson 添加额外的属性

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的子类同样有效

element-ui 粘贴上传图片
Instagram 模拟登录