Spring Framework JSON Сериализация в массив, а не объект

1

В моем веб-приложении, использующем Spring, мы хотим использовать пользовательскую структуру JSON. Spring по умолчанию принимает POJO следующим образом:

public class Model {

    private int id;
    private String name;

    public Model(){}

    public Model(int id, String name){
        this.id = id;
        this.name = name;
    }
}

и превращает его в это:

{"id":1, "name":"Bob"}

В нашем приложении мы хотим превратить его в это:

[1, "Bob"]

Я хочу использовать логику сериализации Spring по умолчанию, которая определяет тип Java (int, String, Collection и т.д.) И сопоставляет соответствующий тип JSON, но просто меняйте объект обтекания на массив, а не на объект с полями.

Это сериализатор, который у меня есть до сих пор (который будет реализован в модели с помощью @JsonSerialize (с использованием = Serializer.class)), но предпочел бы не переписывать всю логику Spring, которая уже реализована.

public class Serializer extends JsonSerializer<Model> {
    @Override
    public void serialize(Model value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonProcessingException {

        jgen.writeStartArray();
        jgen.writeString(value.id);
        .... other values ...
        jgen.writeEndArray();

    }
}

Как я могу подключиться к уже существующему Сериализатору, чтобы этот новый сериализатор работал с любым POJO по умолчанию (а не только с классом Model, но с любым подобным или дочерним классом нам нужно сериализовать массив)? Это может иметь смешанные свойства и никакого специального соглашения об именах для свойств.

Я хочу, чтобы избежать написания пользовательского сериализатора для каждого другого класса Model (... других значений...).

  • 1
    Сериализатор - правильный способ сделать это.
  • 0
    О да. Мне нужно уточнить, так как я хочу, чтобы сериализатор не был специфичным для одного класса Model, но работал с любым POJO и динамически создавал массив соответствующим образом. Спасибо!
Показать ещё 7 комментариев
Теги:
spring
spring-mvc
serialization

2 ответа

0
Лучший ответ

Взгляните на библиотеку Apache BeanUtils, в частности, обратите внимание на метод BeanUtils.populate().

То, что этот метод делает, - это преобразовать любой данный Object в Map<String, Object>, на основе соглашений JavaBeans. В ключах у вас будут имена атрибутов, тогда как в значениях вы должны иметь каждое значение атрибута. Для стандартных случаев этого метода должно быть достаточно. Внимательно прочитайте документацию, чтобы проверить, как обращаться с особыми случаями.

Model model = ...; // get your model from some place

Map<String, Object> properties = new HashMap<>();

BeanUtils.populate(model, properties);

// Exception handling and special cases left as an excercise

Вышеуказанная рекурсивно заполняет карту properties, а это означает, что если ваша Model имеет атрибут otherModel, тип которого является OtherModel, тогда карта properties будет иметь другую карту в записи, которая соответствует otherModel, и так далее для других вложенных POJO.

Когда у вас есть карта properties, то, что вы хотите сериализовать, поскольку элементы вашего массива будут в своих значениях. Итак, что-то вроде этого должно выполнить эту работу:

public List<Object> toArray(Map<String, Object> properties) {
    List<Object> result = new ArrayList<>();

    for (Object obj : properties.values()) {
        Object elem = null;
        if (obj != null) {
            Class<?> clz = obj.getClass();
            if (Map.class.isAssignableFrom(clz)) {
                elem = toArray((Map<String, Object>) obj); // recursion!
            } else {
                elem = obj;
            }
        }
        result.add(elem); // this adds null values
                          // move 1 line up if you don't
                          // want to serialize nulls
    }
    return result;
}

Затем, после вызова метода toArray(), у вас будет List<Object> готовый к сериализации, используя стандартные механизмы Spring. Я даже считаю, что вам не понадобится определенный сериализатор:

List<Object> array = toArray(properties);
return array; // return this array, i.e. from a Controller 

Отказ от ответственности:

Пожалуйста, используйте это как руководство, а не как окончательное решение. Я старался быть как можно более осторожным, но у кода могут быть ошибки. Я вполне уверен, что для обработки массивов и Iterable POJO требуется специальная обработка. Это, несомненно, отсутствие обработки исключений. Он работает только для POJO. Он может взорваться, если поставляемый объект имеет круглые ссылки. Это не проверено!

0

Для этого вы можете использовать аннотацию @JsonValue. Пример:

public class Model {

  private int id;

  public Model(){}

  public Model(int id){
    this.id = id;
  }

  @JsonValue
  public int[] getValue() {
    return new int[]{this.id};
  } 
}
  • 0
    Это будет работать для одного конкретного экземпляра. Я обновил вопрос, чтобы уточнить, что я ищу более общее решение. Спасибо!
  • 0
    В таком случае я бы использовал смешанные аннотации ( wiki.fasterxml.com/JacksonMixInAnnotations ). Вы можете создать интерфейс (или абстрактный класс) с методом value, аннотированным @JsonValue и реализовать этот смешанный интерфейс во всех ваших классах. Более того, вы можете просто указать этот миксин в конфигурации ObjectMapper и фактически не изменять свои собственные классы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню