Различные выходные данные JSON при использовании настраиваемого сериализатора json в Spring Data Rest

После добавления кастома Jackson Сериализатор, основанный на официальной документации, я наблюдал немного другой формат вывода json.

Этот пример основан на развилке пружинных перестановок.

простираться 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. Любая помощь приветствуется.

4 ответа

Решение

Это не ошибка Spring Data Rest, это на самом деле нормальное поведение сериализатора Джексона. Всякий раз, когда вы используете аннотацию @JsonUnwrapped (как это делает поле содержимого ресурса) вместе с настраиваемым сериализатором, сериализатор 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;
    }
}

Проекция является одним из решений и имеет приоритет над одним методом JsonSerializer Другой:

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

Тогда вы сможете опустить начало и конец объекта.

Найдите мой пост здесь.

Помимо вышеприведенного решения 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();
}

У меня такая же проблема. Вместо того чтобы использовать Jackson Serializer, я использовал @Projecton и настроил свой вывод. Вы можете найти ссылку здесь

Другие вопросы по тегам