Сериализация в JSON

Доброго времени суток, подскажите, как правильно сериализовать несколько объектов в единый конфиг?

Имею некоторые класы
JSONCaseDrop:
public class JSONCaseDrop implements JsonSerializer<CaseDrop>, JsonDeserializer<CaseDrop> {

    public static final Type TYPE = new TypeToken(){}.getType();

    @Override
    public CaseDrop deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException {
        JsonObject object = jsonElement.getAsJsonObject();
        CaseDrop caseDrop = new CaseDrop(new ItemStack(Items.carrot), 1);
        if(object.has("item")) {
            caseDrop.setItemStack((ItemStack) context.deserialize(object.get("item"), JSONItemStack.TYPE));
        }
        if(object.has("chance")) {
            caseDrop.setChance(object.get("chance").getAsFloat());
        }
        return caseDrop;
    }

    @Override
    public JsonElement serialize(CaseDrop caseDrop, Type type, JsonSerializationContext context) {
        JsonObject object = new JsonObject();
        object.add("item", context.serialize(caseDrop.getItemStack()));
        object.addProperty("chance", caseDrop.getChance());
        return object;
    }
}

JSONItemStack:
public class JSONItemStack implements JsonSerializer<ItemStack>, JsonDeserializer<ItemStack> {

    public static final Type TYPE = new TypeToken() {}.getType();

    @Override
    public ItemStack deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        JsonObject object = jsonElement.getAsJsonObject();
        GameRegistry.UniqueIdentifier id = new GameRegistry.UniqueIdentifier(object.get("item").getAsString());
        ItemStack is = GameRegistry.findItemStack(id.modId,id.name, object.has("stacksize") ? object.get("stacksize").getAsInt() : 1);
        if(is == null) {
            String el = jsonElement.toString();
            LogManager.getLogManager().getLogger("MineCraft").log(Level.SEVERE, "Invalid item: " + el);
            ItemStack item = new ItemStack(Items.snowball);
            ItemStackHelper.addDisplayNameAndLores(item, "InvalidItem", new ArrayList<String>());
            return item;
        } else {
            if(object.has("damage")) {
                is.setItemDamage(object.get("damage").getAsInt());
            }
        }
        return is;
    }

    @Override
    public JsonElement serialize(ItemStack itemStack, Type type, JsonSerializationContext context) {
        JsonObject object = new JsonObject();
        GameRegistry.UniqueIdentifier id = GameRegistry.findUniqueIdentifierFor(itemStack.getItem());
        object.addProperty("item", id.toString());
        object.addProperty("stacksize", itemStack.stackSize);
        object.addProperty("damage", itemStack.getItemDamage());
        return object;
    }
}

CaseDrop:
public class CaseDrop {

    private ItemStack itemStack;
    private float chance;

    public CaseDrop(ItemStack itemStack, float chance) {
        this.itemStack = itemStack;
        this.chance = chance;
    }

    public void setChance(float chance) {
        this.chance = chance;
    }

    public void setItemStack(ItemStack itemStack) {
        this.itemStack = itemStack;
    }

    public float getChance() {
        return chance;
    }

    public ItemStack getItemStack() {
        return itemStack;
    }
}

Класс, который отвечает создание самого json файла, туда сразу заношу небольшой пример

ConfigHelper:
public class ConfigHelper {

    private File dir;
    private File cfg;
    public ConfigHelper(File dir, String name) {
        this.dir = dir;

        cfg = new File(this.dir, name + ".json");
        if(!cfg.exists()) {
            createFile(cfg);
        }
    }

