Gson giving StackOverflowError

Discussion in 'Spigot Plugin Development' started by MinecraftKnightz, Jun 21, 2018.

  1. Hey guys. So I'm currently working on a minigame, and when trying to use GSON to serialize (and deserialize) my Generator object which just generates items, I am getting a StackOverflowError. I've Googled to no avail, so am looking for help here. The problem is likely a dumb move from me, not making a field transient or something (although I'm using a manual TypeAdapter which shouldn't be affected by transient fields AFAIK).

    This is the error (repeats a couple of hundred times to fill up most of the console)
    Code (Text):
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:375)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:380)
            at com.google.gson.internal.$Gson$Types.res
    [18:58:03 WARN]: olve($Gson$Types.java:375)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:380)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:375)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:380)
            at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:355)
            at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:117)
     
    I know how to read a stack-trace, and from what I can tell it's nothing to do with my code, the closest piece of info I could find after Googling was that there's some sort of recursive field dependencies. Originally I was serializing the contents of my Locations class (which I still intend to do, I simply changed it for debugging), which has a Set of all the Generators. Same error as only serializing one generator. Also, these are the only fields in my Generator class so I don't think it's recursive dependencies:

    Code (Java):
    private final GeneratorType generatorType;
        private final Location[] locations;
        private final Location root;

        //* For serialization purposes
        private final boolean withHologram;
        private transient final AtomicInteger timeUntilEvolve;
        private transient final Placeholder time;
        private transient final Placeholder level;
        private transient AtomicInteger previousUpgradeTimes;
        private transient long timer;
        private GeneratorTier tier;
        private transient ScheduledFuture dropFuture;
        private transient Hologram h;
    Finally, here's the contents of my GeneratorSerializer class (YES it has been registered in GsonBuilder before you ask :p):

    Code (Java):
    public class GeneratorSerializer extends TypeAdapter<Generator> {

        public GeneratorSerializer() {
            super();
        }

        @Override
        public void write(JsonWriter out, Generator value) throws IOException {
            out.beginObject();
            if (value.isWithHologram()) {
                out.name("hologram").value(value.getH().getId());
            }
            out.name("type").value(value.getGeneratorType().name());
            out.name("root").value(Bedwars.getGson().toJson(value.getRoot()));
    //        val locs = out.name("locations").beginArray();
    //        int x = 0;
    //        locs.name(String.valueOf(x)).value(Bedwars.getGson().toJson(value.getLocations()[x]));
    //        locs.endArray();
            out.endObject();
            out.flush();
            out.close();
        }

        @SuppressWarnings ("unchecked")
        @Override
        public Generator read(JsonReader in) throws IOException {
            in.beginObject();
            Hologram hologram = null;
            GeneratorType type = null;
            Location root = null;
            String nextName;
            do {
                nextName = in.nextString();
                switch (nextName.toLowerCase()) {
                    case "hologram":
                        String s = in.nextString();
                        hologram = Bedwars.getManager().getHologram(s);
                        break;
                    case "type":
                        type = GeneratorType.valueOf(in.nextString());
                        break;
                    case "root":
                        root = Bedwars.getGson().fromJson(in.nextString(), Location.class);
                        break;
                }
            } while (in.hasNext());
            in.endObject();
            in.close();
            Validate.notNull(type, "GeneratorType");
            Validate.notNull(root, "Root Location");
            if (hologram == null) {
                return new Generator(root.getBlock(), type, false);
            }
            return new Generator(root.getBlock(), type, true);
        }

    }
     

    As always, any help would be massively appreciated (as this is incredibly annoying issue), so thanks guys :D
     
  2. Choco

    Moderator

    As far as I'm aware, this is due to Location not being GSON-friendly in terms of serialization. Keep in mind that Location holds a reference to World and GSON serializes all non-transient non-static fields... including the World. That's a lot of data. You're going to want to write a type adapter for World to serialize it as a name and deserialize it as a World from a name (Bukkit#getWorld(String)). See JsonSerializer<World> and JsonDeserializer<World>
     
    • Useful Useful x 1
  3. @2008Choco You are a life-saver - thanks a lot!
     
    • Like Like x 1