Ale*_*yda 55 java customization serialization json jackson
我正在为我的应用程序开发一个REST接口,使用Jackson将我的POJO域对象序列化为JSON表示.我想为某些类型自定义序列化,以便为POJO中不存在的JSON表示添加其他属性(例如,添加一些元数据,参考数据等).我知道如何编写自己的JsonSerializer
,但在这种情况下,我需要为我的对象的每个属性显式调用JsonGenerator.writeXXX(..)
方法,而我只需添加一个额外的属性.换句话说,我希望能够写出如下内容:
@Override
public void serialize(TaxonomyNode value, JsonGenerator jgen, SerializerProvider provider) {
jgen.writeStartObject();
jgen.writeAllFields(value); // <-- The method I'd like to have
jgen.writeObjectField("my_extra_field", "some data");
jgen.writeEndObject();
}
Run Code Online (Sandbox Code Playgroud)
或者(甚至更好)在jgen.writeEndObject()
调用之前以某种方式拦截序列化,例如:
@Override void beforeEndObject(....) {
jgen.writeObjectField("my_extra_field", "some data");
}
Run Code Online (Sandbox Code Playgroud)
我以为我可以扩展BeanSerializer
和覆盖它的serialize(..)
方法,但是它被声明了final
,而且我找不到一种简单的方法来创建一个新的实例BeanSerializer
而不提供所有类型的元数据细节实际上复制了杰克逊的一部分.所以我放弃了这样做.
我的问题是 - 如何定制Jackson的序列化,为特定POJO的JSON输出添加额外的东西,而不会引入太多的样板代码并尽可能多地重用默认的Jackson行为.
rya*_*anp 37
自从(我认为)杰克逊1.7以来,你可以通过BeanSerializerModifier
扩展来做到这一点BeanSerializerBase
.我用Jackson 2.0.4测试了下面的例子.
import java.io.IOException;
import org.junit.Test;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
public class JacksonSerializeWithExtraField {
@Test
public void testAddExtraField() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new SimpleModule() {
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
public JsonSerializer<?> modifySerializer(
SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (serializer instanceof BeanSerializerBase) {
return new ExtraFieldSerializer(
(BeanSerializerBase) serializer);
}
return serializer;
}
});
}
});
mapper.writeValue(System.out, new MyClass());
//prints {"classField":"classFieldValue","extraField":"extraFieldValue"}
}
class MyClass {
private String classField = "classFieldValue";
public String getClassField() {
return classField;
}
public void setClassField(String classField) {
this.classField = classField;
}
}
class ExtraFieldSerializer extends BeanSerializerBase {
ExtraFieldSerializer(BeanSerializerBase source) {
super(source);
}
ExtraFieldSerializer(ExtraFieldSerializer source,
ObjectIdWriter objectIdWriter) {
super(source, objectIdWriter);
}
ExtraFieldSerializer(ExtraFieldSerializer source,
String[] toIgnore) {
super(source, toIgnore);
}
protected BeanSerializerBase withObjectIdWriter(
ObjectIdWriter objectIdWriter) {
return new ExtraFieldSerializer(this, objectIdWriter);
}
protected BeanSerializerBase withIgnorals(String[] toIgnore) {
return new ExtraFieldSerializer(this, toIgnore);
}
public void serialize(Object bean, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonGenerationException {
jgen.writeStartObject();
serializeFields(bean, jgen, provider);
jgen.writeStringField("extraField", "extraFieldValue");
jgen.writeEndObject();
}
}
}
Run Code Online (Sandbox Code Playgroud)
Hen*_*sen 26
Jackson 2.5引入了@JsonAppend
注释,可用于在序列化期间添加"虚拟"属性.它可以与mixin功能一起使用,以避免修改原始POJO.
以下示例ApprovalState
在序列化期间添加属性:
@JsonAppend(
attrs = {
@JsonAppend.Attr(value = "ApprovalState")
}
)
public static class ApprovalMixin {}
Run Code Online (Sandbox Code Playgroud)
注册mixin ObjectMapper
:
mapper.addMixIn(POJO.class, ApprovalMixin.class);
Run Code Online (Sandbox Code Playgroud)
使用an ObjectWriter
在序列化期间设置属性:
ObjectWriter writer = mapper.writerFor(POJO.class)
.withAttribute("ApprovalState", "Pending");
Run Code Online (Sandbox Code Playgroud)
使用编写器进行序列化将ApprovalState
字段添加到输出.
Die*_*rDP 17
虽然这个问题已经得到解答,但我找到了另一种不需要特殊杰克逊钩子的方式.
static class JsonWrapper<T> {
@JsonUnwrapped
private T inner;
private String extraField;
public JsonWrapper(T inner, String field) {
this.inner = inner;
this.extraField = field;
}
public T getInner() {
return inner;
}
public String getExtraField() {
return extraField;
}
}
static class BaseClass {
private String baseField;
public BaseClass(String baseField) {
this.baseField = baseField;
}
public String getBaseField() {
return baseField;
}
}
public static void main(String[] args) throws JsonProcessingException {
Object input = new JsonWrapper<>(new BaseClass("inner"), "outer");
System.out.println(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(input));
}
Run Code Online (Sandbox Code Playgroud)
输出:
{
"baseField" : "inner",
"extraField" : "outer"
}
Run Code Online (Sandbox Code Playgroud)
要编写集合,您只需使用视图:
public static void main(String[] args) throws JsonProcessingException {
List<BaseClass> inputs = Arrays.asList(new BaseClass("1"), new BaseClass("2"));
//Google Guava Library <3
List<JsonWrapper<BaseClass>> modInputs = Lists.transform(inputs, base -> new JsonWrapper<>(base, "hello"));
System.out.println(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(modInputs));
}
Run Code Online (Sandbox Code Playgroud)
输出:
[ {
"baseField" : "1",
"extraField" : "hello"
}, {
"baseField" : "2",
"extraField" : "hello"
} ]
Run Code Online (Sandbox Code Playgroud)
Ras*_*ber 16
你可以这样做(以前的版本在2.6之后不适用于Jackson,但这适用于Jackson 2.7.3):
public static class CustomModule extends SimpleModule {
public CustomModule() {
addSerializer(CustomClass.class, new CustomClassSerializer());
}
private static class CustomClassSerializer extends JsonSerializer {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
//Validate.isInstanceOf(CustomClass.class, value);
jgen.writeStartObject();
JavaType javaType = provider.constructType(CustomClass.class);
BeanDescription beanDesc = provider.getConfig().introspect(javaType);
JsonSerializer<Object> serializer = BeanSerializerFactory.instance.findBeanSerializer(provider,
javaType,
beanDesc);
// this is basically your 'writeAllFields()'-method:
serializer.unwrappingSerializer(null).serialize(value, jgen, provider);
jgen.writeObjectField("my_extra_field", "some data");
jgen.writeEndObject();
}
}
}
Run Code Online (Sandbox Code Playgroud)
另一个也许是最简单的解决方案:
使序列化过程分为两步。首先创建一个Map<String,Object>
类似:
Map<String,Object> map = req.mapper().convertValue( result, new TypeReference<Map<String,Object>>() {} );
Run Code Online (Sandbox Code Playgroud)
然后添加您想要的属性,例如:
map.put( "custom", "value" );
Run Code Online (Sandbox Code Playgroud)
然后将其序列化为 json:
String json = req.mapper().writeValueAsString( map );
Run Code Online (Sandbox Code Playgroud)
Ale*_*yda -2
在查看了更多JacksonBeanSerializer
源代码之后,我得出的结论是,如果不编写自己的代码,就根本不可能实现,BeanSerializerBuilder
并BeanSerializerFactory
提供一些扩展点,例如:
/*
/**********************************************************
/* Extension points
/**********************************************************
*/
protected void beforeEndObject(T bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JSONException {
// May be overridden
}
protected void afterStartObject(T bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JSONException {
// May be overridden
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,我不得不复制并粘贴整个Jackson的BeanSerializer
源代码,MyCustomBeanSerializer
因为前者不是为将所有字段和一些重要方法(例如serialize(...)
)声明为扩展而开发的final
归档时间: |
|
查看次数: |
48163 次 |
最近记录: |