001/* 002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.benchmarking.common.util; 017 018import com.google.gson.*; 019import com.google.gson.reflect.TypeToken; 020 021import java.io.IOException; 022import java.lang.reflect.Type; 023import java.nio.file.Files; 024import java.nio.file.Path; 025import java.time.Instant; 026import java.time.format.DateTimeFormatter; 027import java.util.List; 028import java.util.Locale; 029import java.util.Map; 030 031/** 032 * Common JSON serialization utilities for benchmark results. 033 * Provides consistent formatting and serialization across all benchmark modules. 034 * 035 */ 036public final class JsonSerializationHelper { 037 038 private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_INSTANT; 039 040 /** 041 * Gson instance configured for benchmark result serialization. 042 * Features: 043 * - Pretty printing 044 * - Smart number formatting (integers without .0) 045 * - ISO instant formatting 046 * - Special floating point value serialization 047 * - Thread-safe singleton 048 */ 049 public static final Gson GSON = new GsonBuilder() 050 .setPrettyPrinting() 051 .serializeSpecialFloatingPointValues() 052 .registerTypeAdapter(Double.class, new DoubleSerializer()) 053 .registerTypeAdapter(Instant.class, new InstantSerializer()) 054 .create(); 055 056 /** 057 * Compact Gson instance for minimal output (no pretty printing). 058 * Useful for reducing JSON size in performance-critical situations. 059 */ 060 public static final Gson COMPACT_GSON = new GsonBuilder() 061 .serializeSpecialFloatingPointValues() 062 .registerTypeAdapter(Double.class, new DoubleSerializer()) 063 .registerTypeAdapter(Instant.class, new InstantSerializer()) 064 .create(); 065 066 private JsonSerializationHelper() { 067 // Utility class 068 } 069 070 /** 071 * Writes an object to a JSON file. 072 * 073 * @param path the file path to write to 074 * @param object the object to serialize 075 * @throws IOException if an I/O error occurs 076 */ 077 public static void writeJsonFile(Path path, Object object) throws IOException { 078 String json = GSON.toJson(object); 079 Files.createDirectories(path.getParent()); 080 Files.writeString(path, json); 081 } 082 083 /** 084 * Reads a JSON file into an object. 085 * 086 * @param <T> the type to deserialize to 087 * @param path the file path to read from 088 * @param type the class of the type to deserialize 089 * @return the deserialized object 090 * @throws IOException if an I/O error occurs 091 */ 092 public static <T> T readJsonFile(Path path, Class<T> type) throws IOException { 093 String json = Files.readString(path); 094 return GSON.fromJson(json, type); 095 } 096 097 /** 098 * Reads a JSON file into a generic type using TypeToken. 099 * Useful for deserializing collections and generic types. 100 * 101 * @param <T> the type to deserialize to 102 * @param path the file path to read from 103 * @param typeToken the TypeToken representing the generic type 104 * @return the deserialized object 105 * @throws IOException if an I/O error occurs 106 */ 107 public static <T> T readJsonFile(Path path, TypeToken<T> typeToken) throws IOException { 108 String json = Files.readString(path); 109 return GSON.fromJson(json, typeToken.getType()); 110 } 111 112 /** 113 * Serializes an object to JSON string using the default GSON instance. 114 * 115 * @param object the object to serialize 116 * @return the JSON string representation 117 */ 118 public static String toJson(Object object) { 119 return GSON.toJson(object); 120 } 121 122 /** 123 * Serializes an object to compact JSON string (no pretty printing). 124 * 125 * @param object the object to serialize 126 * @return the compact JSON string representation 127 */ 128 public static String toCompactJson(Object object) { 129 return COMPACT_GSON.toJson(object); 130 } 131 132 /** 133 * Deserializes a JSON string to an object. 134 * 135 * @param <T> the type to deserialize to 136 * @param json the JSON string 137 * @param type the class of the type to deserialize 138 * @return the deserialized object 139 */ 140 public static <T> T fromJson(String json, Class<T> type) { 141 return GSON.fromJson(json, type); 142 } 143 144 /** 145 * Deserializes a JSON string to a generic type using TypeToken. 146 * 147 * @param <T> the type to deserialize to 148 * @param json the JSON string 149 * @param typeToken the TypeToken representing the generic type 150 * @return the deserialized object 151 */ 152 public static <T> T fromJson(String json, TypeToken<T> typeToken) { 153 return GSON.fromJson(json, typeToken.getType()); 154 } 155 156 /** 157 * Convenience method for deserializing JSON to Map<String, Object>. 158 * 159 * @param json the JSON string 160 * @return the deserialized map 161 */ 162 public static Map<String, Object> jsonToMap(String json) { 163 Type type = new TypeToken<Map<String, Object>>() { 164 }.getType(); 165 return GSON.fromJson(json, type); 166 } 167 168 /** 169 * Convenience method for deserializing JSON to List<Map<String, Object>>. 170 * 171 * @param json the JSON string 172 * @return the deserialized list of maps 173 */ 174 public static List<Map<String, Object>> jsonToListOfMaps(String json) { 175 Type type = new TypeToken<List<Map<String, Object>>>() { 176 }.getType(); 177 return GSON.fromJson(json, type); 178 } 179 180 /** 181 * Creates a JsonElement from an object using the default GSON instance. 182 * 183 * @param object the object to convert 184 * @return the JsonElement representation 185 */ 186 public static JsonElement toJsonTree(Object object) { 187 return GSON.toJsonTree(object); 188 } 189 190 /** 191 * Formats a double value for display. 192 * Returns integer representation if the value is a whole number. 193 * 194 * @param value the value to format 195 * @return formatted string 196 */ 197 public static String formatDouble(double value) { 198 if (value == (long) value) { 199 return String.valueOf((long) value); 200 } 201 return String.format(Locale.US, "%.2f", value); 202 } 203 204 /** 205 * Custom serializer for Double values. 206 * Serializes whole numbers without decimal point. 207 */ 208 private static class DoubleSerializer implements JsonSerializer<Double> { 209 @Override 210 public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) { 211 if (src == null) { 212 return JsonNull.INSTANCE; 213 } 214 if (src.isNaN() || src.isInfinite()) { 215 return new JsonPrimitive(src.toString()); 216 } 217 if (src == src.longValue()) { 218 return new JsonPrimitive(src.longValue()); 219 } 220 return new JsonPrimitive(src); 221 } 222 } 223 224 /** 225 * Custom serializer for Instant values. 226 * Serializes to ISO-8601 format. 227 */ 228 private static class InstantSerializer implements JsonSerializer<Instant> { 229 @Override 230 public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { 231 if (src == null) { 232 return JsonNull.INSTANCE; 233 } 234 return new JsonPrimitive(ISO_FORMATTER.format(src)); 235 } 236 } 237 238}