Java 如何使用Jackson JSON注释来抑制空对象?
我正在尝试序列化类似以下内容的对象:Java 如何使用Jackson JSON注释来抑制空对象?,java,json,jackson,Java,Json,Jackson,我正在尝试序列化类似以下内容的对象: class Header { public String title; public String author; } class Document { public String data; public Header header = new Header(); } 如果没有任何注释,jackson将序列化为: {"data":null,"header":{"title":null,"author":null}} {"
class Header {
public String title;
public String author;
}
class Document {
public String data;
public Header header = new Header();
}
如果没有任何注释,jackson将序列化为:
{"data":null,"header":{"title":null,"author":null}}
{"header":{}}
使用@JsonInclude(非空)
或@JsonInclude(非空)
标题和文档上的@JsonInclude(非空)
,它将序列化为:
{"data":null,"header":{"title":null,"author":null}}
{"header":{}}
如果header
属性为空,我想将其取消,但这仅支持集合和字符串。理想情况下,这可以通过注释来解决,因为默认的BeanSerializer
可以轻松实现这一点
我可以通过编写自定义序列化程序来实现所需的功能,但这样我就失去了默认序列化程序中所有高级逻辑的好处
有人能想出更好的办法来解决这个问题吗?上面的结构只是一个例子,因为我正在寻找一个通用的解决方案。其实并不那么容易——基本上,bean序列化程序将不得不推迟编写属性的字段名,直到属性序列化程序编写了某些内容——一旦有多个这样的字段相互嵌套,这可能会变得特别棘手
解决此问题的最简单方法是,属性序列化程序将其值序列化为令牌缓冲区,然后在令牌缓冲区仅包含“{”和“}”时跳过该属性。但这意味着对象的图形最终会在每一级的缓冲区中被读入或读出,这就破坏了流生成器的功能,因为流生成器不能生成与生成的输出大小成比例的内容
如果您可以接受缓冲区复制,这将大致满足您的要求:
@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' } }"));
}
@测试
public void suppress\u empty\u objects()引发异常{
registerModule(新的SimpleModule(){
@凌驾
公共无效设置模块(设置上下文上下文){
addBeanSerializerModifier(新的SuppressEmptyBean());
}
类SuppressEmptyBean扩展BeanSerializerModifier{
@凌驾
公共列表changeProperties(SerializationConfig配置,
BEAN说明beanDesc,
列表(不动产){
//TODO:检查bean描述中的注释以启用/禁用抑制
ListIterator iter=beanProperties.ListIterator();
while(iter.hasNext()){
BeanPropertyWriter BeanPropertyWriter=iter.next();
//TODO:仅与抑制本身是bean的属性相关
iter.set(新的SuppressEmptyPropertyWriter(beanPropertyWriter));
}
返回Bean属性;
}
}
类SuppressEmptyPropertyWriter扩展BeanPropertyWriter{
私有最终BeanPropertyWriter基础;
SuppressEmptyPropertyWriter(BeanPropertyWriter基础){
超级(基础);
这个。潜在的=潜在的;
}
@凌驾
public void serializeAsField(对象bean、JsonGenerator输出、SerializerProvider prov)
抛出异常{
TokenBuffer TokenBuffer=新的TokenBuffer(output.getCodec(),false);
serializeAsField(bean、tokenBuffer、prov);
如果(!抑制(令牌缓冲区,输出)){
序列化(输出);
}
}
私有布尔抑制(TokenBuffer TokenBuffer,JsonGenerator输出)抛出JsonParseException,
IOException{
if(tokenBuffer.firstToken()!=JsonToken.FIELD\u NAME)返回false;//否
JsonParser bufferParser=tokenBuffer.aspaser();
bufferParser.nextToken();//关于字段名
JsonToken valueToken1=bufferParser.nextToken();//在开始对象上
if(valueToken1!=JsonToken.START\u对象)返回false;
JsonToken valueToken2=bufferParser.nextToken();//在对象内部的第一件事上
返回值token2==JsonToken.END_对象;
}
}
});
文档=新文档();
document.data=“测试”;
assertThat(mapper.writeValueAsString(document),等价于(“{data:'test'}”);
document.header=新标题();
assertThat(mapper.writeValueAsString(document),等价于(“{data:'test'}”);
document.header.title=“标题”;
资产(mapper.writeValueAsString(文档),
等价于(“{data:'test',header:{title:'thetitle'}”);
}
如果header=null没有值,您是否尝试过将其设置为null?是的,这是我当前的解决方案,但在填充时,它会使服务器代码充满恼人的null检查。类似的解决方案是使用@JsonIgnore标记头,并添加一个getter,该getter仅在头为空时返回非null头。