1 /*******************************************************************************
\r
2 * Copyright 2011 See AUTHORS file.
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
15 ******************************************************************************/
\r
17 package com.badlogic.gdx.utils;
\r
19 import com.badlogic.gdx.files.FileHandle;
\r
20 import com.badlogic.gdx.utils.JsonWriter.OutputType;
\r
21 import com.badlogic.gdx.utils.ObjectMap.Entry;
\r
22 import com.badlogic.gdx.utils.ObjectMap.Values;
\r
23 import com.badlogic.gdx.utils.reflect.ArrayReflection;
\r
24 import com.badlogic.gdx.utils.reflect.ClassReflection;
\r
25 import com.badlogic.gdx.utils.reflect.Constructor;
\r
26 import com.badlogic.gdx.utils.reflect.Field;
\r
27 import com.badlogic.gdx.utils.reflect.ReflectionException;
\r
29 import java.io.IOException;
\r
30 import java.io.InputStream;
\r
31 import java.io.Reader;
\r
32 import java.io.StringWriter;
\r
33 import java.io.Writer;
\r
34 import java.security.AccessControlException;
\r
35 import java.util.ArrayList;
\r
36 import java.util.Collection;
\r
37 import java.util.Collections;
\r
38 import java.util.HashMap;
\r
39 import java.util.List;
\r
40 import java.util.Map;
\r
42 /** Reads/writes Java objects to/from JSON, automatically. See the wiki for usage:
\r
43 * https://code.google.com/p/libgdx/wiki/JsonParsing
\r
44 * @author Nathan Sweet */
\r
46 private static final boolean debug = false;
\r
48 private JsonWriter writer;
\r
49 private String typeName = "class";
\r
50 private boolean usePrototypes = true;
\r
51 private OutputType outputType;
\r
52 private final ObjectMap<Class, ObjectMap<String, FieldMetadata>> typeToFields = new ObjectMap();
\r
53 private final ObjectMap<String, Class> tagToClass = new ObjectMap();
\r
54 private final ObjectMap<Class, String> classToTag = new ObjectMap();
\r
55 private final ObjectMap<Class, Serializer> classToSerializer = new ObjectMap();
\r
56 private final ObjectMap<Class, Object[]> classToDefaultValues = new ObjectMap();
\r
57 private Serializer defaultSerializer;
\r
58 private boolean ignoreUnknownFields;
\r
61 outputType = OutputType.minimal;
\r
64 public Json (OutputType outputType) {
\r
65 this.outputType = outputType;
\r
68 public void setIgnoreUnknownFields (boolean ignoreUnknownFields) {
\r
69 this.ignoreUnknownFields = ignoreUnknownFields;
\r
72 public void setOutputType (OutputType outputType) {
\r
73 this.outputType = outputType;
\r
76 public void addClassTag (String tag, Class type) {
\r
77 tagToClass.put(tag, type);
\r
78 classToTag.put(type, tag);
\r
81 public Class getClass (String tag) {
\r
82 Class type = tagToClass.get(tag);
\r
83 if (type != null) return type;
\r
85 return ClassReflection.forName(tag);
\r
86 } catch (ReflectionException ex) {
\r
87 throw new SerializationException(ex);
\r
91 public String getTag (Class type) {
\r
92 String tag = classToTag.get(type);
\r
93 if (tag != null) return tag;
\r
94 return type.getName();
\r
97 /** Sets the name of the JSON field to store the Java class name or class tag when required to avoid ambiguity during
\r
98 * deserialization. Set to null to never output this information, but be warned that deserialization may fail. */
\r
99 public void setTypeName (String typeName) {
\r
100 this.typeName = typeName;
\r
103 public void setDefaultSerializer (Serializer defaultSerializer) {
\r
104 this.defaultSerializer = defaultSerializer;
\r
107 public <T> void setSerializer (Class<T> type, Serializer<T> serializer) {
\r
108 classToSerializer.put(type, serializer);
\r
111 public <T> Serializer<T> getSerializer (Class<T> type) {
\r
112 return classToSerializer.get(type);
\r
115 public void setUsePrototypes (boolean usePrototypes) {
\r
116 this.usePrototypes = usePrototypes;
\r
119 public void setElementType (Class type, String fieldName, Class elementType) {
\r
120 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);
\r
121 if (fields == null) fields = cacheFields(type);
\r
122 FieldMetadata metadata = fields.get(fieldName);
\r
123 if (metadata == null) throw new SerializationException("Field not found: " + fieldName + " (" + type.getName() + ")");
\r
124 metadata.elementType = elementType;
\r
127 private ObjectMap<String, FieldMetadata> cacheFields (Class type) {
\r
128 ArrayList<Field> allFields = new ArrayList();
\r
129 Class nextClass = type;
\r
130 while (nextClass != Object.class) {
\r
131 Collections.addAll(allFields, ClassReflection.getDeclaredFields(nextClass));
\r
132 nextClass = nextClass.getSuperclass();
\r
135 ObjectMap<String, FieldMetadata> nameToField = new ObjectMap();
\r
136 for (int i = 0, n = allFields.size(); i < n; i++) {
\r
137 Field field = allFields.get(i);
\r
139 if (field.isTransient()) continue;
\r
140 if (field.isStatic()) continue;
\r
141 if (field.isSynthetic()) continue;
\r
143 if (!field.isAccessible()) {
\r
145 field.setAccessible(true);
\r
146 } catch (AccessControlException ex) {
\r
151 nameToField.put(field.getName(), new FieldMetadata(field));
\r
153 typeToFields.put(type, nameToField);
\r
154 return nameToField;
\r
157 public String toJson (Object object) {
\r
158 return toJson(object, object == null ? null : object.getClass(), (Class)null);
\r
161 public String toJson (Object object, Class knownType) {
\r
162 return toJson(object, knownType, (Class)null);
\r
165 /** @param knownType May be null if the type is unknown.
\r
166 * @param elementType May be null if the type is unknown. */
\r
167 public String toJson (Object object, Class knownType, Class elementType) {
\r
168 StringWriter buffer = new StringWriter();
\r
169 toJson(object, knownType, elementType, buffer);
\r
170 return buffer.toString();
\r
173 public void toJson (Object object, FileHandle file) {
\r
174 toJson(object, object == null ? null : object.getClass(), null, file);
\r
177 /** @param knownType May be null if the type is unknown. */
\r
178 public void toJson (Object object, Class knownType, FileHandle file) {
\r
179 toJson(object, knownType, null, file);
\r
182 /** @param knownType May be null if the type is unknown.
\r
183 * @param elementType May be null if the type is unknown. */
\r
184 public void toJson (Object object, Class knownType, Class elementType, FileHandle file) {
\r
185 Writer writer = null;
\r
187 writer = file.writer(false);
\r
188 toJson(object, knownType, elementType, writer);
\r
189 } catch (Exception ex) {
\r
190 throw new SerializationException("Error writing file: " + file, ex);
\r
192 StreamUtils.closeQuietly(writer);
\r
196 public void toJson (Object object, Writer writer) {
\r
197 toJson(object, object == null ? null : object.getClass(), null, writer);
\r
200 /** @param knownType May be null if the type is unknown. */
\r
201 public void toJson (Object object, Class knownType, Writer writer) {
\r
202 toJson(object, knownType, null, writer);
\r
205 /** @param knownType May be null if the type is unknown.
\r
206 * @param elementType May be null if the type is unknown. */
\r
207 public void toJson (Object object, Class knownType, Class elementType, Writer writer) {
\r
210 writeValue(object, knownType, elementType);
\r
212 StreamUtils.closeQuietly(this.writer);
\r
213 this.writer = null;
\r
217 /** Sets the writer where JSON output will go. This is only necessary when not using the toJson methods. */
\r
218 public void setWriter (Writer writer) {
\r
219 if (!(writer instanceof JsonWriter)) writer = new JsonWriter(writer);
\r
220 this.writer = (JsonWriter)writer;
\r
221 this.writer.setOutputType(outputType);
\r
224 public JsonWriter getWriter () {
\r
228 public void writeFields (Object object) {
\r
229 Class type = object.getClass();
\r
231 Object[] defaultValues = getDefaultValues(type);
\r
233 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);
\r
234 if (fields == null) fields = cacheFields(type);
\r
236 for (FieldMetadata metadata : new Values<FieldMetadata>(fields)) {
\r
237 Field field = metadata.field;
\r
239 Object value = field.get(object);
\r
240 if (defaultValues != null) {
\r
241 Object defaultValue = defaultValues[i++];
\r
242 if (value == null && defaultValue == null) continue;
\r
243 if (value != null && defaultValue != null && value.equals(defaultValue)) continue;
\r
246 if (debug) System.out.println("Writing field: " + field.getName() + " (" + type.getName() + ")");
\r
247 writer.name(field.getName());
\r
248 writeValue(value, field.getType(), metadata.elementType);
\r
249 } catch (ReflectionException ex) {
\r
250 throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
\r
251 } catch (SerializationException ex) {
\r
252 ex.addTrace(field + " (" + type.getName() + ")");
\r
254 } catch (Exception runtimeEx) {
\r
255 SerializationException ex = new SerializationException(runtimeEx);
\r
256 ex.addTrace(field + " (" + type.getName() + ")");
\r
262 private Object[] getDefaultValues (Class type) {
\r
263 if (!usePrototypes) return null;
\r
264 if (classToDefaultValues.containsKey(type)) return classToDefaultValues.get(type);
\r
267 object = newInstance(type);
\r
268 } catch (Exception ex) {
\r
269 classToDefaultValues.put(type, null);
\r
273 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);
\r
274 if (fields == null) fields = cacheFields(type);
\r
276 Object[] values = new Object[fields.size];
\r
277 classToDefaultValues.put(type, values);
\r
280 for (FieldMetadata metadata : fields.values()) {
\r
281 Field field = metadata.field;
\r
283 values[i++] = field.get(object);
\r
284 } catch (ReflectionException ex) {
\r
285 throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
\r
286 } catch (SerializationException ex) {
\r
287 ex.addTrace(field + " (" + type.getName() + ")");
\r
289 } catch (RuntimeException runtimeEx) {
\r
290 SerializationException ex = new SerializationException(runtimeEx);
\r
291 ex.addTrace(field + " (" + type.getName() + ")");
\r
298 public void writeField (Object object, String name) {
\r
299 writeField(object, name, name, null);
\r
302 /** @param elementType May be null if the type is unknown. */
\r
303 public void writeField (Object object, String name, Class elementType) {
\r
304 writeField(object, name, name, elementType);
\r
307 public void writeField (Object object, String fieldName, String jsonName) {
\r
308 writeField(object, fieldName, jsonName, null);
\r
311 /** @param elementType May be null if the type is unknown. */
\r
312 public void writeField (Object object, String fieldName, String jsonName, Class elementType) {
\r
313 Class type = object.getClass();
\r
314 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);
\r
315 if (fields == null) fields = cacheFields(type);
\r
316 FieldMetadata metadata = fields.get(fieldName);
\r
317 if (metadata == null) throw new SerializationException("Field not found: " + fieldName + " (" + type.getName() + ")");
\r
318 Field field = metadata.field;
\r
319 if (elementType == null) elementType = metadata.elementType;
\r
321 if (debug) System.out.println("Writing field: " + field.getName() + " (" + type.getName() + ")");
\r
322 writer.name(jsonName);
\r
323 writeValue(field.get(object), field.getType(), elementType);
\r
324 } catch (ReflectionException ex) {
\r
325 throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
\r
326 } catch (SerializationException ex) {
\r
327 ex.addTrace(field + " (" + type.getName() + ")");
\r
329 } catch (Exception runtimeEx) {
\r
330 SerializationException ex = new SerializationException(runtimeEx);
\r
331 ex.addTrace(field + " (" + type.getName() + ")");
\r
336 /** @param value May be null. */
\r
337 public void writeValue (String name, Object value) {
\r
340 } catch (IOException ex) {
\r
341 throw new SerializationException(ex);
\r
344 writeValue(value, null, null);
\r
346 writeValue(value, value.getClass(), null);
\r
349 /** @param value May be null.
\r
350 * @param knownType May be null if the type is unknown. */
\r
351 public void writeValue (String name, Object value, Class knownType) {
\r
354 } catch (IOException ex) {
\r
355 throw new SerializationException(ex);
\r
357 writeValue(value, knownType, null);
\r
360 /** @param value May be null.
\r
361 * @param knownType May be null if the type is unknown.
\r
362 * @param elementType May be null if the type is unknown. */
\r
363 public void writeValue (String name, Object value, Class knownType, Class elementType) {
\r
366 } catch (IOException ex) {
\r
367 throw new SerializationException(ex);
\r
369 writeValue(value, knownType, elementType);
\r
372 /** @param value May be null. */
\r
373 public void writeValue (Object value) {
\r
375 writeValue(value, null, null);
\r
377 writeValue(value, value.getClass(), null);
\r
380 /** @param value May be null.
\r
381 * @param knownType May be null if the type is unknown. */
\r
382 public void writeValue (Object value, Class knownType) {
\r
383 writeValue(value, knownType, null);
\r
386 /** @param value May be null.
\r
387 * @param knownType May be null if the type is unknown.
\r
388 * @param elementType May be null if the type is unknown. */
\r
389 public void writeValue (Object value, Class knownType, Class elementType) {
\r
391 if (value == null) {
\r
392 writer.value(null);
\r
396 if ((knownType != null && knownType.isPrimitive()) || knownType == String.class || knownType == Integer.class
\r
397 || knownType == Boolean.class || knownType == Float.class || knownType == Long.class || knownType == Double.class
\r
398 || knownType == Short.class || knownType == Byte.class || knownType == Character.class) {
\r
399 writer.value(value);
\r
403 Class actualType = value.getClass();
\r
405 if (actualType.isPrimitive() || actualType == String.class || actualType == Integer.class || actualType == Boolean.class
\r
406 || actualType == Float.class || actualType == Long.class || actualType == Double.class || actualType == Short.class
\r
407 || actualType == Byte.class || actualType == Character.class) {
\r
408 writeObjectStart(actualType, null);
\r
409 writeValue("value", value);
\r
414 if (value instanceof Serializable) {
\r
415 writeObjectStart(actualType, knownType);
\r
416 ((Serializable)value).write(this);
\r
421 Serializer serializer = classToSerializer.get(actualType);
\r
422 if (serializer != null) {
\r
423 serializer.write(this, value, knownType);
\r
427 if (value instanceof Array) {
\r
428 if (knownType != null && actualType != knownType && actualType != Array.class)
\r
429 throw new SerializationException("Serialization of an Array other than the known type is not supported.\n"
\r
430 + "Known type: " + knownType + "\nActual type: " + actualType);
\r
432 Array array = (Array)value;
\r
433 for (int i = 0, n = array.size; i < n; i++)
\r
434 writeValue(array.get(i), elementType, null);
\r
439 if (value instanceof Collection) {
\r
440 if (knownType != null && actualType != knownType && actualType != ArrayList.class)
\r
441 throw new SerializationException("Serialization of a Collection other than the known type is not supported.\n"
\r
442 + "Known type: " + knownType + "\nActual type: " + actualType);
\r
444 for (Object item : (Collection)value)
\r
445 writeValue(item, elementType, null);
\r
450 if (actualType.isArray()) {
\r
451 if (elementType == null) elementType = actualType.getComponentType();
\r
452 int length = ArrayReflection.getLength(value);
\r
454 for (int i = 0; i < length; i++)
\r
455 writeValue(ArrayReflection.get(value, i), elementType, null);
\r
460 if (value instanceof OrderedMap) {
\r
461 if (knownType == null) knownType = OrderedMap.class;
\r
462 writeObjectStart(actualType, knownType);
\r
463 OrderedMap map = (OrderedMap)value;
\r
464 for (Object key : map.orderedKeys()) {
\r
465 writer.name(convertToString(key));
\r
466 writeValue(map.get(key), elementType, null);
\r
472 if (value instanceof ArrayMap) {
\r
473 if (knownType == null) knownType = ArrayMap.class;
\r
474 writeObjectStart(actualType, knownType);
\r
475 ArrayMap map = (ArrayMap)value;
\r
476 for (int i = 0, n = map.size; i < n; i++) {
\r
477 writer.name(convertToString(map.keys[i]));
\r
478 writeValue(map.values[i], elementType, null);
\r
484 if (value instanceof ObjectMap) {
\r
485 if (knownType == null) knownType = OrderedMap.class;
\r
486 writeObjectStart(actualType, knownType);
\r
487 for (Entry entry : ((ObjectMap<?, ?>)value).entries()) {
\r
488 writer.name(convertToString(entry.key));
\r
489 writeValue(entry.value, elementType, null);
\r
495 if (value instanceof Map) {
\r
496 if (knownType == null) knownType = HashMap.class;
\r
497 writeObjectStart(actualType, knownType);
\r
498 for (Map.Entry entry : ((Map<?, ?>)value).entrySet()) {
\r
499 writer.name(convertToString(entry.getKey()));
\r
500 writeValue(entry.getValue(), elementType, null);
\r
506 if (ClassReflection.isAssignableFrom(Enum.class, actualType)) {
\r
507 if (knownType == null || !knownType.equals(actualType)) {
\r
508 writeObjectStart(actualType, null);
\r
509 writer.name("value");
\r
510 writer.value(value);
\r
513 writer.value(value);
\r
518 writeObjectStart(actualType, knownType);
\r
519 writeFields(value);
\r
521 } catch (IOException ex) {
\r
522 throw new SerializationException(ex);
\r
526 public void writeObjectStart (String name) {
\r
529 } catch (IOException ex) {
\r
530 throw new SerializationException(ex);
\r
532 writeObjectStart();
\r
535 /** @param knownType May be null if the type is unknown. */
\r
536 public void writeObjectStart (String name, Class actualType, Class knownType) {
\r
539 } catch (IOException ex) {
\r
540 throw new SerializationException(ex);
\r
542 writeObjectStart(actualType, knownType);
\r
545 public void writeObjectStart () {
\r
548 } catch (IOException ex) {
\r
549 throw new SerializationException(ex);
\r
553 /** @param knownType May be null if the type is unknown. */
\r
554 public void writeObjectStart (Class actualType, Class knownType) {
\r
557 } catch (IOException ex) {
\r
558 throw new SerializationException(ex);
\r
560 if (knownType == null || knownType != actualType) writeType(actualType);
\r
563 public void writeObjectEnd () {
\r
566 } catch (IOException ex) {
\r
567 throw new SerializationException(ex);
\r
571 public void writeArrayStart (String name) {
\r
575 } catch (IOException ex) {
\r
576 throw new SerializationException(ex);
\r
580 public void writeArrayStart () {
\r
583 } catch (IOException ex) {
\r
584 throw new SerializationException(ex);
\r
588 public void writeArrayEnd () {
\r
591 } catch (IOException ex) {
\r
592 throw new SerializationException(ex);
\r
596 public void writeType (Class type) {
\r
597 if (typeName == null) return;
\r
598 String className = classToTag.get(type);
\r
599 if (className == null) className = type.getName();
\r
601 writer.set(typeName, className);
\r
602 } catch (IOException ex) {
\r
603 throw new SerializationException(ex);
\r
605 if (debug) System.out.println("Writing type: " + type.getName());
\r
608 /** @param type May be null if the type is unknown.
\r
609 * @return May be null. */
\r
610 public <T> T fromJson (Class<T> type, Reader reader) {
\r
611 return (T)readValue(type, null, new JsonReader().parse(reader));
\r
614 /** @param type May be null if the type is unknown.
\r
615 * @param elementType May be null if the type is unknown.
\r
616 * @return May be null. */
\r
617 public <T> T fromJson (Class<T> type, Class elementType, Reader reader) {
\r
618 return (T)readValue(type, elementType, new JsonReader().parse(reader));
\r
621 /** @param type May be null if the type is unknown.
\r
622 * @return May be null. */
\r
623 public <T> T fromJson (Class<T> type, InputStream input) {
\r
624 return (T)readValue(type, null, new JsonReader().parse(input));
\r
627 /** @param type May be null if the type is unknown.
\r
628 * @param elementType May be null if the type is unknown.
\r
629 * @return May be null. */
\r
630 public <T> T fromJson (Class<T> type, Class elementType, InputStream input) {
\r
631 return (T)readValue(type, elementType, new JsonReader().parse(input));
\r
634 /** @param type May be null if the type is unknown.
\r
635 * @return May be null. */
\r
636 public <T> T fromJson (Class<T> type, FileHandle file) {
\r
638 return (T)readValue(type, null, new JsonReader().parse(file));
\r
639 } catch (Exception ex) {
\r
640 throw new SerializationException("Error reading file: " + file, ex);
\r
644 /** @param type May be null if the type is unknown.
\r
645 * @param elementType May be null if the type is unknown.
\r
646 * @return May be null. */
\r
647 public <T> T fromJson (Class<T> type, Class elementType, FileHandle file) {
\r
649 return (T)readValue(type, elementType, new JsonReader().parse(file));
\r
650 } catch (Exception ex) {
\r
651 throw new SerializationException("Error reading file: " + file, ex);
\r
655 /** @param type May be null if the type is unknown.
\r
656 * @return May be null. */
\r
657 public <T> T fromJson (Class<T> type, char[] data, int offset, int length) {
\r
658 return (T)readValue(type, null, new JsonReader().parse(data, offset, length));
\r
661 /** @param type May be null if the type is unknown.
\r
662 * @param elementType May be null if the type is unknown.
\r
663 * @return May be null. */
\r
664 public <T> T fromJson (Class<T> type, Class elementType, char[] data, int offset, int length) {
\r
665 return (T)readValue(type, elementType, new JsonReader().parse(data, offset, length));
\r
668 /** @param type May be null if the type is unknown.
\r
669 * @return May be null. */
\r
670 public <T> T fromJson (Class<T> type, String json) {
\r
671 return (T)readValue(type, null, new JsonReader().parse(json));
\r
674 /** @param type May be null if the type is unknown.
\r
675 * @return May be null. */
\r
676 public <T> T fromJson (Class<T> type, Class elementType, String json) {
\r
677 return (T)readValue(type, elementType, new JsonReader().parse(json));
\r
680 public void readField (Object object, String name, JsonValue jsonData) {
\r
681 readField(object, name, name, null, jsonData);
\r
684 public void readField (Object object, String name, Class elementType, JsonValue jsonData) {
\r
685 readField(object, name, name, elementType, jsonData);
\r
688 public void readField (Object object, String fieldName, String jsonName, JsonValue jsonData) {
\r
689 readField(object, fieldName, jsonName, null, jsonData);
\r
692 /** @param elementType May be null if the type is unknown. */
\r
693 public void readField (Object object, String fieldName, String jsonName, Class elementType, JsonValue jsonMap) {
\r
694 Class type = object.getClass();
\r
695 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);
\r
696 if (fields == null) fields = cacheFields(type);
\r
697 FieldMetadata metadata = fields.get(fieldName);
\r
698 if (metadata == null) throw new SerializationException("Field not found: " + fieldName + " (" + type.getName() + ")");
\r
699 Field field = metadata.field;
\r
700 JsonValue jsonValue = jsonMap.get(jsonName);
\r
701 if (jsonValue == null) return;
\r
702 if (elementType == null) elementType = metadata.elementType;
\r
704 field.set(object, readValue(field.getType(), elementType, jsonValue));
\r
705 } catch (ReflectionException ex) {
\r
706 throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
\r
707 } catch (SerializationException ex) {
\r
708 ex.addTrace(field.getName() + " (" + type.getName() + ")");
\r
710 } catch (RuntimeException runtimeEx) {
\r
711 SerializationException ex = new SerializationException(runtimeEx);
\r
712 ex.addTrace(field.getName() + " (" + type.getName() + ")");
\r
717 public void readFields (Object object, JsonValue jsonMap) {
\r
718 Class type = object.getClass();
\r
719 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);
\r
720 if (fields == null) fields = cacheFields(type);
\r
721 for (JsonValue child = jsonMap.child(); child != null; child = child.next()) {
\r
722 FieldMetadata metadata = fields.get(child.name());
\r
723 if (metadata == null) {
\r
724 if (ignoreUnknownFields) {
\r
725 if (debug) System.out.println("Ignoring unknown field: " + child.name() + " (" + type.getName() + ")");
\r
728 throw new SerializationException("Field not found: " + child.name() + " (" + type.getName() + ")");
\r
730 Field field = metadata.field;
\r
731 // if (entry.value == null) continue; // I don't remember what this did. :(
\r
733 field.set(object, readValue(field.getType(), metadata.elementType, child));
\r
734 } catch (ReflectionException ex) {
\r
735 throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
\r
736 } catch (SerializationException ex) {
\r
737 ex.addTrace(field.getName() + " (" + type.getName() + ")");
\r
739 } catch (RuntimeException runtimeEx) {
\r
740 SerializationException ex = new SerializationException(runtimeEx);
\r
741 ex.addTrace(field.getName() + " (" + type.getName() + ")");
\r
747 /** @param type May be null if the type is unknown.
\r
748 * @return May be null. */
\r
749 public <T> T readValue (String name, Class<T> type, JsonValue jsonMap) {
\r
750 return (T)readValue(type, null, jsonMap.get(name));
\r
753 /** @param type May be null if the type is unknown.
\r
754 * @return May be null. */
\r
755 public <T> T readValue (String name, Class<T> type, T defaultValue, JsonValue jsonMap) {
\r
756 JsonValue jsonValue = jsonMap.get(name);
\r
757 if (jsonValue == null) return defaultValue;
\r
758 return (T)readValue(type, null, jsonValue);
\r
761 /** @param type May be null if the type is unknown.
\r
762 * @param elementType May be null if the type is unknown.
\r
763 * @return May be null. */
\r
764 public <T> T readValue (String name, Class<T> type, Class elementType, JsonValue jsonMap) {
\r
765 return (T)readValue(type, elementType, jsonMap.get(name));
\r
768 /** @param type May be null if the type is unknown.
\r
769 * @param elementType May be null if the type is unknown.
\r
770 * @return May be null. */
\r
771 public <T> T readValue (String name, Class<T> type, Class elementType, T defaultValue, JsonValue jsonMap) {
\r
772 JsonValue jsonValue = jsonMap.get(name);
\r
773 if (jsonValue == null) return defaultValue;
\r
774 return (T)readValue(type, elementType, jsonValue);
\r
777 /** @param type May be null if the type is unknown.
\r
778 * @param elementType May be null if the type is unknown.
\r
779 * @return May be null. */
\r
780 public <T> T readValue (Class<T> type, Class elementType, T defaultValue, JsonValue jsonData) {
\r
781 return (T)readValue(type, elementType, jsonData);
\r
784 /** @param type May be null if the type is unknown.
\r
785 * @return May be null. */
\r
786 public <T> T readValue (Class<T> type, JsonValue jsonData) {
\r
787 return (T)readValue(type, null, jsonData);
\r
790 /** @param type May be null if the type is unknown.
\r
791 * @param elementType May be null if the type is unknown.
\r
792 * @return May be null. */
\r
793 public <T> T readValue (Class<T> type, Class elementType, JsonValue jsonData) {
\r
794 if (jsonData == null) return null;
\r
796 if (jsonData.isObject()) {
\r
797 String className = typeName == null ? null : jsonData.getString(typeName, null);
\r
798 if (className != null) {
\r
799 jsonData.remove(typeName);
\r
801 type = (Class<T>)ClassReflection.forName(className);
\r
802 } catch (ReflectionException ex) {
\r
803 type = tagToClass.get(className);
\r
804 if (type == null) throw new SerializationException(ex);
\r
809 if (type != null) {
\r
810 if (type == String.class || type == Integer.class || type == Boolean.class || type == Float.class
\r
811 || type == Long.class || type == Double.class || type == Short.class || type == Byte.class
\r
812 || type == Character.class || type.isEnum()) {
\r
813 return readValue("value", type, jsonData);
\r
816 Serializer serializer = classToSerializer.get(type);
\r
817 if (serializer != null) return (T)serializer.read(this, jsonData, type);
\r
819 object = newInstance(type);
\r
821 if (object instanceof Serializable) {
\r
822 ((Serializable)object).read(this, jsonData);
\r
826 if (object instanceof HashMap) {
\r
827 HashMap result = (HashMap)object;
\r
828 for (JsonValue child = jsonData.child(); child != null; child = child.next())
\r
829 result.put(child.name(), readValue(elementType, null, child));
\r
832 } else if (defaultSerializer != null) {
\r
833 return (T)defaultSerializer.read(this, jsonData, type);
\r
835 return (T)jsonData;
\r
837 if (object instanceof ObjectMap) {
\r
838 ObjectMap result = (ObjectMap)object;
\r
839 for (JsonValue child = jsonData.child(); child != null; child = child.next())
\r
840 result.put(child.name(), readValue(elementType, null, child));
\r
843 readFields(object, jsonData);
\r
847 if (type != null) {
\r
848 Serializer serializer = classToSerializer.get(type);
\r
849 if (serializer != null) return (T)serializer.read(this, jsonData, type);
\r
852 if (jsonData.isArray()) {
\r
853 if ((type == null || type == Object.class) || ClassReflection.isAssignableFrom(Array.class, type)) {
\r
854 Array newArray = (type == null || type == Object.class) ? new Array() : (Array)newInstance(type);
\r
855 for (JsonValue child = jsonData.child(); child != null; child = child.next())
\r
856 newArray.add(readValue(elementType, null, child));
\r
857 return (T)newArray;
\r
859 if (ClassReflection.isAssignableFrom(List.class, type)) {
\r
860 List newArray = type == null ? new ArrayList() : (List)newInstance(type);
\r
861 for (JsonValue child = jsonData.child(); child != null; child = child.next())
\r
862 newArray.add(readValue(elementType, null, child));
\r
863 return (T)newArray;
\r
865 if (type.isArray()) {
\r
866 Class componentType = type.getComponentType();
\r
867 if (elementType == null) elementType = componentType;
\r
868 Object newArray = ArrayReflection.newInstance(componentType, jsonData.size());
\r
870 for (JsonValue child = jsonData.child(); child != null; child = child.next())
\r
871 ArrayReflection.set(newArray, i++, readValue(elementType, null, child));
\r
872 return (T)newArray;
\r
874 throw new SerializationException("Unable to convert value to required type: " + jsonData + " (" + type.getName() + ")");
\r
877 if (jsonData.isNumber()) {
\r
879 if (type == null || type == float.class || type == Float.class) return (T)(Float)jsonData.asFloat();
\r
880 if (type == int.class || type == Integer.class) return (T)(Integer)jsonData.asInt();
\r
881 if (type == long.class || type == Long.class) return (T)(Long)jsonData.asLong();
\r
882 if (type == double.class || type == Double.class) return (T)(Double)(double)jsonData.asFloat();
\r
883 if (type == String.class) return (T)Float.toString(jsonData.asFloat());
\r
884 if (type == short.class || type == Short.class) return (T)(Short)(short)jsonData.asInt();
\r
885 if (type == byte.class || type == Byte.class) return (T)(Byte)(byte)jsonData.asInt();
\r
886 } catch (NumberFormatException ignored) {
\r
888 jsonData = new JsonValue(jsonData.asString());
\r
891 if (jsonData.isBoolean()) {
\r
893 if (type == null || type == boolean.class || type == Boolean.class) return (T)(Boolean)jsonData.asBoolean();
\r
894 } catch (NumberFormatException ignored) {
\r
896 jsonData = new JsonValue(jsonData.asString());
\r
899 if (jsonData.isString()) {
\r
900 String string = jsonData.asString();
\r
901 if (type == null || type == String.class) return (T)string;
\r
903 if (type == int.class || type == Integer.class) return (T)Integer.valueOf(string);
\r
904 if (type == float.class || type == Float.class) return (T)Float.valueOf(string);
\r
905 if (type == long.class || type == Long.class) return (T)Long.valueOf(string);
\r
906 if (type == double.class || type == Double.class) return (T)Double.valueOf(string);
\r
907 if (type == short.class || type == Short.class) return (T)Short.valueOf(string);
\r
908 if (type == byte.class || type == Byte.class) return (T)Byte.valueOf(string);
\r
909 } catch (NumberFormatException ignored) {
\r
911 if (type == boolean.class || type == Boolean.class) return (T)Boolean.valueOf(string);
\r
912 if (type == char.class || type == Character.class) return (T)(Character)string.charAt(0);
\r
913 if (ClassReflection.isAssignableFrom(Enum.class, type)) {
\r
914 Object[] constants = type.getEnumConstants();
\r
915 for (int i = 0, n = constants.length; i < n; i++)
\r
916 if (string.equals(constants[i].toString())) return (T)constants[i];
\r
918 if (type == CharSequence.class) return (T)string;
\r
919 throw new SerializationException("Unable to convert value to required type: " + jsonData + " (" + type.getName() + ")");
\r
925 private String convertToString (Object object) {
\r
926 if (object instanceof Class) return ((Class)object).getName();
\r
927 return String.valueOf(object);
\r
930 private Object newInstance (Class type) {
\r
932 return ClassReflection.newInstance(type);
\r
933 } catch (Exception ex) {
\r
935 // Try a private constructor.
\r
936 Constructor constructor = ClassReflection.getDeclaredConstructor(type);
\r
937 constructor.setAccessible(true);
\r
938 return constructor.newInstance();
\r
939 } catch (SecurityException ignored) {
\r
940 } catch (ReflectionException ignored) {
\r
941 if (type.isEnum()) {
\r
942 return type.getEnumConstants()[0];
\r
944 if (type.isArray())
\r
945 throw new SerializationException("Encountered JSON object when expected array of type: " + type.getName(), ex);
\r
946 else if (ClassReflection.isMemberClass(type) && !ClassReflection.isStaticClass(type))
\r
947 throw new SerializationException("Class cannot be created (non-static member class): " + type.getName(), ex);
\r
949 throw new SerializationException("Class cannot be created (missing no-arg constructor): " + type.getName(), ex);
\r
950 } catch (Exception privateConstructorException) {
\r
951 ex = privateConstructorException;
\r
953 throw new SerializationException("Error constructing instance of class: " + type.getName(), ex);
\r
957 public String prettyPrint (Object object) {
\r
958 return prettyPrint(object, 0);
\r
961 public String prettyPrint (String json) {
\r
962 return prettyPrint(json, 0);
\r
965 public String prettyPrint (Object object, int singleLineColumns) {
\r
966 return prettyPrint(toJson(object), singleLineColumns);
\r
969 public String prettyPrint (String json, int singleLineColumns) {
\r
970 return new JsonReader().parse(json).prettyPrint(outputType, singleLineColumns);
\r
973 static private class FieldMetadata {
\r
977 public FieldMetadata (Field field) {
\r
978 this.field = field;
\r
979 this.elementType = field.getElementType();
\r
983 static public interface Serializer<T> {
\r
984 public void write (Json json, T object, Class knownType);
\r
986 public T read (Json json, JsonValue jsonData, Class type);
\r
989 static abstract public class ReadOnlySerializer<T> implements Serializer<T> {
\r
990 public void write (Json json, T object, Class knownType) {
\r
993 abstract public T read (Json json, JsonValue jsonData, Class type);
\r
996 static public interface Serializable {
\r
997 public void write (Json json);
\r
999 public void read (Json json, JsonValue jsonData);
\r