问题 在Spring Data Rest中使用自定义json序列化程序时输出不同的JSON


添加自定义后 Jackson 基于的序列化器 官方文件 我观察到一种略有不同的json输出格式。

这个例子是基于fork的 弹簧restbucks

延伸 org.springsource.restbucks.WebConfiguration 从 RepositoryRestMvcConfiguration 和覆盖 configureJacksonObjectMapper

@Override
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {
    final SimpleSerializers serializers = new SimpleSerializers();
    serializers.addSerializer(Order.class, new OrderSerializer());
    objectMapper.registerModule(new SimpleModule("CustomSerializerModule"){
        @Override public void setupModule(SetupContext context) {
            context.addSerializers(serializers);
        }
    });
}

创建类 org.springsource.restbucks.order.OrderSerializer。为了简洁起见,只需写入属性 paid 作为JSON。

public class OrderSerializer extends JsonSerializer<Order> {
    @Override
    public void serialize(Order value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        jgen.writeStartObject();
        jgen.writeBooleanField("paid", value.isPaid());
        jgen.writeEndObject();
    }
}

在添加OrderSerializer json响应之前 http://localhost:8080/orders/1 好像:

{
  "location": "TAKE_AWAY",
  "status": "PAYMENT_EXPECTED",
  "orderedDate": "2014-03-24T15:05:09.988+01:00",
  "items": [
    {
      "name": "Java Chip",
      "quantity": 1,
      "milk": "SEMI",
      "size": "LARGE",
      "price": {
        "currency": "EUR",
        "value": 4.2
      }
    }
  ],
  "_links": {
    ...
  }
}

添加OrderSerializer json响应后 http://localhost:8080/orders/1 好像

{
  "content": {
    "paid": false
  },
  "_links": {
    ...
  }
}

主要的观点是,付费属性被包装到另一个对象内容中,该内容是属性 org.springframework.hateoas.Resource。我期望没有这个属性的响应:

{
  "paid": false,  
  "_links": {
    ...
  }
}

我查看了杰克逊代码并发现了这一点 UnwrappingBeanSerializer 可能是我正在寻找的解决方案。 在查看如何初始化UnwrappingBeanSerializer之后,我认为这个序列化程序并不是为了自定义使用而进行子类化。

我想知道在使用自定义序列化程序时这种偏离json格式是正常行为还是Spring Data Rest中的错误。任何形式的帮助表示赞赏。


11818
2018-03-24 15:05


起源



答案:


这不是Spring Data Rest的错误,它实际上是Jackson Serializer的正常行为。每当您使用@JsonUnwrapped Annotation(与资源内容字段一起使用)和自定义序列化程序时,Jackson Serializer将显式写入字段名称(在本例中为内容)。有关更多详细信息,请查看UnwrappingBeanPropertyWriter。无论如何,你使用UnwrappingBeanSerializer一直在正确的轨道上,但设置与通常的Serializer注册略有不同。以下示例应该可以解决您的问题:

@Override
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {
    mapper.registerModule(new Module() {
        @Override
        public String getModuleName() {
            return "my.module";
        }

        @Override
        public Version version() {
            return Version.unknownVersion();
        }

        @Override
        public void setupModule(SetupContext context) {

            context.addBeanSerializerModifier(new BeanSerializerModifier() {
                @Override
                public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
                    if(beanDesc.getBeanClass().equals(Order.class)) {
                        return new UnwrappingOrderSerializer((BeanSerializerBase) serializer, NameTransformer.NOP);
                    }
                    return serializer;
                }
            });
        }
    });
}

public class UnwrappingOrderSerializer extends UnwrappingBeanSerializer {
    public UnwrappingBarSerializer(BeanSerializerBase src, NameTransformer transformer) {
        super(src, transformer);
    }

    @Override
    public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
        return new UnwrappingOrderSerializer(this, transformer);
    }

    @Override
    protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
        Order order = (Order) bean;
        jgen.writeStringField("paid", order.isPaid();
    }

    @Override
    public boolean isUnwrappingSerializer() {
        return true;
    }
}

7
2018-03-14 10:40



感谢您的回答。它按预期工作。您的示例中存在编译错误 jgen.writeStringField("paid", order.isPaid();。你错过了右括号。 - Kamill Sokol
这太棒了!谢谢你:) - Tometoyou
我收到一个错误:```引起:org.springframework.beans.factory.UnsatisfiedDependencyException:创建名为'com.in.e.i18n.TranslationSerializer'的bean时出错:通过构造函数参数0表示不满意的依赖;嵌套异常是org.springframework.beans.factory.NoSuchBeanDefinitionException:没有'com.fasterxml.jackson.databind.ser.std.BeanSerializerBase'类型的限定bean可用:预期至少有1个bean可以作为autowire候选者。依赖注释:{}```任何想法? - Bruno Leite


答案:


这不是Spring Data Rest的错误,它实际上是Jackson Serializer的正常行为。每当您使用@JsonUnwrapped Annotation(与资源内容字段一起使用)和自定义序列化程序时,Jackson Serializer将显式写入字段名称(在本例中为内容)。有关更多详细信息,请查看UnwrappingBeanPropertyWriter。无论如何,你使用UnwrappingBeanSerializer一直在正确的轨道上,但设置与通常的Serializer注册略有不同。以下示例应该可以解决您的问题:

@Override
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {
    mapper.registerModule(new Module() {
        @Override
        public String getModuleName() {
            return "my.module";
        }

        @Override
        public Version version() {
            return Version.unknownVersion();
        }

        @Override
        public void setupModule(SetupContext context) {

            context.addBeanSerializerModifier(new BeanSerializerModifier() {
                @Override
                public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
                    if(beanDesc.getBeanClass().equals(Order.class)) {
                        return new UnwrappingOrderSerializer((BeanSerializerBase) serializer, NameTransformer.NOP);
                    }
                    return serializer;
                }
            });
        }
    });
}

public class UnwrappingOrderSerializer extends UnwrappingBeanSerializer {
    public UnwrappingBarSerializer(BeanSerializerBase src, NameTransformer transformer) {
        super(src, transformer);
    }

    @Override
    public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
        return new UnwrappingOrderSerializer(this, transformer);
    }

    @Override
    protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
        Order order = (Order) bean;
        jgen.writeStringField("paid", order.isPaid();
    }

    @Override
    public boolean isUnwrappingSerializer() {
        return true;
    }
}

7
2018-03-14 10:40



感谢您的回答。它按预期工作。您的示例中存在编译错误 jgen.writeStringField("paid", order.isPaid();。你错过了右括号。 - Kamill Sokol
这太棒了!谢谢你:) - Tometoyou
我收到一个错误:```引起:org.springframework.beans.factory.UnsatisfiedDependencyException:创建名为'com.in.e.i18n.TranslationSerializer'的bean时出错:通过构造函数参数0表示不满意的依赖;嵌套异常是org.springframework.beans.factory.NoSuchBeanDefinitionException:没有'com.fasterxml.jackson.databind.ser.std.BeanSerializerBase'类型的限定bean可用:预期至少有1个bean可以作为autowire候选者。依赖注释:{}```任何想法? - Bruno Leite


投影是一种解决方案,并且超越了一种方法 JsonSerializer 是另一个:

    @Override
    public boolean isUnwrappingSerializer() {
        return true;
    }

然后你应该能够省略对象的开始和结束。

找到我的博客文章 这里


2
2017-12-22 12:41





除了上面的andreast00解决方案 - 确保也覆盖其他构造函数和...方法,或者它可能在后台创建默认的UnwrappingBeanSerializer并忽略您的自定义序列化代码:

public UnwrappingOrderSerializer(UnwrappingBeanSerializer src, ObjectIdWriter objectIdWriter) {
    super(src, objectIdWriter);
}

public UnwrappingOrderSerializer(UnwrappingBeanSerializer src, ObjectIdWriter objectIdWriter, Object filterId) {
    super(src, objectIdWriter, filterId);
}

public UnwrappingOrderSerializer(UnwrappingBeanSerializer src, Set<String> toIgnore) {
    super(src, toIgnore);
}



@Override
public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) {
    return new UnwrappingOrderSerializer(this, objectIdWriter);
}

@Override
public BeanSerializerBase withFilterId(Object filterId) {
    return new UnwrappingOrderSerializer(this, this._objectIdWriter, filterId);
}

@Override
protected BeanSerializerBase withIgnorals(Set<String> toIgnore) {
    return new UnwrappingOrderSerializer(this, toIgnore);
}

此外,根据序列化是从@JsonUnwrapped对象发生,作为数组中的对象还是作为单个对象,jsonGenerater.writeStartObject可能需要在天气对象已经启动或未启动时进行调用。我用了:

boolean writeStartEnd = !jsonGenerator.getOutputContext().inObject() 
    || jsonGenerator.getOutputContext().getCurrentName() != null;

if (writeStartEnd) jsonGenerator.writeStartObject(entity);

...serialisation code...

if (writeStartEnd) {    
    jsonGenerator.writeEndObject();
}

1
2017-10-12 06:48



你有一些内容。只需更改措辞,例如:“超出其他答案”......例如。否则人们很快就会将这标志为“不是答案”并且可能会对此进行投票。使用答案 答案! - GhostCat


我有同样的问题。我没有使用Jackson Serializer,而是使用了@Projecton并定制了我的输出。你可以找到一个参考 这里 


0
2018-01-03 09:57