Как обойти сериализацию Gson, дающую разные результаты при использовании универсальных символов

Используйте команду xticks .

import matplotlib.pyplot as plt

t11 = ['00', '01', '02', '03', '04', '05', '10', '11', '12', '13', '14', '15',
       '20', '21', '22', '23', '24', '25', '30', '31', '32', '33', '34', '35',
       '40', '41', '42', '43', '44', '45', '50', '51', '52', '53', '54', '55']

t12 = [173, 135, 141, 148, 140, 149, 152, 178, 135, 96, 109, 164, 137, 152,
       172, 149, 93, 78, 116, 81, 149, 202, 172, 99, 134, 85, 104, 172, 177,
       150, 130, 131, 111, 99, 143, 194]


plt.bar(range(len(t12)), t12, align='center')
plt.xticks(range(len(t12)), t11, size='small')
plt.show()

1
задан GhostCat 18 March 2019 в 13:54
поделиться

3 ответа

Обычно реализации коллекции «берут» тип из объявления поля коллекции, а не из данного элемента в List / Set / и т. Д. Нам нужно написать собственный сериализатор, который для каждого элемента находит сериализатор и использует его. Простая реализация:

class TypeAwareListJsonSeserializer implements JsonSerializer<List<?>> {
    @Override
    public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {
        if (src == null) {
            return JsonNull.INSTANCE;
        }
        JsonArray array = new JsonArray();
        for (Object item : src) {
            JsonElement jsonElement = context.serialize(item, item.getClass());
            array.add(jsonElement);
        }
        return array;
    }
}

И вот как мы можем использовать это:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.annotations.JsonAdapter;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        List<BaseBean> children = Arrays.asList(new BaseBean(), new ChildBean(), new ChildBean2());
        BaseBeanHolder baseHolder = new BaseBeanHolder(children);
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .create();
        System.out.println(gson.toJson(baseHolder));
    }
}

class BaseBean {
    String baseField = "base";
}

class ChildBean extends BaseBean {
    String childField = "child";
}

class ChildBean2 extends BaseBean {
    int bean2Int = 356;
}

class BaseBeanHolder {

    @JsonAdapter(TypeAwareListJsonSeserializer.class)
    private List<? extends BaseBean> beans;

    // getters, setters, toString
}

Выше кода печатается:

{
  "beans": [
    {
      "baseField": "base"
    },
    {
      "childField": "child",
      "baseField": "base"
    },
    {
      "bean2Int": 356,
      "baseField": "base"
    }
  ]
}

РЕДАКТИРОВАТЬ

Во время сериализации мы теряем информацию о типе, которая потребуется в процессе десериализации. Я разработал информацию простого типа, которая будет храниться во время сериализации и использоваться при десериализации. Это может выглядеть следующим образом:

class TypeAwareListJsonAdapter implements JsonSerializer<List<?>>, JsonDeserializer<List<?>> {

    private final String typeProperty = "@type";

    @Override
    public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {
        if (src == null) {
            return JsonNull.INSTANCE;
        }
        JsonArray array = new JsonArray();
        for (Object item : src) {
            JsonObject jsonElement = (JsonObject) context.serialize(item, item.getClass());
            jsonElement.addProperty(typeProperty, item.getClass().getSimpleName());

            array.add(jsonElement);
        }
        return array;
    }

    @Override
    public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        final Type elementType = $Gson$Types.getCollectionElementType(typeOfT, List.class);

        if (json instanceof JsonArray) {
            final JsonArray array = (JsonArray) json;
            final int size = array.size();
            if (size == 0) {
                return Collections.emptyList();
            }

            final List<?> suites = new ArrayList<>(size);
            for (int i = 0; i < size; i++) {
                JsonObject jsonElement = (JsonObject) array.get(i);
                String simpleName = jsonElement.get(typeProperty).getAsString();
                suites.add(context.deserialize(jsonElement, getClass(simpleName, elementType)));
            }

            return suites;
        }

        return Collections.emptyList();
    }

    private Type getClass(String simpleName, Type defaultType) {
        try {
            // you can use mapping or something else...
            return Class.forName("com.model." + simpleName);
        } catch (ClassNotFoundException e) {
            return defaultType;
        }
    }
}

Самая большая проблема заключается в том, как сопоставить классы значениям JSON. Мы можем использовать простое имя класса или предоставить Map<String, Class> и использовать его. Теперь мы можем использовать его, как указано выше. Пример приложения печатается сейчас:

{
  "beans": [
    {
      "baseField": "base",
      "@type": "BaseBean"
    },
    {
      "childField": "child",
      "baseField": "base",
      "@type": "ChildBean"
    },
    {
      "bean2Int": 356,
      "baseField": "base",
      "@type": "ChildBean2"
    }
  ]
}
BaseBean{baseField='base'}
ChildBean{baseField='base', childField='child'}
ChildBean2{baseField='base', bean2Int=356}
0
ответ дан Michał Ziober 18 March 2019 в 13:54
поделиться

Еще одно дополнение: я только что подумал, что по крайней мере сериализация отлично работает с массивами , поэтому простым обходным решением было переделать держатель:

static class BaseBeanHolder {
    BaseBean[] beans;
    public BaseBeanHolder(BaseBean... beans) { this.beans = beans; }
}
0
ответ дан GhostCat 18 March 2019 в 13:54
поделиться

Gson построен с учетом «Я собираюсь быть использован для сериализации» и «Я собираюсь быть использован для десериализации».

Нет никакого способа определить из необработанного JSON, каков точный тип времени выполнения потомка из BaseBean.

Вы можете использовать RuntimeTypeAdapterFactory, как описано здесь , здесь - к сожалению, он не публикуется с базовым модулем Gson и не находится в Maven Central, как описано здесь . Это опубликует достаточно информации с JSON, что позволит Gson десериализовать его.

0
ответ дан Not a JD 18 March 2019 в 13:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: