From 2362d29ce89d930bd9a93683bf67e197400b89a1 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Thu, 25 Apr 2013 14:23:12 +0200 Subject: [PATCH] JSON refactoring for JsonValue. http://www.badlogicgames.com/wordpress/?p=2993 --- .../gwt/emu/com/badlogic/gdx/utils/Json.java | 234 +++------ .../com/badlogic/gdx/scenes/scene2d/ui/Skin.java | 34 +- gdx/src/com/badlogic/gdx/utils/Json.java | 240 +++------ gdx/src/com/badlogic/gdx/utils/JsonReader.java | 53 +- gdx/src/com/badlogic/gdx/utils/JsonReader.rl | 53 +- gdx/src/com/badlogic/gdx/utils/JsonValue.java | 583 +++++++++++++++++++++ gdx/src/com/badlogic/gdx/utils/JsonWriter.java | 4 + 7 files changed, 789 insertions(+), 412 deletions(-) create mode 100644 gdx/src/com/badlogic/gdx/utils/JsonValue.java diff --git a/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/com/badlogic/gdx/utils/Json.java b/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/com/badlogic/gdx/utils/Json.java index 6b4bfc739..51a1cf772 100644 --- a/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/com/badlogic/gdx/utils/Json.java +++ b/backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/com/badlogic/gdx/utils/Json.java @@ -31,6 +31,7 @@ import java.util.Map; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.JsonWriter.OutputType; import com.badlogic.gdx.utils.ObjectMap.Entry; +import com.badlogic.gdx.utils.ObjectMap.Values; import com.badlogic.gwtref.client.Constructor; import com.badlogic.gwtref.client.Field; import com.badlogic.gwtref.client.ReflectionCache; @@ -230,7 +231,7 @@ public class Json { ObjectMap fields = typeToFields.get(type); if (fields == null) fields = cacheFields(type); int i = 0; - for (FieldMetadata metadata : fields.values()) { + for (FieldMetadata metadata : new Values(fields)) { Field field = metadata.field; try { Object value = field.get(object); @@ -374,7 +375,7 @@ public class Json { } /** @param value May be null. - * @param knownType May be null if the type is unknown. + * @param knownClass May be null if the type is unknown. * @param elementType May be null if the type is unknown. */ public void writeValue (Object value, Class knownClass, Class elementType) { try { @@ -654,28 +655,27 @@ public class Json { return (T)readValue(type, elementType, new JsonReader().parse(json)); } - public void readField (Object object, String name, Object jsonData) { + public void readField (Object object, String name, JsonValue jsonData) { readField(object, name, name, null, jsonData); } - public void readField (Object object, String name, Class elementType, Object jsonData) { + public void readField (Object object, String name, Class elementType, JsonValue jsonData) { readField(object, name, name, elementType, jsonData); } - public void readField (Object object, String fieldName, String jsonName, Object jsonData) { + public void readField (Object object, String fieldName, String jsonName, JsonValue jsonData) { readField(object, fieldName, jsonName, null, jsonData); } /** @param elementType May be null if the type is unknown. */ - public void readField (Object object, String fieldName, String jsonName, Class elementType, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public void readField (Object object, String fieldName, String jsonName, Class elementType, JsonValue jsonMap) { Type type = ReflectionCache.getType(object.getClass()); ObjectMap fields = typeToFields.get(type); if (fields == null) fields = cacheFields(type); FieldMetadata metadata = fields.get(fieldName); if (metadata == null) throw new SerializationException("Field not found: " + fieldName + " (" + type.getName() + ")"); Field field = metadata.field; - Object jsonValue = jsonMap.get(jsonName); + JsonValue jsonValue = jsonMap.get(jsonName); if (jsonValue == null) return; if (elementType == null) elementType = metadata.elementType; try { @@ -692,24 +692,23 @@ public class Json { } } - public void readFields (Object object, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public void readFields (Object object, JsonValue jsonMap) { Type type = ReflectionCache.getType(object.getClass()); ObjectMap fields = typeToFields.get(type); if (fields == null) fields = cacheFields(type); - for (Entry entry : jsonMap.entries()) { - FieldMetadata metadata = fields.get(entry.key); + for (JsonValue child = jsonMap.child(); child != null; child = child.next()) { + FieldMetadata metadata = fields.get(child.name()); if (metadata == null) { if (ignoreUnknownFields) { - if (debug) System.out.println("Ignoring unknown field: " + entry.key + " (" + type.getName() + ")"); + if (debug) System.out.println("Ignoring unknown field: " + child.name() + " (" + type.getName() + ")"); continue; } else - throw new SerializationException("Field not found: " + entry.key + " (" + type.getName() + ")"); + throw new SerializationException("Field not found: " + child.name() + " (" + type.getName() + ")"); } Field field = metadata.field; - if (entry.value == null) continue; + // if (entry.value == null) continue; // I don't remember what this did. :( try { - field.set(object, readValue(field.getType().getClassOfType(), metadata.elementType, entry.value)); + field.set(object, readValue(field.getType().getClassOfType(), metadata.elementType, child)); } catch (IllegalAccessException ex) { throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex); } catch (SerializationException ex) { @@ -725,16 +724,14 @@ public class Json { /** @param type May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public T readValue (String name, Class type, JsonValue jsonMap) { return (T)readValue(type, null, jsonMap.get(name)); } /** @param type May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, T defaultValue, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; - Object jsonValue = jsonMap.get(name); + public T readValue (String name, Class type, T defaultValue, JsonValue jsonMap) { + JsonValue jsonValue = jsonMap.get(name); if (jsonValue == null) return defaultValue; return (T)readValue(type, null, jsonValue); } @@ -742,17 +739,15 @@ public class Json { /** @param type May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, Class elementType, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public T readValue (String name, Class type, Class elementType, JsonValue jsonMap) { return (T)readValue(type, elementType, jsonMap.get(name)); } /** @param type May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, Class elementType, T defaultValue, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; - Object jsonValue = jsonMap.get(name); + public T readValue (String name, Class type, Class elementType, T defaultValue, JsonValue jsonMap) { + JsonValue jsonValue = jsonMap.get(name); if (jsonValue == null) return defaultValue; return (T)readValue(type, elementType, jsonValue); } @@ -760,28 +755,27 @@ public class Json { /** @param type May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (Class type, Class elementType, T defaultValue, Object jsonData) { + public T readValue (Class type, Class elementType, T defaultValue, JsonValue jsonData) { return (T)readValue(type, elementType, jsonData); } /** @param type May be null if the type is unknown. * @return May be null. */ - public T readValue (Class type, Object jsonData) { + public T readValue (Class type, JsonValue jsonData) { return (T)readValue(type, null, jsonData); } - /** @param type May be null if the type is unknown. + /** @param clazz May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (Class clazz, Class elementType, Object jsonData) { + public T readValue (Class clazz, Class elementType, JsonValue jsonData) { if (jsonData == null) return null; Type type = ReflectionCache.getType(clazz); - if (jsonData instanceof OrderedMap) { - OrderedMap jsonMap = (OrderedMap)jsonData; - - String className = typeName == null ? null : (String)jsonMap.remove(typeName); + if (jsonData.isObject()) { + String className = typeName == null ? null : jsonData.getString(typeName, null); if (className != null) { + jsonData.remove(typeName); try { type = ReflectionCache.forName(className); } catch (ClassNotFoundException ex) { @@ -793,19 +787,19 @@ public class Json { Object object; if (type != null) { Serializer serializer = classToSerializer.get(type); - if (serializer != null) return (T)serializer.read(this, jsonMap, type.getClassOfType()); + if (serializer != null) return (T)serializer.read(this, jsonData, type.getClassOfType()); object = newInstance(type); if (object instanceof Serializable) { - ((Serializable)object).read(this, jsonMap); + ((Serializable)object).read(this, jsonData); return (T)object; } if (object instanceof HashMap) { HashMap result = (HashMap)object; - for (Entry entry : jsonMap.entries()) - result.put(entry.key, readValue(elementType, null, entry.value)); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + result.put(child.name(), readValue(elementType, null, child)); return (T)result; } } else @@ -813,12 +807,12 @@ public class Json { if (object instanceof ObjectMap) { ObjectMap result = (ObjectMap)object; - for (String key : jsonMap.orderedKeys()) - result.put(key, readValue(elementType, null, jsonMap.get(key))); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + result.put(child.name(), readValue(elementType, null, child)); return (T)result; } - readFields(object, jsonMap); + readFields(object, jsonData); return (T)object; } @@ -827,52 +821,58 @@ public class Json { if (serializer != null) return (T)serializer.read(this, jsonData, type.getClassOfType()); } - if (jsonData instanceof Array) { - Array array = (Array)jsonData; + if (jsonData.isArray()) { if (type == null || type.isAssignableFrom(ReflectionCache.getType(Array.class))) { - Array newArray = new Array(array.size); - for (int i = 0, n = array.size; i < n; i++) - newArray.add(readValue(elementType, null, array.get(i))); + Array newArray = new Array(); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + newArray.add(readValue(elementType, null, child)); return (T)newArray; } if (type.isAssignableFrom(ReflectionCache.getType(ArrayList.class))) { - ArrayList newArray = new ArrayList(array.size); - for (int i = 0, n = array.size; i < n; i++) - newArray.add(readValue(elementType, null, array.get(i))); + ArrayList newArray = new ArrayList(); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + newArray.add(readValue(elementType, null, child)); return (T)newArray; } if (type.isArray()) { Class componentType = type.getComponentType(); if (elementType == null) elementType = componentType; - Object newArray = ReflectionCache.newArray(componentType, array.size); + Object newArray = ReflectionCache.newArray(componentType, jsonData.size()); Type arrayType = ReflectionCache.getType(newArray.getClass()); - for (int i = 0, n = array.size; i < n; i++) - arrayType.setArrayElement(newArray, i, readValue(elementType, null, array.get(i))); + int i = 0; + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + arrayType.setArrayElement(newArray, i++, readValue(elementType, null, child)); return (T)newArray; } throw new SerializationException("Unable to convert value to required type: " + jsonData + " (" + type.getName() + ")"); } - Class t = type.getClassOfType(); - if (jsonData instanceof Float) { - Float floatValue = (Float)jsonData; + Class t = type == null ? null : type.getClassOfType(); + if (jsonData.isNumber()) { try { - if (t == null || t == float.class || t == Float.class) return (T)(Float)floatValue; - if (t == int.class || t == Integer.class) return (T)(Integer)floatValue.intValue(); - if (t == long.class || t == Long.class) return (T)(Long)floatValue.longValue(); - if (t == double.class || t == Double.class) return (T)(Double)floatValue.doubleValue(); - if (t == short.class || t == Short.class) return (T)(Short)floatValue.shortValue(); - if (t == byte.class || t == Byte.class) return (T)(Byte)floatValue.byteValue(); + if (type == null || t == float.class || t == Float.class) return (T)(Float)jsonData.asFloat(); + if (t == int.class || t == Integer.class) return (T)(Integer)jsonData.asInt(); + if (t == long.class || t == Long.class) return (T)(Long)jsonData.asLong(); + if (t == double.class || t == Double.class) return (T)(Double)(double)jsonData.asFloat(); + if (t == String.class) return (T)Float.toString(jsonData.asFloat()); + if (t == short.class || t == Short.class) return (T)(Short)(short)jsonData.asInt(); + if (t == byte.class || t == Byte.class) return (T)(Byte)(byte)jsonData.asInt(); } catch (NumberFormatException ignored) { } - jsonData = String.valueOf(jsonData); + jsonData = new JsonValue(jsonData.asString()); } - if (jsonData instanceof Boolean) jsonData = String.valueOf(jsonData); + if (jsonData.isBoolean()) { + try { + if (type == null || t == boolean.class || t == Boolean.class) return (T)(Boolean)jsonData.asBoolean(); + } catch (NumberFormatException ignored) { + } + jsonData = new JsonValue(jsonData.asString()); + } - if (jsonData instanceof String) { - String string = (String)jsonData; - if (type == null || t == String.class) return (T)jsonData; + if (jsonData.isString()) { + String string = jsonData.asString(); + if (type == null || t == String.class) return (T)string; try { if (t == int.class || t == Integer.class) return (T)Integer.valueOf(string); if (t == float.class || t == Float.class) return (T)Float.valueOf(string); @@ -938,101 +938,7 @@ public class Json { } public String prettyPrint (String json, int singleLineColumns) { - StringBuilder buffer = new StringBuilder(512); - prettyPrint(new JsonReader().parse(json), buffer, 0, singleLineColumns); - return buffer.toString(); - } - - private void prettyPrint (Object object, StringBuilder buffer, int indent, int singleLineColumns) { - if (object instanceof OrderedMap) { - OrderedMap map = (OrderedMap)object; - if (map.size == 0) { - buffer.append("{}"); - } else { - boolean newLines = !isFlat(map); - int start = buffer.length(); - outer: - while (true) { - buffer.append(newLines ? "{\n" : "{ "); - int i = 0; - for (String key : map.orderedKeys()) { - if (newLines) indent(indent, buffer); - buffer.append(outputType.quoteName(key)); - buffer.append(": "); - prettyPrint(map.get(key), buffer, indent + 1, singleLineColumns); - if (i++ < map.size - 1) buffer.append(","); - buffer.append(newLines ? '\n' : ' '); - if (!newLines && buffer.length() - start > singleLineColumns) { - buffer.setLength(start); - newLines = true; - continue outer; - } - } - break; - } - if (newLines) indent(indent - 1, buffer); - buffer.append('}'); - } - } else if (object instanceof Array) { - Array array = (Array)object; - if (array.size == 0) { - buffer.append("[]"); - } else { - boolean newLines = !isFlat(array); - int start = buffer.length(); - outer: - while (true) { - buffer.append(newLines ? "[\n" : "[ "); - for (int i = 0, n = array.size; i < n; i++) { - if (newLines) indent(indent, buffer); - prettyPrint(array.get(i), buffer, indent + 1, singleLineColumns); - if (i < array.size - 1) buffer.append(","); - buffer.append(newLines ? '\n' : ' '); - if (!newLines && buffer.length() - start > singleLineColumns) { - buffer.setLength(start); - newLines = true; - continue outer; - } - } - break; - } - if (newLines) indent(indent - 1, buffer); - buffer.append(']'); - } - } else if (object instanceof String) { - buffer.append(outputType.quoteValue((String)object)); - } else if (object instanceof Float) { - Float floatValue = (Float)object; - int intValue = floatValue.intValue(); - buffer.append(floatValue - intValue == 0 ? intValue : object); - } else if (object instanceof Boolean) { - buffer.append(object); - } else if (object == null) { - buffer.append("null"); - } else - throw new SerializationException("Unknown object type: " + object.getClass()); - } - - static private boolean isFlat (ObjectMap map) { - for (Entry entry : map.entries()) { - if (entry.value instanceof ObjectMap) return false; - if (entry.value instanceof Array) return false; - } - return true; - } - - static private boolean isFlat (Array array) { - for (int i = 0, n = array.size; i < n; i++) { - Object value = array.get(i); - if (value instanceof ObjectMap) return false; - if (value instanceof Array) return false; - } - return true; - } - - static private void indent (int count, StringBuilder buffer) { - for (int i = 0; i < count; i++) - buffer.append('\t'); + return new JsonReader().parse(json).prettyPrint(outputType, singleLineColumns); } static private class FieldMetadata { @@ -1047,19 +953,19 @@ public class Json { static public interface Serializer { public void write (Json json, T object, Class knownType); - public T read (Json json, Object jsonData, Class type); + public T read (Json json, JsonValue jsonData, Class type); } static abstract public class ReadOnlySerializer implements Serializer { public void write (Json json, T object, Class knownType) { } - abstract public T read (Json json, Object jsonData, Class type); + abstract public T read (Json json, JsonValue jsonData, Class type); } static public interface Serializable { public void write (Json json); - public void read (Json json, OrderedMap jsonData); + public void read (Json json, JsonValue jsonData); } } diff --git a/gdx/src/com/badlogic/gdx/scenes/scene2d/ui/Skin.java b/gdx/src/com/badlogic/gdx/scenes/scene2d/ui/Skin.java index 88edd9a3c..1402d4b61 100644 --- a/gdx/src/com/badlogic/gdx/scenes/scene2d/ui/Skin.java +++ b/gdx/src/com/badlogic/gdx/scenes/scene2d/ui/Skin.java @@ -38,6 +38,7 @@ import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.Json.ReadOnlySerializer; +import com.badlogic.gdx.utils.JsonValue; import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap.Entry; import com.badlogic.gdx.utils.SerializationException; @@ -403,9 +404,9 @@ public class Skin implements Disposable { final Skin skin = this; final Json json = new Json() { - public T readValue (Class type, Class elementType, Object jsonData) { + public T readValue (Class type, Class elementType, JsonValue jsonData) { // If the JSON is a string but the type is not, look up the actual value by name. - if (jsonData instanceof String && !CharSequence.class.isAssignableFrom(type)) return get((String)jsonData, type); + if (jsonData.isString() && !CharSequence.class.isAssignableFrom(type)) return get(jsonData.asString(), type); return super.readValue(type, elementType, jsonData); } }; @@ -413,13 +414,10 @@ public class Skin implements Disposable { json.setUsePrototypes(false); json.setSerializer(Skin.class, new ReadOnlySerializer() { - public Skin read (Json json, Object jsonData, Class ignored) { - ObjectMap typeToValueMap = (ObjectMap)jsonData; - for (Entry typeEntry : typeToValueMap.entries()) { - String className = typeEntry.key; - ObjectMap valueMap = (ObjectMap)typeEntry.value; + public Skin read (Json json, JsonValue typeToValueMap, Class ignored) { + for (JsonValue valueMap = typeToValueMap.child(); valueMap != null; valueMap = valueMap.next()) { try { - readNamedObjects(json, Class.forName(className), valueMap); + readNamedObjects(json, Class.forName(valueMap.name()), valueMap); } catch (ClassNotFoundException ex) { throw new SerializationException(ex); } @@ -427,23 +425,22 @@ public class Skin implements Disposable { return skin; } - private void readNamedObjects (Json json, Class type, ObjectMap valueMap) { + private void readNamedObjects (Json json, Class type, JsonValue valueMap) { Class addType = type == TintedDrawable.class ? Drawable.class : type; - for (Entry valueEntry : valueMap.entries()) { - String name = valueEntry.key; - Object object = json.readValue(type, valueEntry.value); + for (JsonValue valueEntry = valueMap.child(); valueEntry != null; valueEntry = valueEntry.next()) { + Object object = json.readValue(type, valueEntry); if (object == null) continue; try { - add(name, object, addType); + add(valueEntry.name(), object, addType); } catch (Exception ex) { - throw new SerializationException("Error reading " + type.getSimpleName() + ": " + valueEntry.key, ex); + throw new SerializationException("Error reading " + type.getSimpleName() + ": " + valueEntry.name(), ex); } } } }); json.setSerializer(BitmapFont.class, new ReadOnlySerializer() { - public BitmapFont read (Json json, Object jsonData, Class type) { + public BitmapFont read (Json json, JsonValue jsonData, Class type) { String path = json.readValue("file", String.class, jsonData); FileHandle fontFile = skinFile.parent().child(path); @@ -470,9 +467,8 @@ public class Skin implements Disposable { }); json.setSerializer(Color.class, new ReadOnlySerializer() { - public Color read (Json json, Object jsonData, Class type) { - if (jsonData instanceof String) return get((String)jsonData, Color.class); - ObjectMap map = (ObjectMap)jsonData; + public Color read (Json json, JsonValue jsonData, Class type) { + if (jsonData.isString()) return get(jsonData.asString(), Color.class); String hex = json.readValue("hex", String.class, (String)null, jsonData); if (hex != null) return Color.valueOf(hex); float r = json.readValue("r", float.class, 0f, jsonData); @@ -484,7 +480,7 @@ public class Skin implements Disposable { }); json.setSerializer(TintedDrawable.class, new ReadOnlySerializer() { - public Object read (Json json, Object jsonData, Class type) { + public Object read (Json json, JsonValue jsonData, Class type) { String name = json.readValue("name", String.class, jsonData); Color color = json.readValue("color", Color.class, jsonData); return newDrawable(name, color); diff --git a/gdx/src/com/badlogic/gdx/utils/Json.java b/gdx/src/com/badlogic/gdx/utils/Json.java index 2787fb1a1..754643ded 100644 --- a/gdx/src/com/badlogic/gdx/utils/Json.java +++ b/gdx/src/com/badlogic/gdx/utils/Json.java @@ -19,6 +19,7 @@ package com.badlogic.gdx.utils; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.JsonWriter.OutputType; import com.badlogic.gdx.utils.ObjectMap.Entry; +import com.badlogic.gdx.utils.ObjectMap.Values; import java.io.IOException; import java.io.InputStream; @@ -38,7 +39,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -/** Reads/writes Java objects to/from JSON, automatically. +/** Reads/writes Java objects to/from JSON, automatically. See the wiki for usage: + * https://code.google.com/p/libgdx/wiki/JsonParsing * @author Nathan Sweet */ public class Json { private static final boolean debug = false; @@ -233,7 +235,7 @@ public class Json { ObjectMap fields = typeToFields.get(type); if (fields == null) fields = cacheFields(type); int i = 0; - for (FieldMetadata metadata : fields.values()) { + for (FieldMetadata metadata : new Values(fields)) { Field field = metadata.field; try { Object value = field.get(object); @@ -671,28 +673,27 @@ public class Json { return (T)readValue(type, elementType, new JsonReader().parse(json)); } - public void readField (Object object, String name, Object jsonData) { + public void readField (Object object, String name, JsonValue jsonData) { readField(object, name, name, null, jsonData); } - public void readField (Object object, String name, Class elementType, Object jsonData) { + public void readField (Object object, String name, Class elementType, JsonValue jsonData) { readField(object, name, name, elementType, jsonData); } - public void readField (Object object, String fieldName, String jsonName, Object jsonData) { + public void readField (Object object, String fieldName, String jsonName, JsonValue jsonData) { readField(object, fieldName, jsonName, null, jsonData); } /** @param elementType May be null if the type is unknown. */ - public void readField (Object object, String fieldName, String jsonName, Class elementType, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public void readField (Object object, String fieldName, String jsonName, Class elementType, JsonValue jsonMap) { Class type = object.getClass(); ObjectMap fields = typeToFields.get(type); if (fields == null) fields = cacheFields(type); FieldMetadata metadata = fields.get(fieldName); if (metadata == null) throw new SerializationException("Field not found: " + fieldName + " (" + type.getName() + ")"); Field field = metadata.field; - Object jsonValue = jsonMap.get(jsonName); + JsonValue jsonValue = jsonMap.get(jsonName); if (jsonValue == null) return; if (elementType == null) elementType = metadata.elementType; try { @@ -709,24 +710,23 @@ public class Json { } } - public void readFields (Object object, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public void readFields (Object object, JsonValue jsonMap) { Class type = object.getClass(); ObjectMap fields = typeToFields.get(type); if (fields == null) fields = cacheFields(type); - for (Entry entry : jsonMap.entries()) { - FieldMetadata metadata = fields.get(entry.key); + for (JsonValue child = jsonMap.child(); child != null; child = child.next()) { + FieldMetadata metadata = fields.get(child.name()); if (metadata == null) { if (ignoreUnknownFields) { - if (debug) System.out.println("Ignoring unknown field: " + entry.key + " (" + type.getName() + ")"); + if (debug) System.out.println("Ignoring unknown field: " + child.name() + " (" + type.getName() + ")"); continue; } else - throw new SerializationException("Field not found: " + entry.key + " (" + type.getName() + ")"); + throw new SerializationException("Field not found: " + child.name() + " (" + type.getName() + ")"); } Field field = metadata.field; - if (entry.value == null) continue; + // if (entry.value == null) continue; // I don't remember what this did. :( try { - field.set(object, readValue(field.getType(), metadata.elementType, entry.value)); + field.set(object, readValue(field.getType(), metadata.elementType, child)); } catch (IllegalAccessException ex) { throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex); } catch (SerializationException ex) { @@ -742,16 +742,14 @@ public class Json { /** @param type May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public T readValue (String name, Class type, JsonValue jsonMap) { return (T)readValue(type, null, jsonMap.get(name)); } /** @param type May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, T defaultValue, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; - Object jsonValue = jsonMap.get(name); + public T readValue (String name, Class type, T defaultValue, JsonValue jsonMap) { + JsonValue jsonValue = jsonMap.get(name); if (jsonValue == null) return defaultValue; return (T)readValue(type, null, jsonValue); } @@ -759,17 +757,15 @@ public class Json { /** @param type May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, Class elementType, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; + public T readValue (String name, Class type, Class elementType, JsonValue jsonMap) { return (T)readValue(type, elementType, jsonMap.get(name)); } /** @param type May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (String name, Class type, Class elementType, T defaultValue, Object jsonData) { - OrderedMap jsonMap = (OrderedMap)jsonData; - Object jsonValue = jsonMap.get(name); + public T readValue (String name, Class type, Class elementType, T defaultValue, JsonValue jsonMap) { + JsonValue jsonValue = jsonMap.get(name); if (jsonValue == null) return defaultValue; return (T)readValue(type, elementType, jsonValue); } @@ -777,27 +773,26 @@ public class Json { /** @param type May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (Class type, Class elementType, T defaultValue, Object jsonData) { + public T readValue (Class type, Class elementType, T defaultValue, JsonValue jsonData) { return (T)readValue(type, elementType, jsonData); } /** @param type May be null if the type is unknown. * @return May be null. */ - public T readValue (Class type, Object jsonData) { + public T readValue (Class type, JsonValue jsonData) { return (T)readValue(type, null, jsonData); } /** @param type May be null if the type is unknown. * @param elementType May be null if the type is unknown. * @return May be null. */ - public T readValue (Class type, Class elementType, Object jsonData) { + public T readValue (Class type, Class elementType, JsonValue jsonData) { if (jsonData == null) return null; - if (jsonData instanceof OrderedMap) { - OrderedMap jsonMap = (OrderedMap)jsonData; - - String className = typeName == null ? null : (String)jsonMap.remove(typeName); + if (jsonData.isObject()) { + String className = typeName == null ? null : jsonData.getString(typeName, null); if (className != null) { + jsonData.remove(typeName); try { type = (Class)Class.forName(className); } catch (ClassNotFoundException ex) { @@ -814,32 +809,32 @@ public class Json { Object object; if (type != null) { Serializer serializer = classToSerializer.get(type); - if (serializer != null) return (T)serializer.read(this, jsonMap, type); + if (serializer != null) return (T)serializer.read(this, jsonData, type); object = newInstance(type); if (object instanceof Serializable) { - ((Serializable)object).read(this, jsonMap); + ((Serializable)object).read(this, jsonData); return (T)object; } if (object instanceof HashMap) { HashMap result = (HashMap)object; - for (Entry entry : jsonMap.entries()) - result.put(entry.key, readValue(elementType, null, entry.value)); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + result.put(child.name(), readValue(elementType, null, child)); return (T)result; } } else - object = new OrderedMap(); + return (T)jsonData; if (object instanceof ObjectMap) { ObjectMap result = (ObjectMap)object; - for (String key : jsonMap.orderedKeys()) - result.put(key, readValue(elementType, null, jsonMap.get(key))); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + result.put(child.name(), readValue(elementType, null, child)); return (T)result; } - readFields(object, jsonMap); + readFields(object, jsonData); return (T)object; } @@ -848,65 +843,56 @@ public class Json { if (serializer != null) return (T)serializer.read(this, jsonData, type); } - if (jsonData instanceof Array) { - Array array = (Array)jsonData; + if (jsonData.isArray()) { if (type == null || Array.class.isAssignableFrom(type)) { Array newArray = type == null ? new Array() : (Array)newInstance(type); - newArray.ensureCapacity(array.size); - for (int i = 0, n = array.size; i < n; i++) - newArray.add(readValue(elementType, null, array.get(i))); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + newArray.add(readValue(elementType, null, child)); return (T)newArray; } if (List.class.isAssignableFrom(type)) { - List newArray = type == null ? new ArrayList(array.size) : (List)newInstance(type); - for (int i = 0, n = array.size; i < n; i++) - newArray.add(readValue(elementType, null, array.get(i))); + List newArray = type == null ? new ArrayList() : (List)newInstance(type); + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + newArray.add(readValue(elementType, null, child)); return (T)newArray; } if (type.isArray()) { Class componentType = type.getComponentType(); if (elementType == null) elementType = componentType; - Object newArray = java.lang.reflect.Array.newInstance(componentType, array.size); - for (int i = 0, n = array.size; i < n; i++) - java.lang.reflect.Array.set(newArray, i, readValue(elementType, null, array.get(i))); + Object newArray = java.lang.reflect.Array.newInstance(componentType, jsonData.size()); + int i = 0; + for (JsonValue child = jsonData.child(); child != null; child = child.next()) + java.lang.reflect.Array.set(newArray, i++, readValue(elementType, null, child)); return (T)newArray; } throw new SerializationException("Unable to convert value to required type: " + jsonData + " (" + type.getName() + ")"); } - if (jsonData instanceof Float) { - Float floatValue = (Float)jsonData; + if (jsonData.isNumber()) { try { - if (type == null || type == float.class || type == Float.class) return (T)floatValue; - if (type == int.class || type == Integer.class) return (T)(Integer)floatValue.intValue(); - if (type == long.class || type == Long.class) return (T)(Long)floatValue.longValue(); - if (type == double.class || type == Double.class) return (T)(Double)floatValue.doubleValue(); - if (type == short.class || type == Short.class) return (T)(Short)floatValue.shortValue(); - if (type == byte.class || type == Byte.class) return (T)(Byte)floatValue.byteValue(); + if (type == null || type == float.class || type == Float.class) return (T)(Float)jsonData.asFloat(); + if (type == int.class || type == Integer.class) return (T)(Integer)jsonData.asInt(); + if (type == long.class || type == Long.class) return (T)(Long)jsonData.asLong(); + if (type == double.class || type == Double.class) return (T)(Double)(double)jsonData.asFloat(); + if (type == String.class) return (T)Float.toString(jsonData.asFloat()); + if (type == short.class || type == Short.class) return (T)(Short)(short)jsonData.asInt(); + if (type == byte.class || type == Byte.class) return (T)(Byte)(byte)jsonData.asInt(); } catch (NumberFormatException ignored) { } - jsonData = String.valueOf(jsonData); + jsonData = new JsonValue(jsonData.asString()); } - if (jsonData instanceof Long) { - Long longValue = (Long)jsonData; + if (jsonData.isBoolean()) { try { - if (type == null || type == long.class || type == Long.class) return (T)longValue; - if (type == int.class || type == Integer.class) return (T)(Integer)longValue.intValue(); - if (type == float.class || type == Float.class) return (T)(Float)longValue.floatValue(); - if (type == double.class || type == Double.class) return (T)(Double)longValue.doubleValue(); - if (type == short.class || type == Short.class) return (T)(Short)longValue.shortValue(); - if (type == byte.class || type == Byte.class) return (T)(Byte)longValue.byteValue(); + if (type == null || type == boolean.class || type == Boolean.class) return (T)(Boolean)jsonData.asBoolean(); } catch (NumberFormatException ignored) { } - jsonData = String.valueOf(jsonData); + jsonData = new JsonValue(jsonData.asString()); } - if (jsonData instanceof Boolean) jsonData = String.valueOf(jsonData); - - if (jsonData instanceof String) { - String string = (String)jsonData; - if (type == null || type == String.class) return (T)jsonData; + if (jsonData.isString()) { + String string = jsonData.asString(); + if (type == null || type == String.class) return (T)string; try { if (type == int.class || type == Integer.class) return (T)Integer.valueOf(string); if (type == float.class || type == Float.class) return (T)Float.valueOf(string); @@ -972,103 +958,7 @@ public class Json { } public String prettyPrint (String json, int singleLineColumns) { - StringBuilder buffer = new StringBuilder(512); - prettyPrint(new JsonReader().parse(json), buffer, 0, singleLineColumns); - return buffer.toString(); - } - - private void prettyPrint (Object object, StringBuilder buffer, int indent, int singleLineColumns) { - if (object instanceof OrderedMap) { - OrderedMap map = (OrderedMap)object; - if (map.size == 0) { - buffer.append("{}"); - } else { - boolean newLines = !isFlat(map); - int start = buffer.length(); - outer: - while (true) { - buffer.append(newLines ? "{\n" : "{ "); - int i = 0; - for (String key : map.orderedKeys()) { - if (newLines) indent(indent, buffer); - buffer.append(outputType.quoteName(key)); - buffer.append(": "); - prettyPrint(map.get(key), buffer, indent + 1, singleLineColumns); - if (i++ < map.size - 1) buffer.append(","); - buffer.append(newLines ? '\n' : ' '); - if (!newLines && buffer.length() - start > singleLineColumns) { - buffer.setLength(start); - newLines = true; - continue outer; - } - } - break; - } - if (newLines) indent(indent - 1, buffer); - buffer.append('}'); - } - } else if (object instanceof Array) { - Array array = (Array)object; - if (array.size == 0) { - buffer.append("[]"); - } else { - boolean newLines = !isFlat(array); - int start = buffer.length(); - outer: - while (true) { - buffer.append(newLines ? "[\n" : "[ "); - for (int i = 0, n = array.size; i < n; i++) { - if (newLines) indent(indent, buffer); - prettyPrint(array.get(i), buffer, indent + 1, singleLineColumns); - if (i < array.size - 1) buffer.append(","); - buffer.append(newLines ? '\n' : ' '); - if (!newLines && buffer.length() - start > singleLineColumns) { - buffer.setLength(start); - newLines = true; - continue outer; - } - } - break; - } - if (newLines) indent(indent - 1, buffer); - buffer.append(']'); - } - } else if (object instanceof String) { - buffer.append(outputType.quoteValue((String)object)); - } else if (object instanceof Float) { - Float floatValue = (Float)object; - int intValue = floatValue.intValue(); - buffer.append(floatValue - intValue == 0 ? intValue : object); - } else if (object instanceof Long) { - buffer.append((Long)object); - } else if (object instanceof Boolean) { - buffer.append(object); - } else if (object == null) { - buffer.append("null"); - } else - throw new SerializationException("Unknown object type: " + object.getClass()); - } - - static private boolean isFlat (ObjectMap map) { - for (Entry entry : map.entries()) { - if (entry.value instanceof ObjectMap) return false; - if (entry.value instanceof Array) return false; - } - return true; - } - - static private boolean isFlat (Array array) { - for (int i = 0, n = array.size; i < n; i++) { - Object value = array.get(i); - if (value instanceof ObjectMap) return false; - if (value instanceof Array) return false; - } - return true; - } - - static private void indent (int count, StringBuilder buffer) { - for (int i = 0; i < count; i++) - buffer.append('\t'); + return new JsonReader().parse(json).prettyPrint(outputType, singleLineColumns); } static private class FieldMetadata { @@ -1095,19 +985,19 @@ public class Json { static public interface Serializer { public void write (Json json, T object, Class knownType); - public T read (Json json, Object jsonData, Class type); + public T read (Json json, JsonValue jsonData, Class type); } static abstract public class ReadOnlySerializer implements Serializer { public void write (Json json, T object, Class knownType) { } - abstract public T read (Json json, Object jsonData, Class type); + abstract public T read (Json json, JsonValue jsonData, Class type); } static public interface Serializable { public void write (Json json); - public void read (Json json, OrderedMap jsonData); + public void read (Json json, JsonValue jsonData); } } diff --git a/gdx/src/com/badlogic/gdx/utils/JsonReader.java b/gdx/src/com/badlogic/gdx/utils/JsonReader.java index c4addb61b..49c0620c6 100644 --- a/gdx/src/com/badlogic/gdx/utils/JsonReader.java +++ b/gdx/src/com/badlogic/gdx/utils/JsonReader.java @@ -25,20 +25,20 @@ import java.io.InputStreamReader; import java.io.Reader; import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.JsonValue.ValueType; /** Lightweight JSON parser.
*
- * The default behavior is to parse the JSON into a DOM made up of {@link OrderedMap}, {@link Array}, String, Float, Long, and - * Boolean objects. Extend this class and override methods to perform event driven parsing. When this is done, the parse methods - * will return null. + * The default behavior is to parse the JSON into a DOM containing {@link JsonValue} objects. Extend this class and override + * methods to perform event driven parsing. When this is done, the parse methods will return null. * @author Nathan Sweet */ public class JsonReader { - public Object parse (String json) { + public JsonValue parse (String json) { char[] data = json.toCharArray(); return parse(data, 0, data.length); } - public Object parse (Reader reader) { + public JsonValue parse (Reader reader) { try { char[] data = new char[1024]; int offset = 0; @@ -63,7 +63,7 @@ public class JsonReader { } } - public Object parse (InputStream input) { + public JsonValue parse (InputStream input) { try { return parse(new InputStreamReader(input, "ISO-8859-1")); } catch (IOException ex) { @@ -71,7 +71,7 @@ public class JsonReader { } } - public Object parse (FileHandle file) { + public JsonValue parse (FileHandle file) { try { return parse(file.read()); } catch (Exception ex) { @@ -79,7 +79,7 @@ public class JsonReader { } } - public Object parse (char[] data, int offset, int length) { + public JsonValue parse (char[] data, int offset, int length) { int cs, p = offset, pe = length, eof = pe, top = 0; int[] stack = new int[4]; @@ -431,14 +431,14 @@ public class JsonReader { throw new SerializationException("Error parsing JSON on line " + lineNumber + " near: " + new String(data, p, pe - p), parseRuntimeEx); } else if (elements.size != 0) { - Object element = elements.peek(); + JsonValue element = elements.peek(); elements.clear(); - if (element instanceof OrderedMap) + if (element != null && element.isObject()) throw new SerializationException("Error parsing JSON, unmatched brace."); else throw new SerializationException("Error parsing JSON, unmatched bracket."); } - Object root = this.root; + JsonValue root = this.root; this.root = null; return root; } @@ -576,28 +576,27 @@ public class JsonReader { // line 236 "JsonReader.rl" - private final Array elements = new Array(8); - private Object root, current; + private final Array elements = new Array(8); + private JsonValue root, current; - private void set (String name, Object value) { - if (current instanceof OrderedMap) - ((OrderedMap)current).put(name, value); - else if (current instanceof Array) - ((Array)current).add(value); + private void addChild (String name, JsonValue child) { + child.setName(name); + if (current.isArray() || current.isObject()) + current.addChild(child); else - root = value; + root = current; } protected void startObject (String name) { - OrderedMap value = new OrderedMap(); - if (current != null) set(name, value); + JsonValue value = new JsonValue(ValueType.object); + if (current != null) addChild(name, value); elements.add(value); current = value; } protected void startArray (String name) { - Array value = new Array(); - if (current != null) set(name, value); + JsonValue value = new JsonValue(ValueType.array); + if (current != null) addChild(name, value); elements.add(value); current = value; } @@ -608,19 +607,19 @@ public class JsonReader { } protected void string (String name, String value) { - set(name, value); + addChild(name, new JsonValue(value)); } protected void number (String name, float value) { - set(name, value); + addChild(name, new JsonValue(value)); } protected void number (String name, long value) { - set(name, value); + addChild(name, new JsonValue(value)); } protected void bool (String name, boolean value) { - set(name, value); + addChild(name, new JsonValue(value)); } private String unescape (String value) { diff --git a/gdx/src/com/badlogic/gdx/utils/JsonReader.rl b/gdx/src/com/badlogic/gdx/utils/JsonReader.rl index b26c31160..dad839794 100644 --- a/gdx/src/com/badlogic/gdx/utils/JsonReader.rl +++ b/gdx/src/com/badlogic/gdx/utils/JsonReader.rl @@ -24,20 +24,20 @@ import java.io.InputStreamReader; import java.io.Reader; import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.JsonValue.ValueType; /** Lightweight JSON parser.
*
- * The default behavior is to parse the JSON into a DOM made up of {@link OrderedMap}, {@link Array}, String, Float, Long, and - * Boolean objects. Extend this class and override methods to perform event driven parsing. When this is done, the parse methods - * will return null. + * The default behavior is to parse the JSON into a DOM containing {@link JsonValue} objects. Extend this class and override + * methods to perform event driven parsing. When this is done, the parse methods will return null. * @author Nathan Sweet */ public class JsonReader { - public Object parse (String json) { + public JsonValue parse (String json) { char[] data = json.toCharArray(); return parse(data, 0, data.length); } - public Object parse (Reader reader) { + public JsonValue parse (Reader reader) { try { char[] data = new char[1024]; int offset = 0; @@ -62,7 +62,7 @@ public class JsonReader { } } - public Object parse (InputStream input) { + public JsonValue parse (InputStream input) { try { return parse(new InputStreamReader(input, "ISO-8859-1")); } catch (IOException ex) { @@ -70,7 +70,7 @@ public class JsonReader { } } - public Object parse (FileHandle file) { + public JsonValue parse (FileHandle file) { try { return parse(file.read()); } catch (Exception ex) { @@ -78,7 +78,7 @@ public class JsonReader { } } - public Object parse (char[] data, int offset, int length) { + public JsonValue parse (char[] data, int offset, int length) { int cs, p = offset, pe = length, eof = pe, top = 0; int[] stack = new int[4]; @@ -220,42 +220,41 @@ public class JsonReader { if (data[i] == '\n') lineNumber++; throw new SerializationException("Error parsing JSON on line " + lineNumber + " near: " + new String(data, p, pe - p), parseRuntimeEx); } else if (elements.size != 0) { - Object element = elements.peek(); + JsonValue element = elements.peek(); elements.clear(); - if (element instanceof OrderedMap) + if (element != null && element.isObject()) throw new SerializationException("Error parsing JSON, unmatched brace."); else throw new SerializationException("Error parsing JSON, unmatched bracket."); } - Object root = this.root; + JsonValue root = this.root; this.root = null; return root; } %% write data; - private final Array elements = new Array(8); - private Object root, current; + private final Array elements = new Array(8); + private JsonValue root, current; - private void set (String name, Object value) { - if (current instanceof OrderedMap) - ((OrderedMap)current).put(name, value); - else if (current instanceof Array) - ((Array)current).add(value); + private void addChild (String name, JsonValue child) { + child.setName(name); + if (current.isArray() || current.isObject()) + current.addChild(child); else - root = value; + root = current; } protected void startObject (String name) { - OrderedMap value = new OrderedMap(); - if (current != null) set(name, value); + JsonValue value = new JsonValue(ValueType.object); + if (current != null) addChild(name, value); elements.add(value); current = value; } protected void startArray (String name) { - Array value = new Array(); - if (current != null) set(name, value); + JsonValue value = new JsonValue(ValueType.array); + if (current != null) addChild(name, value); elements.add(value); current = value; } @@ -266,19 +265,19 @@ public class JsonReader { } protected void string (String name, String value) { - set(name, value); + addChild(name, new JsonValue(value)); } protected void number (String name, float value) { - set(name, value); + addChild(name, new JsonValue(value)); } protected void number (String name, long value) { - set(name, value); + addChild(name, new JsonValue(value)); } protected void bool (String name, boolean value) { - set(name, value); + addChild(name, new JsonValue(value)); } private String unescape (String value) { diff --git a/gdx/src/com/badlogic/gdx/utils/JsonValue.java b/gdx/src/com/badlogic/gdx/utils/JsonValue.java new file mode 100644 index 000000000..2eddb7151 --- /dev/null +++ b/gdx/src/com/badlogic/gdx/utils/JsonValue.java @@ -0,0 +1,583 @@ + +package com.badlogic.gdx.utils; + +import com.badlogic.gdx.utils.JsonWriter.OutputType; + +/** Container for a JSON object, array, string, double, long, boolean, or null. + *

+ * Iteration of arrays or objects is easily done using a for loop:
+ * + *

+ * JsonValue map = ...;
+ * for (JsonValue entry = map.child(); entry != null; entry = entry.next())
+ * 	System.out.println(entry.name() + " = " + entry.asString());
+ * 
+ * @author Nathan Sweet */ +public class JsonValue { + private String name; + private ValueType type; + + private String stringValue; + private Boolean booleanValue; + private Double doubleValue; + private long longValue; + + private JsonValue child, next, prev; + + public JsonValue (ValueType type) { + this.type = type; + } + + /** @param value May be null. */ + public JsonValue (String value) { + set(value); + } + + public JsonValue (double value) { + set(value); + } + + public JsonValue (long value) { + set(value); + } + + public JsonValue (boolean value) { + set(value); + } + + /** Returns the child at the specified index. + * @return May be null. */ + public JsonValue get (int index) { + JsonValue current = child; + while (current != null && index > 0) { + index--; + current = current.next; + } + return current; + } + + /** Returns the child with the specified name. + * @return May be null. */ + public JsonValue get (String name) { + JsonValue current = child; + while (current != null && !current.name.equalsIgnoreCase(name)) + current = current.next; + return current; + } + + /** Returns the child at the specified index. + * @throws IllegalArgumentException if the child was not found. */ + public JsonValue require (int index) { + JsonValue current = child; + while (current != null && index > 0) { + index--; + current = current.next; + } + if (current == null) throw new IllegalArgumentException("Child not found with index: " + index); + return current; + } + + /** Returns the child with the specified name. + * @throws IllegalArgumentException if the child was not found. */ + public JsonValue require (String name) { + JsonValue current = child; + while (current != null && !current.name.equalsIgnoreCase(name)) + current = current.next; + if (current == null) throw new IllegalArgumentException("Child not found with name: " + name); + return current; + } + + /** Removes the child with the specified name. + * @return May be null. */ + public JsonValue remove (int index) { + JsonValue child = get(index); + if (child == null) return null; + if (child.prev == null) { + this.child = child.next; + if (this.child != null) this.child.prev = null; + } else { + child.prev.next = child.next; + if (child.next != null) child.next.prev = child.prev; + } + return child; + } + + /** Removes the child with the specified name. + * @return May be null. */ + public JsonValue remove (String name) { + JsonValue child = get(name); + if (child == null) return null; + if (child.prev == null) { + this.child = child.next; + if (this.child != null) this.child.prev = null; + } else { + child.prev.next = child.next; + if (child.next != null) child.next.prev = child.prev; + } + return child; + } + + /** Returns this number of children in the array or object. */ + public int size () { + JsonValue current = child; + int size = 0; + while (current != null) { + size++; + current = current.next; + } + return size; + } + + /** Returns this value as a string. + * @return May be null if this value is null. + * @throws IllegalStateException if this an array or object. */ + public String asString () { + if (stringValue != null) return stringValue; + if (doubleValue != null) { + if (doubleValue == longValue) return Long.toString(longValue); + return Double.toString(doubleValue); + } + if (booleanValue != null) return Boolean.toString(booleanValue); + if (type == ValueType.nullValue) return null; + throw new IllegalStateException("Value cannot be converted to string: " + type); + } + + /** Returns this value as a float. + * @throws IllegalStateException if this an array or object. */ + public float asFloat () { + if (doubleValue != null) return doubleValue.floatValue(); + if (stringValue != null) { + try { + return Float.parseFloat(stringValue); + } catch (NumberFormatException ignored) { + } + } + if (booleanValue != null) return booleanValue ? 1 : 0; + throw new IllegalStateException("Value cannot be converted to float: " + type); + } + + /** Returns this value as a double. + * @throws IllegalStateException if this an array or object. */ + public double asDouble () { + if (doubleValue != null) return doubleValue; + if (stringValue != null) { + try { + return Double.parseDouble(stringValue); + } catch (NumberFormatException ignored) { + } + } + if (booleanValue != null) return booleanValue ? 1 : 0; + throw new IllegalStateException("Value cannot be converted to double: " + type); + } + + /** Returns this value as a long. + * @throws IllegalStateException if this an array or object. */ + public long asLong () { + if (doubleValue != null) return longValue; + if (stringValue != null) { + try { + return Long.parseLong(stringValue); + } catch (NumberFormatException ignored) { + } + } + if (booleanValue != null) return booleanValue ? 1 : 0; + throw new IllegalStateException("Value cannot be converted to long: " + type); + } + + /** Returns this value as an int. + * @throws IllegalStateException if this an array or object. */ + public int asInt () { + if (doubleValue != null) return (int)longValue; + if (stringValue != null) { + try { + return Integer.parseInt(stringValue); + } catch (NumberFormatException ignored) { + } + } + if (booleanValue != null) return booleanValue ? 1 : 0; + throw new IllegalStateException("Value cannot be converted to int: " + type); + } + + /** Returns this value as a boolean. + * @throws IllegalStateException if this an array or object. */ + public boolean asBoolean () { + if (booleanValue != null) return booleanValue; + if (doubleValue != null) return longValue == 0; + if (stringValue != null) return stringValue.equalsIgnoreCase("true"); + throw new IllegalStateException("Value cannot be converted to boolean: " + type); + } + + /** Finds the child with the specified name and returns its first child. + * @return May be null. */ + public JsonValue getChild (String name) { + JsonValue child = get(name); + return child == null ? null : child.child; + } + + /** Finds the child with the specified name and returns it as a string. Returns defaultValue if not found. + * @param defaultValue May be null. */ + public String getString (String name, String defaultValue) { + JsonValue child = get(name); + return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asString(); + } + + /** Finds the child with the specified name and returns it as a float. Returns defaultValue if not found. */ + public float getFloat (String name, float defaultValue) { + JsonValue child = get(name); + return (child == null || !child.isValue()) ? defaultValue : child.asFloat(); + } + + /** Finds the child with the specified name and returns it as a double. Returns defaultValue if not found. */ + public double getDouble (String name, double defaultValue) { + JsonValue child = get(name); + return (child == null || !child.isValue()) ? defaultValue : child.asDouble(); + } + + /** Finds the child with the specified name and returns it as a long. Returns defaultValue if not found. */ + public long getLong (String name, long defaultValue) { + JsonValue child = get(name); + return (child == null || !child.isValue()) ? defaultValue : child.asLong(); + } + + /** Finds the child with the specified name and returns it as an int. Returns defaultValue if not found. */ + public int getInt (String name, int defaultValue) { + JsonValue child = get(name); + return (child == null || !child.isValue()) ? defaultValue : child.asInt(); + } + + /** Finds the child with the specified name and returns it as a boolean. Returns defaultValue if not found. */ + public boolean getBoolean (String name, boolean defaultValue) { + JsonValue child = get(name); + return (child == null || !child.isValue()) ? defaultValue : child.asBoolean(); + } + + /** Finds the child with the specified name and returns it as a string. + * @throws IllegalArgumentException if the child was not found. */ + public String getString (String name) { + JsonValue child = get(name); + if (child == null) throw new IllegalArgumentException("Named value not found: " + name); + return child.asString(); + } + + /** Finds the child with the specified name and returns it as a float. + * @throws IllegalArgumentException if the child was not found. */ + public float getFloat (String name) { + JsonValue child = get(name); + if (child == null) throw new IllegalArgumentException("Named value not found: " + name); + return child.asFloat(); + } + + /** Finds the child with the specified name and returns it as a double. + * @throws IllegalArgumentException if the child was not found. */ + public double getDouble (String name) { + JsonValue child = get(name); + if (child == null) throw new IllegalArgumentException("Named value not found: " + name); + return child.asDouble(); + } + + /** Finds the child with the specified name and returns it as a long. + * @throws IllegalArgumentException if the child was not found. */ + public long getLong (String name) { + JsonValue child = get(name); + if (child == null) throw new IllegalArgumentException("Named value not found: " + name); + return child.asLong(); + } + + /** Finds the child with the specified name and returns it as an int. + * @throws IllegalArgumentException if the child was not found. */ + public int getInt (String name) { + JsonValue child = get(name); + if (child == null) throw new IllegalArgumentException("Named value not found: " + name); + return child.asInt(); + } + + /** Finds the child with the specified name and returns it as a boolean. + * @throws IllegalArgumentException if the child was not found. */ + public boolean getBoolean (String name) { + JsonValue child = get(name); + if (child == null) throw new IllegalArgumentException("Named value not found: " + name); + return child.asBoolean(); + } + + /** Finds the child with the specified index and returns it as a string. + * @throws IllegalArgumentException if the child was not found. */ + public String getString (int index) { + JsonValue child = get(index); + if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name); + return child.asString(); + } + + /** Finds the child with the specified index and returns it as a float. + * @throws IllegalArgumentException if the child was not found. */ + public float getFloat (int index) { + JsonValue child = get(index); + if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name); + return child.asFloat(); + } + + /** Finds the child with the specified index and returns it as a double. + * @throws IllegalArgumentException if the child was not found. */ + public double getDouble (int index) { + JsonValue child = get(index); + if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name); + return child.asDouble(); + } + + /** Finds the child with the specified index and returns it as a long. + * @throws IllegalArgumentException if the child was not found. */ + public long getLong (int index) { + JsonValue child = get(index); + if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name); + return child.asLong(); + } + + /** Finds the child with the specified index and returns it as an int. + * @throws IllegalArgumentException if the child was not found. */ + public int getInt (int index) { + JsonValue child = get(index); + if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name); + return child.asInt(); + } + + /** Finds the child with the specified index and returns it as a boolean. + * @throws IllegalArgumentException if the child was not found. */ + public boolean getBoolean (int index) { + JsonValue child = get(index); + if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name); + return child.asBoolean(); + } + + public ValueType type () { + return type; + } + + public void setType (ValueType type) { + if (type == null) throw new IllegalArgumentException("type cannot be null."); + this.type = type; + } + + public boolean isArray () { + return type == ValueType.array; + } + + public boolean isObject () { + return type == ValueType.object; + } + + public boolean isString () { + return type == ValueType.stringValue; + } + + /** Returns true if this is a double or long value. */ + public boolean isNumber () { + return type == ValueType.doubleValue || type == ValueType.longValue; + } + + public boolean isDouble () { + return type == ValueType.doubleValue; + } + + public boolean isLong () { + return type == ValueType.longValue; + } + + public boolean isBoolean () { + return type == ValueType.booleanValue; + } + + public boolean isNull () { + return type == ValueType.nullValue; + } + + /** Returns true if this is not an array or object. */ + public boolean isValue () { + switch (type) { + case stringValue: + case doubleValue: + case longValue: + case booleanValue: + case nullValue: + return true; + } + return false; + } + + /** Returns the name for this object value. + * @return May be null. */ + public String name () { + return name; + } + + public void setName (String name) { + this.name = name; + } + + /** Returns the first child for this object or array. + * @return May be null. */ + public JsonValue child () { + return child; + } + + public void addChild (JsonValue newChild) { + JsonValue current = child; + if (current == null) { + child = newChild; + return; + } + while (true) { + if (current.next == null) { + current.next = newChild; + newChild.prev = current; + return; + } + current = current.next; + } + } + + /** Returns the next sibling of this value. + * @return May be null. */ + public JsonValue next () { + return next; + } + + public void setNext (JsonValue next) { + this.next = next; + } + + /** Returns the previous sibling of this value. + * @return May be null. */ + public JsonValue prev () { + return prev; + } + + public void setPrev (JsonValue prev) { + this.prev = prev; + } + + /** @param value May be null. */ + public void set (String value) { + stringValue = value; + type = value == null ? ValueType.nullValue : ValueType.stringValue; + } + + public void set (double value) { + doubleValue = value; + longValue = (long)value; + type = ValueType.doubleValue; + } + + public void set (long value) { + longValue = value; + doubleValue = (double)value; + type = ValueType.longValue; + } + + public void set (boolean value) { + booleanValue = value; + type = ValueType.booleanValue; + } + + public String toString () { + return prettyPrint(OutputType.minimal, 0); + } + + public String prettyPrint (OutputType outputType, int singleLineColumns) { + StringBuilder buffer = new StringBuilder(512); + prettyPrint(this, buffer, outputType, 0, singleLineColumns); + return buffer.toString(); + } + + private void prettyPrint (JsonValue object, StringBuilder buffer, OutputType outputType, int indent, int singleLineColumns) { + if (object.isObject()) { + if (object.child() == null) { + buffer.append("{}"); + } else { + boolean newLines = !isFlat(object); + int start = buffer.length(); + outer: + while (true) { + buffer.append(newLines ? "{\n" : "{ "); + int i = 0; + for (JsonValue child = object.child(); child != null; child = child.next()) { + if (newLines) indent(indent, buffer); + buffer.append(outputType.quoteName(child.name())); + buffer.append(": "); + prettyPrint(child, buffer, outputType, indent + 1, singleLineColumns); + if (child.next() != null) buffer.append(","); + buffer.append(newLines ? '\n' : ' '); + if (!newLines && buffer.length() - start > singleLineColumns) { + buffer.setLength(start); + newLines = true; + continue outer; + } + } + break; + } + if (newLines) indent(indent - 1, buffer); + buffer.append('}'); + } + } else if (object.isArray()) { + if (object.child() == null) { + buffer.append("[]"); + } else { + boolean newLines = !isFlat(object); + int start = buffer.length(); + outer: + while (true) { + buffer.append(newLines ? "[\n" : "[ "); + for (JsonValue child = object.child(); child != null; child = child.next()) { + if (newLines) indent(indent, buffer); + prettyPrint(child, buffer, outputType, indent + 1, singleLineColumns); + if (child.next() != null) buffer.append(","); + buffer.append(newLines ? '\n' : ' '); + if (!newLines && buffer.length() - start > singleLineColumns) { + buffer.setLength(start); + newLines = true; + continue outer; + } + } + break; + } + if (newLines) indent(indent - 1, buffer); + buffer.append(']'); + } + } else if (object.isString()) { + buffer.append(outputType.quoteValue(object.asString())); + } else if (object.isDouble()) { + double doubleValue = object.asDouble(); + long longValue = (int)doubleValue; + buffer.append(doubleValue - longValue == 0 ? longValue : object); + } else if (object.isLong()) { + buffer.append(object.asLong()); + } else if (object.isBoolean()) { + buffer.append(object.asBoolean()); + } else if (object.isNull()) { + buffer.append("null"); + } else + throw new SerializationException("Unknown object type: " + object); + } + + static private boolean isFlat (JsonValue object) { + for (JsonValue child = object.child(); child != null; child = child.next()) + if (child.isObject() || child.isArray()) return false; + return true; + } + + static private boolean isFlat (Array array) { + for (int i = 0, n = array.size; i < n; i++) { + Object value = array.get(i); + if (value instanceof ObjectMap) return false; + if (value instanceof Array) return false; + } + return true; + } + + static private void indent (int count, StringBuilder buffer) { + for (int i = 0; i < count; i++) + buffer.append('\t'); + } + + public enum ValueType { + object, array, stringValue, doubleValue, longValue, booleanValue, nullValue + } +} diff --git a/gdx/src/com/badlogic/gdx/utils/JsonWriter.java b/gdx/src/com/badlogic/gdx/utils/JsonWriter.java index e13c4a002..c82109fcc 100644 --- a/gdx/src/com/badlogic/gdx/utils/JsonWriter.java +++ b/gdx/src/com/badlogic/gdx/utils/JsonWriter.java @@ -86,6 +86,10 @@ public class JsonWriter extends Writer { } public JsonWriter value (Object value) throws IOException { + if (value instanceof Number) { + Number number = (Number)value; + if (number.doubleValue() % 1 == 0) value = number.longValue(); + } if (current != null) { if (current.array) { if (!current.needsComma) -- 2.11.0