001package squidpony.store.json; 002 003import com.badlogic.gdx.utils.Json; 004import com.badlogic.gdx.utils.JsonValue; 005import com.badlogic.gdx.utils.JsonWriter; 006import com.badlogic.gdx.utils.reflect.ClassReflection; 007import com.badlogic.gdx.utils.reflect.ReflectionException; 008import regexodus.Pattern; 009import squidpony.FakeLanguageGen; 010import squidpony.Maker; 011import squidpony.StringStringMap; 012import squidpony.squidgrid.Direction; 013import squidpony.squidgrid.Radius; 014import squidpony.squidmath.*; 015 016import java.util.*; 017 018/** 019 * Augmented version of LibGDX's Json class that knows how to handle various data types common in SquidLib. 020 * This includes OrderedMap, which notably allows non-String keys (LibGDX's default Map serializer requires keys to be 021 * Strings), but does not currently allow the IHasher to be set (which only should affect OrderedMaps with array keys). 022 * It also makes significantly shorter serialized output for 2D char arrays, GreasedRegion and FakeLanguageGen objects, 023 * and various collections (IntDoubleOrderedMap, IntVLA, Arrangement, K2, and K2V1 at least). 024 * Created by Tommy Ettinger on 1/9/2017. 025 */ 026public class JsonConverter extends Json { 027 /** 028 * Creates a new JsonConverter using "minimal" output type, so it omits double quotes whenever possible but gives 029 * up compatibility with most other JSON readers. Give the constructor 030 * {@link JsonWriter.OutputType#json} if you need full compatibility. 031 */ 032 public JsonConverter() { 033 super(); 034 initialize(this); 035 } 036 037 /** 038 * Creates a new JsonConverter with the given OutputType; typically minimal is fine, but compatibility may require 039 * you to use json; the javascript type is a sort of middle ground. 040 * @param outputType a JsonWriter.OutputType enum value that determines what syntax can be omitted from the output 041 */ 042 public JsonConverter(JsonWriter.OutputType outputType) { 043 super(outputType); 044 initialize(this); 045 } 046 047 public static final Object INVALID = Float.NaN; 048 049 public static void initialize(Json json) 050 { 051 json.addClassTag("#St", String.class); 052 json.addClassTag("#Z", Boolean.class); 053 json.addClassTag("#z", boolean.class); 054 json.addClassTag("#B", Byte.class); 055 json.addClassTag("#b", byte.class); 056 json.addClassTag("#S", Short.class); 057 json.addClassTag("#s", short.class); 058 json.addClassTag("#C", Character.class); 059 json.addClassTag("#c", char.class); 060 json.addClassTag("#I", Integer.class); 061 json.addClassTag("#i", int.class); 062 json.addClassTag("#F", Float.class); 063 json.addClassTag("#f", float.class); 064 json.addClassTag("#L", Long.class); 065 json.addClassTag("#l", long.class); 066 json.addClassTag("#D", Double.class); 067 json.addClassTag("#d", double.class); 068 json.addClassTag("#Co", Coord.class); 069 json.addClassTag("#Co3", Coord3D.class); 070 json.addClassTag("#CoD", CoordDouble.class); 071 json.addClassTag("#SSet", SortedSet.class); 072 json.addClassTag("#Patt", Pattern.class); 073 json.addClassTag("#Grea", GreasedRegion.class); 074 json.addClassTag("#IDOM", IntDoubleOrderedMap.class); 075 json.addClassTag("#Lang", FakeLanguageGen.class); 076 json.addClassTag("#LnAl", FakeLanguageGen.Alteration.class); 077 json.addClassTag("#LnMd", FakeLanguageGen.Modifier.class); 078 json.addClassTag("#SSMp", StringStringMap.class); 079 json.addClassTag("#OMap", OrderedMap.class); 080 json.addClassTag("#EOMp", EnumOrderedMap.class); 081 json.addClassTag("#OSet", OrderedSet.class); 082 json.addClassTag("#UOSt", UnorderedSet.class); 083 json.addClassTag("#EOSt", EnumOrderedSet.class); 084 json.addClassTag("#Aran", Arrangement.class); 085 json.addClassTag("#K2", K2.class); 086 json.addClassTag("#K2V1", K2V1.class); 087 json.addClassTag("#IVLA", IntVLA.class); 088 json.addClassTag("#SVLA", ShortVLA.class); 089 json.addClassTag("#FNoi", FastNoise.class); 090 json.addClassTag("#RNG", RNG.class); 091 json.addClassTag("#SRNG", StatefulRNG.class); 092 json.addClassTag("#EdiR", EditRNG.class); 093 json.addClassTag("#CriR", CriticalRNG.class); 094 json.addClassTag("#DhaR", DharmaRNG.class); 095 json.addClassTag("#DecR", DeckRNG.class); 096 json.addClassTag("#Ligh", LightRNG.class); 097 json.addClassTag("#Linn", LinnormRNG.class); 098 json.addClassTag("#DivR", DiverRNG.class); 099 json.addClassTag("#LonP", LongPeriodRNG.class); 100 json.addClassTag("#MnwR", MoonwalkRNG.class); 101 json.addClassTag("#ThrA", ThrustAltRNG.class); 102 json.addClassTag("#SlkR", SilkRNG.class); 103 json.addClassTag("#LthR", Lathe32RNG.class); 104 json.addClassTag("#SblR", SobolQRNG.class); 105 json.addClassTag("#GWTR", GWTRNG.class); 106 json.addClassTag("#XoRo", XoRoRNG.class); 107 json.addClassTag("#MMvR", MiniMover64RNG.class); 108 json.addClassTag("#Mist", CrossHash.Mist.class); 109 json.addClassTag("#Yolk", CrossHash.Yolk.class); 110 json.addClassTag("#Dir", Direction.class); 111 json.addClassTag("#Rad", Radius.class); 112 113 json.setSerializer(Pattern.class, new Serializer<Pattern>() { 114 @Override 115 public void write(Json json, Pattern object, Class knownType) { 116 if(object == null) 117 { 118 json.writeValue(null); 119 return; 120 } 121 json.writeValue((Object) object.serializeToString(), String.class); 122 } 123 124 @Override 125 public Pattern read(Json json, JsonValue jsonData, Class type) { 126 if(jsonData == null || jsonData.isNull()) return null; 127 String data = jsonData.asString(); 128 if(data == null || data.length() < 2) return null; 129 return Pattern.deserializeFromString(data); 130 } 131 }); 132 json.setSerializer(Coord.class, new Serializer<Coord>() { 133 @Override 134 public void write(Json json, Coord object, Class knownType) { 135 if(object == null) 136 { 137 json.writeValue(null); 138 return; 139 } 140 json.writeArrayStart(); 141 json.writeValue(object.x, int.class); 142 json.writeValue(object.y, int.class); 143 json.writeArrayEnd(); 144 } 145 146 @Override 147 public Coord read(Json json, JsonValue jsonData, Class type) { 148 if(jsonData == null || jsonData.isNull()) return null; 149 return Coord.get(jsonData.getInt(0), jsonData.getInt(1)); 150 } 151 }); 152 153 json.setSerializer(GreasedRegion.class, new Serializer<GreasedRegion>() { 154 @Override 155 public void write(Json json, GreasedRegion object, Class knownType) { 156 if(object == null) 157 { 158 json.writeValue(null); 159 return; 160 } 161 json.writeObjectStart(); 162 json.writeObjectStart("items", GreasedRegion.class, GreasedRegion.class); 163 json.writeValue("w", object.width); 164 json.writeValue("h", object.height); 165 json.writeValue("d", object.data); 166 json.writeObjectEnd(); 167 json.writeObjectEnd(); 168 } 169 170 @Override 171 public GreasedRegion read(Json json, JsonValue jsonData, Class type) { 172 if(jsonData == null || jsonData.isNull()) return null; 173 return new GreasedRegion(jsonData.get("d").asLongArray(), jsonData.getInt("w"), jsonData.getInt("h")); 174 } 175 }); 176 json.setSerializer(IntVLA.class, new Serializer<IntVLA>() { 177 @Override 178 public void write(Json json, IntVLA object, Class knownType) { 179 if(object == null) 180 { 181 json.writeValue(null); 182 return; 183 } 184 json.writeValue(object.toArray(), int[].class); 185 } 186 187 @Override 188 public IntVLA read(Json json, JsonValue jsonData, Class type) { 189 if(jsonData == null || jsonData.isNull()) return null; 190 return new IntVLA(jsonData.asIntArray()); 191 } 192 }); 193 194 json.setSerializer(IntDoubleOrderedMap.class, new Serializer<IntDoubleOrderedMap>() { 195 @Override 196 public void write(Json json, IntDoubleOrderedMap object, Class knownType) { 197 if(object == null) 198 { 199 json.writeValue(null); 200 return; 201 } 202 json.writeObjectStart(); 203 json.writeArrayStart("k"); 204 for (int i = 0; i < object.size(); i++) { 205 json.writeValue(object.keyAt(i)); 206 } 207 json.writeArrayEnd(); 208 json.writeArrayStart("v"); 209 for (int i = 0; i < object.size(); i++) { 210 json.writeValue(object.getAt(i)); 211 } 212 json.writeArrayEnd(); 213 json.writeValue("f", object.f); 214 json.writeObjectEnd(); 215 } 216 217 @Override 218 public IntDoubleOrderedMap read(Json json, JsonValue jsonData, Class type) { 219 if(jsonData == null || jsonData.isNull()) return null; 220 return new IntDoubleOrderedMap(jsonData.get("k").asIntArray(), jsonData.get("v").asDoubleArray(), jsonData.getFloat("f")); 221 } 222 }); 223 224 json.setSerializer(StringStringMap.class, new Serializer<StringStringMap>() { 225 @Override 226 public void write(Json json, StringStringMap object, Class knownType) { 227 if(object == null) 228 { 229 json.writeValue(null); 230 return; 231 } 232 json.writeObjectStart(); 233 json.writeValue("k", object.keysAsOrderedSet(), OrderedSet.class, String.class); 234 json.writeValue("v", object.valuesAsList(), ArrayList.class, String.class); 235 json.writeValue("f", object.f); 236 json.writeObjectEnd(); 237 } 238 239 @Override 240 @SuppressWarnings("unchecked") 241 public StringStringMap read(Json json, JsonValue jsonData, Class type) { 242 if(jsonData == null || jsonData.isNull()) return null; 243 return new StringStringMap(json.readValue(OrderedSet.class, String.class, jsonData.get("k")), 244 json.readValue(ArrayList.class, String.class, jsonData.get("v")), jsonData.getFloat("f")); 245 } 246 }); 247 248 json.setSerializer(OrderedMap.class, new Serializer<OrderedMap>() { 249 @Override 250 public void write(Json json, OrderedMap object, Class knownType) { 251 if(object == null) 252 { 253 json.writeValue(null); 254 return; 255 } 256 json.writeObjectStart(); 257 json.writeValue("f", object.f); 258 if(!object.isEmpty()) { 259 json.writeValue("k", object.firstKey(), null); 260 json.writeValue("v", object.getAt(0), null); 261 int sz = object.size(); 262 Object[] r = new Object[(sz - 1) * 2]; 263 for (int i = 1, p = 0; i < sz; i++) { 264 r[p++] = object.keyAt(i); 265 r[p++] = object.getAt(i); 266 } 267 json.writeValue("r", r, Object[].class, Object.class); 268 } 269 json.writeObjectEnd(); 270 } 271 272 @Override 273 public OrderedMap read(Json json, JsonValue jsonData, Class type) { 274 if(jsonData == null || jsonData.isNull()) return null; 275 float f = json.readValue("f", float.class, jsonData); 276 Object k = json.readValue("k", null, INVALID, jsonData); 277 Object v = json.readValue("v", null, INVALID, jsonData); 278 Object[] r = json.readValue("r", Object[].class, jsonData); 279 if(k == INVALID) 280 return new OrderedMap(0, f); 281 return Maker.makeOM(f, k, v, r); 282 //return new OrderedMap(json.readValue(OrderedSet.class, jsonData.get("k")), 283 // json.readValue(ArrayList.class, jsonData.get("v")), jsonData.getFloat("f")); 284 } 285 }); 286 json.setSerializer(EnumOrderedMap.class, new Serializer<EnumOrderedMap>() { 287 @Override 288 public void write(Json json, EnumOrderedMap object, Class knownType) { 289 if(object == null) 290 { 291 json.writeValue(null); 292 return; 293 } 294 json.writeObjectStart(); 295 if(!object.isEmpty()) { 296 json.writeValue("c", object.firstKey().getClass().getName()); 297 json.writeValue("k", object.firstKey(), null); 298 json.writeValue("v", object.getAt(0), null); 299 int sz = object.size(); 300 Object[] r = new Object[(sz - 1) * 2]; 301 for (int i = 1, p = 0; i < sz; i++) { 302 r[p++] = object.keyAt(i); 303 r[p++] = object.getAt(i); 304 } 305 json.writeValue("r", r, Object[].class, Object.class); 306 } 307 else 308 json.writeValue("c", "default"); 309 json.writeObjectEnd(); 310 } 311 312 @Override 313 @SuppressWarnings("unchecked") 314 public EnumOrderedMap read(Json json, JsonValue jsonData, Class type) { 315 if(jsonData == null || jsonData.isNull()) return null; 316 String c = json.readValue("c", String.class, "", jsonData); 317 if("default".equals(c)) 318 return new EnumOrderedMap(); 319 try { 320 Class<? extends Enum<?>> cl = ClassReflection.forName(c); 321 if(!ClassReflection.isEnum(cl)) 322 return null; 323 Enum<?> k = json.readValue("k", cl, jsonData); 324 Object v = json.readValue("v", null, INVALID, jsonData); 325 Object[] r = json.readValue("r", Object[].class, jsonData); 326 if(v == INVALID) 327 return new EnumOrderedMap(); 328 return Maker.makeEOM(k, v, r); 329 } catch (ReflectionException e) { 330 return null; 331 } 332 //return new OrderedMap(json.readValue(OrderedSet.class, jsonData.get("k")), 333 // json.readValue(ArrayList.class, jsonData.get("v")), jsonData.getFloat("f")); 334 } 335 }); 336 json.setSerializer(EnumOrderedSet.class, new Serializer<EnumOrderedSet>() { 337 @Override 338 public void write(Json json, EnumOrderedSet object, Class knownType) { 339 if(object == null) 340 { 341 json.writeValue(null); 342 return; 343 } 344 json.writeObjectStart(); 345 if(!object.isEmpty()) { 346 json.writeValue("c", object.first().getClass().getName()); 347 json.writeValue("i", object.first(), null); 348 int sz = object.size(); 349 Object[] r = new Object[sz - 1]; 350 for (int i = 1, p = 0; i < sz; i++) { 351 r[p++] = object.getAt(i); 352 } 353 json.writeValue("r", r, Object[].class, Object.class); 354 } 355 else 356 json.writeValue("c", "default"); 357 json.writeObjectEnd(); 358 } 359 360 @Override 361 @SuppressWarnings("unchecked") 362 public EnumOrderedSet read(Json json, JsonValue jsonData, Class type) { 363 if(jsonData == null || jsonData.isNull()) return null; 364 String c = json.readValue("c", String.class, "", jsonData); 365 if("default".equals(c)) 366 return new EnumOrderedSet(); 367 try { 368 Class<? extends Enum<?>> cl = ClassReflection.forName(c); 369 if(!ClassReflection.isEnum(cl)) 370 return null; 371 Enum<?> i = json.readValue("i", cl, jsonData); 372 Object[] r = json.readValue("r", Object[].class, jsonData); 373 return Maker.makeEOS(i, (Enum<?>[]) r); 374 } catch (ReflectionException e) { 375 return null; 376 } 377 //return new OrderedMap(json.readValue(OrderedSet.class, jsonData.get("k")), 378 // json.readValue(ArrayList.class, jsonData.get("v")), jsonData.getFloat("f")); 379 } 380 }); 381 382 json.setSerializer(EnumMap.class, new Serializer<EnumMap>() { 383 @Override 384 public void write(Json json, EnumMap object, Class knownType) { 385 if(object == null) 386 { 387 json.writeValue(null); 388 return; 389 } 390 json.writeObjectStart(); 391 if(!object.isEmpty()) { 392 Iterator it = object.entrySet().iterator(); 393 Map.Entry en = (Map.Entry)it.next(); 394 json.writeValue("e", en.getKey(), Enum.class); 395 json.writeValue("v", en.getValue(), null); 396 int sz = object.size(); 397 Object[] r = new Object[(sz - 1) * 2]; 398 for (int i = 1, p = 0; i < sz; i++) { 399 if(!it.hasNext()) 400 break; 401 en = (Map.Entry)it.next(); 402 r[p++] = en.getKey(); 403 r[p++] = en.getValue(); 404 } 405 json.writeValue("r", r, Object[].class, Object.class); 406 } 407 json.writeObjectEnd(); 408 } 409 410 @Override 411 @SuppressWarnings("unchecked") 412 public EnumMap read(Json json, JsonValue jsonData, Class type) { 413 if(jsonData == null || jsonData.isNull() || jsonData.size == 0) return null; 414 return new EnumMap(Maker.makeOM(0.75f, 415 json.readValue("e", null, jsonData), 416 json.readValue("v", null, jsonData), 417 json.readValue("r", Object[].class, jsonData))); 418 //return new OrderedMap(json.readValue(OrderedSet.class, jsonData.get("k")), 419 // json.readValue(ArrayList.class, jsonData.get("v")), jsonData.getFloat("f")); 420 } 421 }); 422 json.setSerializer(Arrangement.class, new Serializer<Arrangement>() { 423 @Override 424 public void write(Json json, Arrangement object, Class knownType) { 425 if(object == null) 426 { 427 json.writeValue(null); 428 return; 429 } 430 json.writeObjectStart(); 431 json.writeValue("k", object.keysAsOrderedSet(), OrderedSet.class); 432 json.writeValue("f", object.f); 433 json.writeObjectEnd(); 434 } 435 436 @Override 437 @SuppressWarnings("unchecked") 438 public Arrangement read(Json json, JsonValue jsonData, Class type) { 439 if(jsonData == null || jsonData.isNull()) return null; 440 return new Arrangement(json.readValue(OrderedSet.class, jsonData.get("k")), jsonData.getFloat("f")); 441 } 442 }); 443 444 json.setSerializer(K2.class, new Serializer<K2>() { 445 @Override 446 public void write(Json json, K2 object, Class knownType) { 447 if(object == null) 448 { 449 json.writeValue(null); 450 return; 451 } 452 json.writeObjectStart(); 453 json.writeValue("a", object.getSetA(), SortedSet.class); 454 json.writeValue("b", object.getSetB(), SortedSet.class); 455 json.writeObjectEnd(); 456 } 457 458 @Override 459 @SuppressWarnings("unchecked") 460 public K2 read(Json json, JsonValue jsonData, Class type) { 461 if(jsonData == null || jsonData.isNull()) return null; 462 return new K2(json.readValue(SortedSet.class, jsonData.get("a")), json.readValue(SortedSet.class, jsonData.get("b"))); 463 } 464 }); 465 466 json.setSerializer(K2V1.class, new Serializer<K2V1>() { 467 @Override 468 public void write(Json json, K2V1 object, Class knownType) { 469 if(object == null) 470 { 471 json.writeValue(null); 472 return; 473 } 474 json.writeObjectStart(); 475 json.writeValue("a", object.getSetA(), SortedSet.class); 476 json.writeValue("b", object.getSetB(), SortedSet.class); 477 json.writeValue("q", object.getListQ(), ArrayList.class); 478 json.writeObjectEnd(); 479 } 480 481 @Override 482 @SuppressWarnings("unchecked") 483 public K2V1 read(Json json, JsonValue jsonData, Class type) { 484 if(jsonData == null || jsonData.isNull()) return null; 485 return new K2V1( 486 json.readValue(SortedSet.class, jsonData.get("a")), 487 json.readValue(SortedSet.class, jsonData.get("b")), 488 json.readValue(ArrayList.class, jsonData.get("q"))); 489 } 490 }); 491 492 json.setSerializer(char[][].class, new Serializer<char[][]>() { 493 @Override 494 public void write(Json json, char[][] object, Class knownType) { 495 if(object == null) 496 { 497 json.writeValue(null); 498 return; 499 } 500 int sz = object.length; 501 json.writeArrayStart(); 502 for (int i = 0; i < sz; i++) { 503 json.writeValue(String.valueOf(object[i])); 504 } 505 json.writeArrayEnd(); 506 } 507 508 @Override 509 public char[][] read(Json json, JsonValue jsonData, Class type) { 510 if(jsonData == null || jsonData.isNull()) 511 return null; 512 int sz = jsonData.size; 513 char[][] data = new char[sz][]; 514 JsonValue c = jsonData.child(); 515 for (int i = 0; i < sz && c != null; i++, c = c.next()) { 516 data[i] = c.asString().toCharArray(); 517 } 518 return data; 519 } 520 }); 521 json.setSerializer(FakeLanguageGen.class, new Serializer<FakeLanguageGen>() { 522 @Override 523 public void write(Json json, FakeLanguageGen object, Class knownType) { 524 if(object == null) 525 { 526 json.writeValue(null); 527 return; 528 } 529 json.writeValue(object.serializeToString()); 530 } 531 532 @Override 533 public FakeLanguageGen read(Json json, JsonValue jsonData, Class type) { 534 if(jsonData == null || jsonData.isNull()) 535 return null; 536 return FakeLanguageGen.deserializeFromString(jsonData.asString()); 537 } 538 }); 539 } 540}