    public void createFile(File file) {
        file.getParentFile().mkdir();
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        CaseGroup cases = getExample();
        try {
            FileUtils.writeStringToFile(cfg, DiverseThings.gson.toJson(cases));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public File getCfg() {
        return cfg;
    }

    public CaseGroup getExample() {
        CaseDrop caseDrop1 = new CaseDrop(new ItemStack(Items.diamond), 3.0F);
        CaseDrop caseDrop2 = new CaseDrop(new ItemStack(Items.diamond), 1.3F);
        CaseDrop caseDrop3 = new CaseDrop(new ItemStack(Items.diamond), 7.9F);
        List<CaseDrop> drops = new ArrayList<CaseDrop>();
        drops.add(caseDrop1);
        drops.add(caseDrop2);
        drops.add(caseDrop3);
        Case case1 = new Case(new ItemStack(DiverseThings.instance.thingsUtils.bagItem), drops);
        List<Case> cases = new ArrayList<Case>();
        cases.add(case1);
        CaseGroup caseGroup = new CaseGroup(cases);
        return caseGroup;
    }
}

Сам GsonBuilder
Java:
public static final Gson gson = new GsonBuilder()
            .setPrettyPrinting().registerTypeAdapter(ItemStack.class, new JSONItemStack())
            .registerTypeAdapter(CaseDrop.class, new JSONCaseDrop()).registerTypeAdapter(NBTBase.class, new JSONNBT()).create();

Ошибка при запуске

Ошибка =(:
java.lang.ExceptionInInitializerError
    at me.Sa1ZeR_.DiverseThings.DiverseThings.<clinit>(DiverseThings.java:50) ~[modid-1.0.jar:?]
    at java.lang.Class.forName0(Native Method) ~[?:1.8.0_101]
    at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_101]
    at cpw.mods.fml.common.FMLModContainer.constructMod(FMLModContainer.java:440) ~[forgeSrc-1.7.10-10.13.4.1614-1.7.10.jar:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
    at com.google.common.eventbus.EventSubscriber.handleEvent(EventSubscriber.java:74) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.SynchronizedEventSubscriber.handleEvent(SynchronizedEventSubscriber.java:47) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.EventBus.dispatch(EventBus.java:322) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:304) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.EventBus.post(EventBus.java:275) ~[guava-17.0.jar:?]
    at cpw.mods.fml.common.LoadController.sendEventToModContainer(LoadController.java:212) ~[forgeSrc-1.7.10-10.13.4.1614-1.7.10.jar:?]
    at cpw.mods.fml.common.LoadController.propogateStateMessage(LoadController.java:190) ~[forgeSrc-1.7.10-10.13.4.1614-1.7.10.jar:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
    at com.google.common.eventbus.EventSubscriber.handleEvent(EventSubscriber.java:74) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.SynchronizedEventSubscriber.handleEvent(SynchronizedEventSubscriber.java:47) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.EventBus.dispatch(EventBus.java:322) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:304) ~[guava-17.0.jar:?]
    at com.google.common.eventbus.EventBus.post(EventBus.java:275) ~[guava-17.0.jar:?]
    at cpw.mods.fml.common.LoadController.distributeStateMessage(LoadController.java:119) [LoadController.class:?]
    at cpw.mods.fml.common.Loader.loadMods(Loader.java:513) [Loader.class:?]
    at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:208) [FMLClientHandler.class:?]
    at net.minecraft.client.Minecraft.startGame(Minecraft.java:522) [Minecraft.class:?]
    at net.minecraft.client.Minecraft.run(Minecraft.java:942) [Minecraft.class:?]
    at net.minecraft.client.main.Main.main(Main.java:164) [Main.class:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
    at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) [launchwrapper-1.12.jar:?]
    at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.12.jar:?]
    at net.minecraftforge.gradle.GradleStartCommon.launch(Unknown Source) [start/:?]
    at GradleStart.main(Unknown Source) [start/:?]
Caused by: java.lang.RuntimeException: Missing type parameter.
    at com.google.gson.reflect.TypeToken.getSuperclassTypeParameter(TypeToken.java:84) ~[TypeToken.class:?]
    at com.google.gson.reflect.TypeToken.<init>(TypeToken.java:62) ~[TypeToken.class:?]
    at me.Sa1ZeR_.DiverseThings.utils.json.JSONItemStack$1.<init>(JSONItemStack.java:17) ~[JSONItemStack$1.class:?]
    at me.Sa1ZeR_.DiverseThings.utils.json.JSONItemStack.<clinit>(JSONItemStack.java:17) ~[JSONItemStack.class:?]
    ... 38 more
 

tox1cozZ

aka Agravaine
8,455
598
2,892
Зачем тебе эти кастомные десереализаторы?
Напиши свой класс который будет предметом с полями: айди, метадата, нбт. Сделай в нем метод toItemStack которы йбудет из этих полей создавать стак.
И читай такие вещи. Гсон легко такие классы сам читает и не надо мозги парить.
 
2,505
81
397
Зачем тебе эти кастомные десереализаторы?
Напиши свой класс который будет предметом с полями: айди, метадата, нбт. Сделай в нем метод toItemStack которы йбудет из этих полей создавать стак.
И читай такие вещи. Гсон легко такие классы сам читает и не надо мозги парить.
Gson не сможет распарсить нбт в приемлемый вид. Нужно писать свой TypeAdapter (кмк, он проще, чем сериализатор/десериализатор). А заодно для Item и ItemStack, так как там есть поля, которые не нужно сериализовать. А вот с CaseDrop gson прекрасно справится сам.
 

tox1cozZ

aka Agravaine
8,455
598
2,892
Зачем писать свой десериализатор для нбт если уже есть дефолтный майновский? Нбт просто строка, далее юзаем JsonToNBT класс при создании стака и вуаля

Зачем у себя в конфиге храть объекты айтемов и стаков? Я описал выше что хранить нужно. А когда нужен стак - создаём сразу новый из этих данных. Если уж планируешь где-то каждый тик создавать, то можно просто кешировать при необходимости
 
