From 218010e910bc0a93af11a07ee0b6ff4bc056951b Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Thu, 9 Sep 2021 23:31:54 +0200 Subject: [PATCH] Added Json util file to avoid multiple identical instances of Gson object --- .../fr/pandacube/lib/core/db/SQLElement.java | 4 +- .../java/fr/pandacube/lib/core/util/Json.java | 104 ++++++++++++++++++ .../lib/core/util/ServerPropertyFile.java | 8 +- .../lib/core/util/TypeConverter.java | 5 +- 4 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 Core/src/main/java/fr/pandacube/lib/core/util/Json.java diff --git a/Core/src/main/java/fr/pandacube/lib/core/db/SQLElement.java b/Core/src/main/java/fr/pandacube/lib/core/db/SQLElement.java index 7305104..3ecb54a 100644 --- a/Core/src/main/java/fr/pandacube/lib/core/db/SQLElement.java +++ b/Core/src/main/java/fr/pandacube/lib/core/db/SQLElement.java @@ -19,10 +19,10 @@ import java.util.UUID; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; -import com.google.gson.Gson; import com.google.gson.JsonObject; import fr.pandacube.lib.core.util.EnumUtil; +import fr.pandacube.lib.core.util.Json; import fr.pandacube.lib.core.util.Log; public abstract class SQLElement> { @@ -398,7 +398,7 @@ public abstract class SQLElement> { public JsonObject asJsonObject() { JsonObject json = new JsonObject(); for (SQLField f : getFields().values()) { - json.add(f.getName(), new Gson().toJsonTree(get(f))); + json.add(f.getName(), Json.gson.toJsonTree(get(f))); } return json; } diff --git a/Core/src/main/java/fr/pandacube/lib/core/util/Json.java b/Core/src/main/java/fr/pandacube/lib/core/util/Json.java new file mode 100644 index 0000000..1cef161 --- /dev/null +++ b/Core/src/main/java/fr/pandacube/lib/core/util/Json.java @@ -0,0 +1,104 @@ +package fr.pandacube.lib.core.util; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.RecordComponent; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class Json { + public static final Gson gson = build(b -> b); + public static final Gson gsonPrettyPrinting = build(b -> b.setPrettyPrinting()); + public static final Gson gsonSerializeNulls = build(b -> b.serializeNulls()); + public static final Gson gsonSerializeNullsPrettyPrinting = build(b -> b.serializeNulls().setPrettyPrinting()); + + + private static Gson build(Function builderModifier) { + return builderModifier + .apply(new GsonBuilder().registerTypeAdapterFactory(new RecordAdapterFactory()).setLenient()).create(); + } + + + + + // from https://github.com/google/gson/issues/1794#issuecomment-812964421 + private static class RecordAdapterFactory implements TypeAdapterFactory { + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + @SuppressWarnings("unchecked") + Class clazz = (Class) type.getRawType(); + if (!clazz.isRecord() || clazz == Record.class) { + return null; + } + return new RecordTypeAdapter<>(gson, this, type); + } + } + + private static class RecordTypeAdapter extends TypeAdapter { + private Gson gson; + private TypeAdapterFactory factory; + private TypeToken type; + + public RecordTypeAdapter(Gson gson, TypeAdapterFactory factory, TypeToken type) { + this.gson = gson; + this.factory = factory; + this.type = type; + } + + @Override + public void write(JsonWriter out, T value) throws IOException { + gson.getDelegateAdapter(factory, type).write(out, value); + } + + @Override + public T read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } else { + @SuppressWarnings("unchecked") + Class clazz = (Class) type.getRawType(); + + RecordComponent[] recordComponents = clazz.getRecordComponents(); + Map> typeMap = new HashMap<>(); + for (int i = 0; i < recordComponents.length; i++) { + typeMap.put(recordComponents[i].getName(), TypeToken.get(recordComponents[i].getGenericType())); + } + var argsMap = new HashMap(); + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + argsMap.put(name, gson.getAdapter(typeMap.get(name)).read(reader)); + } + reader.endObject(); + + var argTypes = new Class[recordComponents.length]; + var args = new Object[recordComponents.length]; + for (int i = 0; i < recordComponents.length; i++) { + argTypes[i] = recordComponents[i].getType(); + args[i] = argsMap.get(recordComponents[i].getName()); + } + Constructor constructor; + try { + constructor = clazz.getDeclaredConstructor(argTypes); + constructor.setAccessible(true); + return constructor.newInstance(args); + } catch (NoSuchMethodException | InstantiationException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + } + } +} diff --git a/Core/src/main/java/fr/pandacube/lib/core/util/ServerPropertyFile.java b/Core/src/main/java/fr/pandacube/lib/core/util/ServerPropertyFile.java index 373ad51..360a4ea 100644 --- a/Core/src/main/java/fr/pandacube/lib/core/util/ServerPropertyFile.java +++ b/Core/src/main/java/fr/pandacube/lib/core/util/ServerPropertyFile.java @@ -7,13 +7,9 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; public class ServerPropertyFile { - - private static final Gson SERIALIZER = new GsonBuilder().setPrettyPrinting().create(); private transient File file; @@ -39,7 +35,7 @@ public class ServerPropertyFile { public boolean loadFromFile() { try (BufferedReader in = new BufferedReader(new FileReader(file))) { - ServerPropertyFile dataFile = SERIALIZER.fromJson(in, getClass()); + ServerPropertyFile dataFile = Json.gsonPrettyPrinting.fromJson(in, getClass()); name = dataFile.name; memory = dataFile.memory; @@ -61,7 +57,7 @@ public class ServerPropertyFile { public boolean save() { try (BufferedWriter out = new BufferedWriter(new FileWriter(file, false))) { - SERIALIZER.toJson(this, out); + Json.gsonPrettyPrinting.toJson(this, out); out.flush(); return true; } catch (IOException e) { diff --git a/Core/src/main/java/fr/pandacube/lib/core/util/TypeConverter.java b/Core/src/main/java/fr/pandacube/lib/core/util/TypeConverter.java index ef0df57..e93cce6 100644 --- a/Core/src/main/java/fr/pandacube/lib/core/util/TypeConverter.java +++ b/Core/src/main/java/fr/pandacube/lib/core/util/TypeConverter.java @@ -5,7 +5,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import com.google.gson.Gson; import com.google.gson.JsonElement; /** @@ -128,7 +127,7 @@ public class TypeConverter { } if (o instanceof JsonElement) { - o = new Gson().fromJson((JsonElement)o, Object.class); + o = Json.gson.fromJson((JsonElement)o, Object.class); } if (o instanceof Map) { @@ -175,7 +174,7 @@ public class TypeConverter { } if (o instanceof JsonElement) { - o = new Gson().fromJson((JsonElement)o, Object.class); + o = Json.gson.fromJson((JsonElement)o, Object.class); }