Страницы

Поиск по вопросам

суббота, 11 января 2020 г.

Сериализация в JSON как объект конкретного класса

#java #gson


Есть у меня объект такого класса

class A {
  List items;
}


Я хочу его сериализовать в JSON. Пишу

A obj;
........
gson.toJson(obj);


Проблема: в items могут хранится как объекты BaseClass, так и его наследников. И
Gson сериализует все поля этих объектов.

Вопрос: нельзя ли как-то указать Gson сериализовать только поля, относящиеся к BaseClass
и его предкам. А поля наследников не трогать?

P.S. Наследники участвуют в сериализации других классов
    


Ответы

Ответ 1



Определяем свои правила конвертации объектов Gson Gson позволяет разработчикам определять свои собственные правила для сериализации и десериализации объектов. Зарегистрировать их можно с помощью метода registerTypeAdapter(). Для класса A(): class A { List items; } Предположим, что BaseClass имеет одно поле: class BaseClass { String field; } тогда создадим класс CustomSerializer и имплиментируем там соответствующий интерфейс: public class CustomSerializer implements JsonSerializer { @Override public JsonElement serialize(A src, Type type, JsonSerializationContext context) { JsonArray items = new JsonArray(); for (BaseClass item : src.items) { if (item.getClass().getSimpleName().equals("BaseClass")) { JsonObject currentItem = new JsonObject(); currentItem.addProperty("field", item.field); items.add(currentItem); } } return items; } } Примечание: класс сериализатор будет содержать только объекты типа BaseClass, чтобы он содержал еще и его предков, нужно добавить дополнительное условие. После этого регистрируем наш кастомный сериализатор: GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(A.class, new CustomSerializer()); Gson gson = builder.create(); Теперь можно смело вызывать метод A obj; ........ gson.toJson(obj); Для дерериализации аналогично создаем класс CustomDeserializer и имплиментируем там соответствующий интерфейс. Сперва добавим конструкторы для классов A и BaseClass: class A { List items; A(ArrayList items) { this.items = items; } } class BaseClass { String field; BaseClass(String field) { this.field = field; } } Теперь создаем класс CustomDeserializer: public class CustomDeserializer implements JsonDeserializer { @Override public A deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonArray items = (JsonArray) json; A a = new A(new ArrayList<>()); for (JsonElement item : items) { JsonObject currentItem = new JsonObject(); BaseClass baseClass = new BaseClass(item.getAsString()); a.items.add(baseClass); } return a; } } и регистрируем его аналогичным образом: GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(A.class, new CustomDeserializer()); Gson gson = builder.create(); Внимание: Если используется одновременно и сериализатор, и десериализатор, тогда нужно создать один класс, в котором имплементировать оба интерфейса, т.к. последовательная регистрация разных классов методом registerTypeAdapter будет переписывать предыдущую регистрацию.

Ответ 2



Update Или еще более простой вариант. Реализуем сериализатор для любого List public class GenericListSerializer implements JsonSerializer { @Override public JsonElement serialize(List src, Type type, JsonSerializationContext context) { JsonArray res = new JsonArray(); if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType)type; Type[] args = paramType.getActualTypeArguments(); type = args[0]; } for (Object item : src) { JsonObject currentItem = (JsonObject)context.serialize(item, type); res.add(currentItem); } return res; } } А теперь помечаем поля специальной аннотацией class A { @JsonAdapter(GenericListSerializer.class) List items; } Все. теперь при сериализации поля items будет вызываться сериализатор GenericListSerializer Old version Благодаря пинку @Drakonoved получился такой адаптер public class CustomSerializer implements JsonSerializer
{ @Override public JsonElement serialize(A src, Type type, JsonSerializationContext context) { // Сериализуем поля родителя без изменений JsonObject res = (JsonObject)context.serialize(src, src.getClass().getSuperclass()); // Получаем свои поля Field[] flds = src.getClass().getDeclaredFields(); for (Field fld : flds) { Type fldType = fld.getGenericType(); JsonElement jField; Object fieldVal; try { // Получаем значение поля fieldVal = fld.get(src); } catch (IllegalAccessException e) { throw new RuntimeException(e); } // Если тип поля List и его наследники и это generic-тип if (fld.getType().isAssignableFrom(List.class) && fldType instanceof ParameterizedType) { // Получаем конкретные типы, которые были указаны в объявлении List<> ParameterizedType paramType = (ParameterizedType)fldType; Type[] args = paramType.getActualTypeArguments(); // формируем массив JsonArray jsonArray = new JsonArray(); for (Object item : (List)fieldVal) { // сериализуем элемент списка, как тип, который был указан при объявлении JsonObject currentItem = (JsonObject)context.serialize(item, args[0]); jsonArray.add(currentItem); } jField = jsonArray; } else { // Если у нас не список, то вызываем умолчательный сериализатор jField = context.serialize(fieldVal); } res.add(fld.getName(), jField); } return res; } }

Комментариев нет:

Отправить комментарий