Последнее редактирование:
2,505
81
397
Зачем писать свой десериализатор для нбт если уже есть дефолтный майновский?
Потому что он говняный. Критерии говняности уже не помню, но точно помню, что выход мне не нравился.

Зачем у себя в конфиге храть объекты айтемов и станков?
Потому что это просто и удобно. И подозреваю, что у тебя при создании происходит ненужный парсинг строки в нбт, поиск айтема по строке и т.п..
 

tox1cozZ

aka Agravaine
8,455
598
2,892
Потому что он говняный
Может и говняный, зато работает с ванильными командами и прочим. А то потом будешь голову ломать, почему ванильный парсер не работает

И подозреваю, что у тебя при создании ненужный парсинг строки в нбт
Кеширование в помощь, если делаешь это каждый тик. Если нет - вообще пофиг
 
2,505
81
397
Может и говняный, зато работает с ванильными командами и прочим. А то потом будешь голову ломать, почему ванильный парсер не работает
Хз, не нужен был ванильный парсер. Раз уж очень хочется его поддерживать, то можно внутри TypeAdapter использовать ванильный алгоритм. Т.е. просто обертка алгоритма.

Кеширование в помощь, если делаешь это каждый тик. Если нет - вообще пофиг
А можно просто сделать три TypeAdapter и ни о чем не думать.
 
7,099
324
1,510
и ни о чем не думать
Сделать представление стака как дата-класс, который gson автоматически сериализует выглядит проще. Если писать на скалке, то можно даже сделать имплициты между представлением и оригинальным стаком и вообще не чувствовать разницы
 
Ребят, как убрать кракозябры при отправке сообщения? В проекте стоит UTF8, в build.gradle прописано:
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
Код на отправку сообщения:
String msg = drop.getWinMessage().replace("%player%", player.getDisplayName());
msg = msg.replaceAll("&", "\u00a7");
player.addChatMessage(new ChatComponentText(msg));
 
Gson не сможет распарсить нбт в приемлемый вид. Нужно писать свой TypeAdapter (кмк, он проще, чем сериализатор/десериализатор). А заодно для Item и ItemStack, так как там есть поля, которые не нужно сериализовать. А вот с CaseDrop gson прекрасно справится сам.
Для itemstack юзаю такое:
Java:
cases = new HashMap<String, Case>();
        JsonParser parser = new JsonParser();
        try {
            Object obj = parser.parse(new FileReader(cfg));
            JsonObject object = (JsonObject) obj;
            for(int i = 0; i < object.get("cases").getAsJsonArray().size(); i++) {
                String id = object.get("cases").getAsJsonArray().get(i).getAsJsonObject().get("id").getAsString();
                List<CaseDrop> drops = new ArrayList<CaseDrop>();
                JsonObject dropObject = object.get("cases").getAsJsonArray().get(i).getAsJsonObject();
                for(int j = 0; j < dropObject.get("drop").getAsJsonArray().size(); j++) {
                    String itemId = dropObject.get("drop").getAsJsonArray().get(j).getAsJsonObject().get("id").getAsString();
                    int amount = dropObject.get("drop").getAsJsonArray().get(j).getAsJsonObject().get("amount").getAsInt();
                    double chance = dropObject.get("drop").getAsJsonArray().get(j).getAsJsonObject().get("chance").getAsDouble();
                    CaseDrop drop = new CaseDrop(itemId, amount, chance);
                    if(dropObject.get("drop").getAsJsonArray().get(j).getAsJsonObject().has("winmessage")) {
                        String winMsg = dropObject.get("drop").getAsJsonArray().get(j).getAsJsonObject().get("winmessage").getAsString();
                        drop.setWinMessage(winMsg);
                    }
                    if(dropObject.get("drop").getAsJsonArray().get(j).getAsJsonObject().has("cmd")) {
                        String cmd = dropObject.get("drop").getAsJsonArray().get(j).getAsJsonObject().get("cmd").getAsString();
                        drop.setCommand(cmd);
                    }
                    drops.add(drop);
                }
                Case newcase = new Case(id, drops);
                cases.put(id, newcase);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
Если мне нужно заспавнить/выдать предмет, то создаю его через GameRegistery. Думаю, что такой же способ подойдет и для НБТ имени и описания предмета. Для более сложных, например, как в моде TinkerConstract, еще не придумал как сделать) Если есть варианты, тыкните в правильное русло =)
 
2,505
81
397
Эх.. А мог бы написать что-то вроде и все.

Java:
Type type = new TypeToken<HashMap<String, Case>>() {}.getType();
HashMap<String, Case> map = gson.fromJson(json, type);
 
Сверху