13

Jackson根据官方文档添加自定义序列化程序后,我观察到 json 输出格式略有不同。

此示例基于spring-restbucks

扩展org.springsource.restbucks.WebConfigurationRepositoryRestMvcConfiguration覆盖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": {
    ...
  }
}

主要问题是属性paid 被包装到另一个对象内容中,该对象内容是org.springframework.hateoas.Resource的一个属性。我期望没有此属性的响应:

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

我查看了 Jackson 代码,发现UnwrappingBeanSerializer可能是我正在寻找的解决方案。在查看了如何初始化 UnwrappingBeanSerializer 之后,我认为这个序列化器不应该被子类化以供自定义使用。

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

4

4 回答 4

10

这不是 Spring Data Rest 的错误,它实际上是 Jackson Serializer 的正常行为。每当您将 @JsonUnwrapped 注释(如 Resource 内容字段所做的那样)与自定义序列化器一起使用时,Jackson 序列化器将显式写入字段名称(在本例中为内容)。查看 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;
    }
}
于 2015-03-14T10:40:31.430 回答
3

投影是一种解决方案,而覆盖一种方法JsonSerializer是另一种方法:

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

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

在这里找到我的博客文章。

于 2017-12-22T12:41:08.353 回答
2

除了上面 andreast00 的解决方案 - 确保还覆盖其他构造函数和 with... 方法,否则它可能会在后台创建默认的 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();
}
于 2017-10-12T06:48:58.743 回答
0

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

于 2015-01-03T09:57:27.983 回答