我使用的杰克逊连载我的POJO域对象,以JSON表示开发一个REST接口为我的应用程序。 我想自定义序列化对于某些类型的附加属性添加到不存在的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();
}
或(甚至更好)以某种方式拦截前的系列化jgen.writeEndObject()
调用,如:
@Override void beforeEndObject(....) {
jgen.writeObjectField("my_extra_field", "some data");
}
我以为我可以延长BeanSerializer
并覆盖其serialize(..)
方法,但它的声明final
,也是我无法找到一个简单的方法来创建的新实例BeanSerializer
没有向它提供所有类型的元数据信息几乎复制了良好的部分杰克逊。 所以我已经放弃了这样做。
我的问题是 -如何定制杰克逊的系列化额外的东西添加到JSON输出特定的POJO没有引入太多的样板代码的重用,并尽可能默认杰克逊的行为。
Answer 1:
因为(我认为)杰克逊1.7,你可以用做BeanSerializerModifier
和扩展BeanSerializerBase
。 我曾与杰克逊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();
}
}
}
Answer 2:
杰克逊2.5引入的@JsonAppend
注释,其可用于序列化过程中添加的“虚拟”的性质。 它可与混入的功能可以用来避免修改原始POJO。
下面的示例将一个ApprovalState
序列化过程中属性:
@JsonAppend(
attrs = {
@JsonAppend.Attr(value = "ApprovalState")
}
)
public static class ApprovalMixin {}
注册用的混入ObjectMapper
:
mapper.addMixIn(POJO.class, ApprovalMixin.class);
使用ObjectWriter
序列化过程中设置的属性:
ObjectWriter writer = mapper.writerFor(POJO.class)
.withAttribute("ApprovalState", "Pending");
使用作家序列化将增加ApprovalState
场传至输出。
Answer 3:
你可以这样做(以前版本杰克逊2.6后没有工作,但这个工程与杰克逊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();
}
}
}
Answer 4:
尽管这个问题已经回答了,我发现,不需要特殊的杰克逊挂钩的另一种方式。
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));
}
输出:
{
"baseField" : "inner",
"extraField" : "outer"
}
对于写作的集合,你可以简单地使用一个观点:
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));
}
输出:
[ {
"baseField" : "1",
"extraField" : "hello"
}, {
"baseField" : "2",
"extraField" : "hello"
} ]
Answer 5:
对于我的使用情况下,我可以用一个更简单的方法。 在一个基类我有我所有的“杰克逊的POJO”我补充一下:
protected Map<String,Object> dynamicProperties = new HashMap<String,Object>();
...
public Object get(String name) {
return dynamicProperties.get(name);
}
// "any getter" needed for serialization
@JsonAnyGetter
public Map<String,Object> any() {
return dynamicProperties;
}
@JsonAnySetter
public void set(String name, Object value) {
dynamicProperties.put(name, value);
}
我现在可以反序列化POJO的,有下地干活和重新序列化witjout损失任何性能。 我还可以添加/更改非POJO属性:
// Pojo fields
person.setFirstName("Annna");
// Dynamic field
person.set("ex", "test");
(来自明白了Cowtowncoder )
Answer 6:
我们可以使用反射来得到你想要解析对象的所有领域。
@JsonSerialize(using=CustomSerializer.class)
class Test{
int id;
String name;
String hash;
}
在自定义序列,我们有这样的我们的序列化方法:
@Override
public void serialize(Test value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartObject();
Field[] fields = value.getClass().getDeclaredFields();
for (Field field : fields) {
try {
jgen.writeObjectField(field.getName(), field.get(value));
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
jgen.writeObjectField("extra_field", "whatever_value");
jgen.writeEndObject();
}
Answer 7:
另一种也许是最简单的解决方案:
让序列化2个步骤。 首先创建一个Map<String,Object>
这样的:
Map<String,Object> map = req.mapper().convertValue( result, new TypeReference<Map<String,Object>>() {} );
然后添加你要喜欢的属性:
map.put( "custom", "value" );
然后连载这JSON:
String json = req.mapper().writeValueAsString( map );
Answer 8:
从什么瓦伊达说灵感,写在这个要点 :
下面是如何在杰克逊1.9.12添加一个监听bean序列。 在这个例子中,listerner被认为是命令一个链,其接口是:
public interface BeanSerializerListener {
void postSerialization(Object value, JsonGenerator jgen) throws IOException;
}
MyBeanSerializer.java:
public class MyBeanSerializer extends BeanSerializerBase {
private final BeanSerializerListener serializerListener;
protected MyBeanSerializer(final BeanSerializerBase src, final BeanSerializerListener serializerListener) {
super(src);
this.serializerListener = serializerListener;
}
@Override
public void serialize(final Object bean, final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonGenerationException {
jgen.writeStartObject();
if (_propertyFilterId != null) {
serializeFieldsFiltered(bean, jgen, provider);
} else {
serializeFields(bean, jgen, provider);
}
serializerListener.postSerialization(bean, jgen);
jgen.writeEndObject();
}
}
MyBeanSerializerBuilder.java:
public class MyBeanSerializerBuilder extends BeanSerializerBuilder {
private final BeanSerializerListener serializerListener;
public MyBeanSerializerBuilder(final BasicBeanDescription beanDesc, final BeanSerializerListener serializerListener) {
super(beanDesc);
this.serializerListener = serializerListener;
}
@Override
public JsonSerializer<?> build() {
BeanSerializerBase src = (BeanSerializerBase) super.build();
return new MyBeanSerializer(src, serializerListener);
}
}
MyBeanSerializerFactory.java:
public class MyBeanSerializerFactory extends BeanSerializerFactory {
private final BeanSerializerListener serializerListener;
public MyBeanSerializerFactory(final BeanSerializerListener serializerListener) {
super(null);
this.serializerListener = serializerListener;
}
@Override
protected BeanSerializerBuilder constructBeanSerializerBuilder(final BasicBeanDescription beanDesc) {
return new MyBeanSerializerBuilder(beanDesc, serializerListener);
}
}
下面的最后一堂课了如何使用RestEasy的3.0.7提供它:
@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final MapperConfigurator mapperCfg;
public ObjectMapperProvider() {
mapperCfg = new MapperConfigurator(null, null);
mapperCfg.setAnnotationsToUse(new Annotations[]{Annotations.JACKSON, Annotations.JAXB});
mapperCfg.getConfiguredMapper().setSerializerFactory(serializerFactory);
}
@Override
public ObjectMapper getContext(final Class<?> type) {
return mapperCfg.getConfiguredMapper();
}
}
Answer 9:
我们可以扩展BeanSerializer
,但小动作。
首先,定义一个Java类包装器的POJO。
@JsonSerialize(using = MixinResultSerializer.class)
public class MixinResult {
private final Object origin;
private final Map<String, String> mixed = Maps.newHashMap();
@JsonCreator
public MixinResult(@JsonProperty("origin") Object origin) {
this.origin = origin;
}
public void add(String key, String value) {
this.mixed.put(key, value);
}
public Map<String, String> getMixed() {
return mixed;
}
public Object getOrigin() {
return origin;
}
}
然后,实现自定义serializer
。
public final class MixinResultSerializer extends BeanSerializer {
public MixinResultSerializer() {
super(SimpleType.construct(MixinResult.class), null, new BeanPropertyWriter[0], new BeanPropertyWriter[0]);
}
public MixinResultSerializer(BeanSerializerBase base) {
super(base);
}
@Override
protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (bean instanceof MixinResult) {
MixinResult mixin = (MixinResult) bean;
Object origin = mixin.getOrigin();
BeanSerializer serializer = (BeanSerializer) provider.findValueSerializer(SimpleType.construct(origin.getClass()));
new MixinResultSerializer(serializer).serializeFields(origin, gen, provider);
mixin.getMixed().entrySet()
.stream()
.filter(entry -> entry.getValue() != null)
.forEach((entry -> {
try {
gen.writeFieldName(entry.getKey());
gen.writeRawValue(entry.getValue());
} catch (IOException e) {
throw new RuntimeException(e);
}
}));
} else {
super.serializeFields(bean, gen, provider);
}
}
}
通过这种方式,我们可以处理,使用杰克逊标注产地对象序列化定制行为的情况。
Answer 10:
我需要这种能力为好; 在我的情况,以支持REST的服务领域扩展。 我结束了开发一个小框架来解决这个问题,它的开源,开源在github上 。 它也可以在Maven的中央存储库 。
它负责所有的工作。 简单地包裹在MorphedResult的POJO,然后随意添加或删除属性。 当系列化,MorphedResult包装消失,任何“改变”出现在序列化JSON对象。
MorphedResult<?> result = new MorphedResult<>(pojo);
result.addExpansionData("my_extra_field", "some data");
对于更多细节和例子见GitHub的页面。 一定要与杰克逊的对象映射到注册库“过滤器”,像这样:
ObjectMapper mapper = new ObjectMapper();
mapper.setFilters(new FilteredResultProvider());
Answer 11:
寻找更多关于杰克逊的源代码后,我的结论是,这是根本不可能实现的,而不写我自己的BeanSerializer
, 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
}
不幸的是,我不得不复制和粘贴的整个杰克逊 BeanSerializer
源代码MyCustomBeanSerializer
因为前者不进行功能扩展,宣布所有的领域和一些重要的方法开发(如serialize(...)
作为final
文章来源: Jackson: How to add custom property to the JSON without modifying the POJO