package emu.grasscutter.utils; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import emu.grasscutter.data.common.DynamicFloat; import lombok.val; public final class JsonUtils { // Adapters static class DynamicFloatAdapter extends TypeAdapter { @Override public DynamicFloat read(JsonReader reader) throws IOException { switch (reader.peek()) { case STRING: return new DynamicFloat(reader.nextString()); case NUMBER: return new DynamicFloat((float) reader.nextDouble()); case BEGIN_ARRAY: reader.beginArray(); val opStack = new ArrayList(); while (reader.hasNext()) { opStack.add(switch (reader.peek()) { case STRING -> new DynamicFloat.StackOp(reader.nextString()); case NUMBER -> new DynamicFloat.StackOp((float) reader.nextDouble()); default -> throw new IOException("Invalid DynamicFloat definition - " + reader.peek().name()); }); } reader.endArray(); return new DynamicFloat(opStack); default: throw new IOException("Invalid DynamicFloat definition - " + reader.peek().name()); } } @Override public void write(JsonWriter writer, DynamicFloat f) {}; } static final Gson gson = new GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(DynamicFloat.class, new DynamicFloatAdapter()) .create(); /* * Encode an object to a JSON string */ public static String encode(Object object) { return gson.toJson(object); } public static T decode(JsonElement jsonElement, Class classType) throws JsonSyntaxException { return gson.fromJson(jsonElement, classType); } public static T loadToClass(Reader fileReader, Class classType) throws IOException { return gson.fromJson(fileReader, classType); } @Deprecated(forRemoval = true) public static T loadToClass(String filename, Class classType) throws IOException { try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { return loadToClass(fileReader, classType); } } public static T loadToClass(Path filename, Class classType) throws IOException { try (var fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) { return loadToClass(fileReader, classType); } } public static List loadToList(Reader fileReader, Class classType) throws IOException { return gson.fromJson(fileReader, TypeToken.getParameterized(List.class, classType).getType()); } @Deprecated(forRemoval = true) public static List loadToList(String filename, Class classType) throws IOException { try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { return loadToList(fileReader, classType); } } public static List loadToList(Path filename, Class classType) throws IOException { try (var fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) { return loadToList(fileReader, classType); } } public static Map loadToMap(Reader fileReader, Class keyType, Class valueType) throws IOException { return gson.fromJson(fileReader, TypeToken.getParameterized(Map.class, keyType, valueType).getType()); } @Deprecated(forRemoval = true) public static Map loadToMap(String filename, Class keyType, Class valueType) throws IOException { try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { return loadToMap(fileReader, keyType, valueType); } } public static Map loadToMap(Path filename, Class keyType, Class valueType) throws IOException { try (var fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) { return loadToMap(fileReader, keyType, valueType); } } /** * Safely JSON decodes a given string. * @param jsonData The JSON-encoded data. * @return JSON decoded data, or null if an exception occurred. */ public static T decode(String jsonData, Class classType) { try { return gson.fromJson(jsonData, classType); } catch (Exception ignored) { return null; } } }