我正在尝试序列化类似于以下内容的对象:
class Header {
public String title;
public String author;
}
class Document {
public String data;
public Header header = new Header();
}
没有任何注释,杰克逊将序列化为:
{"data":null,"header":{"title":null,"author":null}}
在@JsonInclude(NOT_NULL)
和@JsonInclude(NOT_EMPTY)
上使用Header
或Document
,将序列化为:
{"header":{}}
我想压缩header
属性,如果它是空的,但这只支持集合和字符串。理想情况下,这可以通过注释来解决,因为默认BeanSerializer
可以轻松实现这一点。
我可以通过编写自定义序列化程序来实现我的需求,但是我失去了默认序列化程序中所有高级逻辑的好处。
有人能想出更好的解决方法吗?上面的结构只是一个例子,因为我正在寻找一个通用的解决方案。
答案 0 :(得分:1)
它并不是那么简单 - 基本上,bean序列化程序必须暂停写入属性的字段名称,直到属性序列化程序写入某些东西 - 一旦你有了这些东西,它就会变得特别毛茸茸几个这样的嵌套在一起。
最简单的解决方法是让属性序列化程序将其值序列化为令牌缓冲区,然后如果令牌缓冲区仅包含" {"和"}"。但这意味着对象图最终会被读入和读出每个级别的缓冲区,从而使得流式生成器无法构建与生成的输出大小成比例的内容。 / p>
如果您可以使用缓冲区复制,这将大致符合您的要求:
@Test
public void suppress_empty_objects() throws Exception {
mapper.registerModule(new SimpleModule() {
@Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new SuppressEmptyBean());
}
class SuppressEmptyBean extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
// TODO: examine bean description for annotations to enable/disable suppression
ListIterator<BeanPropertyWriter> iter = beanProperties.listIterator();
while (iter.hasNext()) {
BeanPropertyWriter beanPropertyWriter = iter.next();
// TODO: only relevant to suppress properties that are themselves beans
iter.set(new SuppressEmptyPropertyWriter(beanPropertyWriter));
}
return beanProperties;
}
}
class SuppressEmptyPropertyWriter extends BeanPropertyWriter {
private final BeanPropertyWriter underlying;
SuppressEmptyPropertyWriter(BeanPropertyWriter underlying) {
super(underlying);
this.underlying = underlying;
}
@Override
public void serializeAsField(Object bean, JsonGenerator output, SerializerProvider prov)
throws Exception {
TokenBuffer tokenBuffer = new TokenBuffer(output.getCodec(), false);
underlying.serializeAsField(bean, tokenBuffer, prov);
if (!suppress(tokenBuffer, output)) {
tokenBuffer.serialize(output);
}
}
private boolean suppress(TokenBuffer tokenBuffer, JsonGenerator output) throws JsonParseException,
IOException {
if (tokenBuffer.firstToken() != JsonToken.FIELD_NAME) return false; // nope
JsonParser bufferParser = tokenBuffer.asParser();
bufferParser.nextToken(); // on field name
JsonToken valueToken1 = bufferParser.nextToken(); // on start object
if (valueToken1 != JsonToken.START_OBJECT) return false;
JsonToken valueToken2 = bufferParser.nextToken(); // on first thing inside object
return valueToken2 == JsonToken.END_OBJECT;
}
}
});
Document document = new Document();
document.data = "test";
assertThat(mapper.writeValueAsString(document), equivalentTo("{ data: 'test' }"));
document.header = new Header();
assertThat(mapper.writeValueAsString(document), equivalentTo("{ data: 'test' }"));
document.header.title = "the title";
assertThat(mapper.writeValueAsString(document),
equivalentTo("{ data: 'test', header: { title: 'the title' } }"));
}