OSDN Git Service

d82a91918f663efe86f46073437f45640ee72f59
[mikumikustudio/libgdx-mikumikustudio.git] / gdx / src / com / badlogic / gdx / utils / Json.java
1 /*******************************************************************************\r
2  * Copyright 2011 See AUTHORS file.\r
3  * \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
7  * \r
8  *   http://www.apache.org/licenses/LICENSE-2.0\r
9  * \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
16 \r
17 package com.badlogic.gdx.utils;\r
18 \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
28 \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
41 \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
45 public class Json {\r
46         private static final boolean debug = false;\r
47 \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
59 \r
60         public Json () {\r
61                 outputType = OutputType.minimal;\r
62         }\r
63 \r
64         public Json (OutputType outputType) {\r
65                 this.outputType = outputType;\r
66         }\r
67 \r
68         public void setIgnoreUnknownFields (boolean ignoreUnknownFields) {\r
69                 this.ignoreUnknownFields = ignoreUnknownFields;\r
70         }\r
71 \r
72         public void setOutputType (OutputType outputType) {\r
73                 this.outputType = outputType;\r
74         }\r
75 \r
76         public void addClassTag (String tag, Class type) {\r
77                 tagToClass.put(tag, type);\r
78                 classToTag.put(type, tag);\r
79         }\r
80 \r
81         public Class getClass (String tag) {\r
82                 Class type = tagToClass.get(tag);\r
83                 if (type != null) return type;\r
84                 try {\r
85                         return ClassReflection.forName(tag);\r
86                 } catch (ReflectionException ex) {\r
87                         throw new SerializationException(ex);\r
88                 }\r
89         }\r
90 \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
95         }\r
96 \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
101         }\r
102 \r
103         public void setDefaultSerializer (Serializer defaultSerializer) {\r
104                 this.defaultSerializer = defaultSerializer;\r
105         }\r
106 \r
107         public <T> void setSerializer (Class<T> type, Serializer<T> serializer) {\r
108                 classToSerializer.put(type, serializer);\r
109         }\r
110 \r
111         public <T> Serializer<T> getSerializer (Class<T> type) {\r
112                 return classToSerializer.get(type);\r
113         }\r
114 \r
115         public void setUsePrototypes (boolean usePrototypes) {\r
116                 this.usePrototypes = usePrototypes;\r
117         }\r
118 \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
125         }\r
126 \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
133                 }\r
134 \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
138 \r
139                         if (field.isTransient()) continue;\r
140                         if (field.isStatic()) continue;\r
141                         if (field.isSynthetic()) continue;\r
142 \r
143                         if (!field.isAccessible()) {\r
144                                 try {\r
145                                         field.setAccessible(true);\r
146                                 } catch (AccessControlException ex) {\r
147                                         continue;\r
148                                 }\r
149                         }\r
150 \r
151                         nameToField.put(field.getName(), new FieldMetadata(field));\r
152                 }\r
153                 typeToFields.put(type, nameToField);\r
154                 return nameToField;\r
155         }\r
156 \r
157         public String toJson (Object object) {\r
158                 return toJson(object, object == null ? null : object.getClass(), (Class)null);\r
159         }\r
160 \r
161         public String toJson (Object object, Class knownType) {\r
162                 return toJson(object, knownType, (Class)null);\r
163         }\r
164 \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
171         }\r
172 \r
173         public void toJson (Object object, FileHandle file) {\r
174                 toJson(object, object == null ? null : object.getClass(), null, file);\r
175         }\r
176 \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
180         }\r
181 \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
186                 try {\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
191                 } finally {\r
192                         StreamUtils.closeQuietly(writer);\r
193                 }\r
194         }\r
195 \r
196         public void toJson (Object object, Writer writer) {\r
197                 toJson(object, object == null ? null : object.getClass(), null, writer);\r
198         }\r
199 \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
203         }\r
204 \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
208                 setWriter(writer);\r
209                 try {\r
210                         writeValue(object, knownType, elementType);\r
211                 } finally {\r
212                         StreamUtils.closeQuietly(this.writer);\r
213                         this.writer = null;\r
214                 }\r
215         }\r
216 \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
222         }\r
223 \r
224         public JsonWriter getWriter () {\r
225                 return writer;\r
226         }\r
227 \r
228         public void writeFields (Object object) {\r
229                 Class type = object.getClass();\r
230 \r
231                 Object[] defaultValues = getDefaultValues(type);\r
232 \r
233                 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);\r
234                 if (fields == null) fields = cacheFields(type);\r
235                 int i = 0;\r
236                 for (FieldMetadata metadata : new Values<FieldMetadata>(fields)) {\r
237                         Field field = metadata.field;\r
238                         try {\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
244                                 }\r
245 \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
253                                 throw ex;\r
254                         } catch (Exception runtimeEx) {\r
255                                 SerializationException ex = new SerializationException(runtimeEx);\r
256                                 ex.addTrace(field + " (" + type.getName() + ")");\r
257                                 throw ex;\r
258                         }\r
259                 }\r
260         }\r
261 \r
262         private Object[] getDefaultValues (Class type) {\r
263                 if (!usePrototypes) return null;\r
264                 if (classToDefaultValues.containsKey(type)) return classToDefaultValues.get(type);\r
265                 Object object;\r
266                 try {\r
267                         object = newInstance(type);\r
268                 } catch (Exception ex) {\r
269                         classToDefaultValues.put(type, null);\r
270                         return null;\r
271                 }\r
272 \r
273                 ObjectMap<String, FieldMetadata> fields = typeToFields.get(type);\r
274                 if (fields == null) fields = cacheFields(type);\r
275 \r
276                 Object[] values = new Object[fields.size];\r
277                 classToDefaultValues.put(type, values);\r
278 \r
279                 int i = 0;\r
280                 for (FieldMetadata metadata : fields.values()) {\r
281                         Field field = metadata.field;\r
282                         try {\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
288                                 throw ex;\r
289                         } catch (RuntimeException runtimeEx) {\r
290                                 SerializationException ex = new SerializationException(runtimeEx);\r
291                                 ex.addTrace(field + " (" + type.getName() + ")");\r
292                                 throw ex;\r
293                         }\r
294                 }\r
295                 return values;\r
296         }\r
297 \r
298         public void writeField (Object object, String name) {\r
299                 writeField(object, name, name, null);\r
300         }\r
301 \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
305         }\r
306 \r
307         public void writeField (Object object, String fieldName, String jsonName) {\r
308                 writeField(object, fieldName, jsonName, null);\r
309         }\r
310 \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
320                 try {\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
328                         throw ex;\r
329                 } catch (Exception runtimeEx) {\r
330                         SerializationException ex = new SerializationException(runtimeEx);\r
331                         ex.addTrace(field + " (" + type.getName() + ")");\r
332                         throw ex;\r
333                 }\r
334         }\r
335 \r
336         /** @param value May be null. */\r
337         public void writeValue (String name, Object value) {\r
338                 try {\r
339                         writer.name(name);\r
340                 } catch (IOException ex) {\r
341                         throw new SerializationException(ex);\r
342                 }\r
343                 if (value == null)\r
344                         writeValue(value, null, null);\r
345                 else\r
346                         writeValue(value, value.getClass(), null);\r
347         }\r
348 \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
352                 try {\r
353                         writer.name(name);\r
354                 } catch (IOException ex) {\r
355                         throw new SerializationException(ex);\r
356                 }\r
357                 writeValue(value, knownType, null);\r
358         }\r
359 \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
364                 try {\r
365                         writer.name(name);\r
366                 } catch (IOException ex) {\r
367                         throw new SerializationException(ex);\r
368                 }\r
369                 writeValue(value, knownType, elementType);\r
370         }\r
371 \r
372         /** @param value May be null. */\r
373         public void writeValue (Object value) {\r
374                 if (value == null)\r
375                         writeValue(value, null, null);\r
376                 else\r
377                         writeValue(value, value.getClass(), null);\r
378         }\r
379 \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
384         }\r
385 \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
390                 try {\r
391                         if (value == null) {\r
392                                 writer.value(null);\r
393                                 return;\r
394                         }\r
395 \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
400                                 return;\r
401                         }\r
402 \r
403                         Class actualType = value.getClass();\r
404 \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
410                                 writeObjectEnd();\r
411                                 return;\r
412                         }\r
413 \r
414                         if (value instanceof Serializable) {\r
415                                 writeObjectStart(actualType, knownType);\r
416                                 ((Serializable)value).write(this);\r
417                                 writeObjectEnd();\r
418                                 return;\r
419                         }\r
420 \r
421                         Serializer serializer = classToSerializer.get(actualType);\r
422                         if (serializer != null) {\r
423                                 serializer.write(this, value, knownType);\r
424                                 return;\r
425                         }\r
426 \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
431                                 writeArrayStart();\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
435                                 writeArrayEnd();\r
436                                 return;\r
437                         }\r
438 \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
443                                 writeArrayStart();\r
444                                 for (Object item : (Collection)value)\r
445                                         writeValue(item, elementType, null);\r
446                                 writeArrayEnd();\r
447                                 return;\r
448                         }\r
449 \r
450                         if (actualType.isArray()) {\r
451                                 if (elementType == null) elementType = actualType.getComponentType();\r
452                                 int length = ArrayReflection.getLength(value);\r
453                                 writeArrayStart();\r
454                                 for (int i = 0; i < length; i++)\r
455                                         writeValue(ArrayReflection.get(value, i), elementType, null);\r
456                                 writeArrayEnd();\r
457                                 return;\r
458                         }\r
459 \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
467                                 }\r
468                                 writeObjectEnd();\r
469                                 return;\r
470                         }\r
471 \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
479                                 }\r
480                                 writeObjectEnd();\r
481                                 return;\r
482                         }\r
483 \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
490                                 }\r
491                                 writeObjectEnd();\r
492                                 return;\r
493                         }\r
494 \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
501                                 }\r
502                                 writeObjectEnd();\r
503                                 return;\r
504                         }\r
505 \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
511                                         writeObjectEnd();\r
512                                 } else {\r
513                                         writer.value(value);\r
514                                 }\r
515                                 return;\r
516                         }\r
517 \r
518                         writeObjectStart(actualType, knownType);\r
519                         writeFields(value);\r
520                         writeObjectEnd();\r
521                 } catch (IOException ex) {\r
522                         throw new SerializationException(ex);\r
523                 }\r
524         }\r
525 \r
526         public void writeObjectStart (String name) {\r
527                 try {\r
528                         writer.name(name);\r
529                 } catch (IOException ex) {\r
530                         throw new SerializationException(ex);\r
531                 }\r
532                 writeObjectStart();\r
533         }\r
534 \r
535         /** @param knownType May be null if the type is unknown. */\r
536         public void writeObjectStart (String name, Class actualType, Class knownType) {\r
537                 try {\r
538                         writer.name(name);\r
539                 } catch (IOException ex) {\r
540                         throw new SerializationException(ex);\r
541                 }\r
542                 writeObjectStart(actualType, knownType);\r
543         }\r
544 \r
545         public void writeObjectStart () {\r
546                 try {\r
547                         writer.object();\r
548                 } catch (IOException ex) {\r
549                         throw new SerializationException(ex);\r
550                 }\r
551         }\r
552 \r
553         /** @param knownType May be null if the type is unknown. */\r
554         public void writeObjectStart (Class actualType, Class knownType) {\r
555                 try {\r
556                         writer.object();\r
557                 } catch (IOException ex) {\r
558                         throw new SerializationException(ex);\r
559                 }\r
560                 if (knownType == null || knownType != actualType) writeType(actualType);\r
561         }\r
562 \r
563         public void writeObjectEnd () {\r
564                 try {\r
565                         writer.pop();\r
566                 } catch (IOException ex) {\r
567                         throw new SerializationException(ex);\r
568                 }\r
569         }\r
570 \r
571         public void writeArrayStart (String name) {\r
572                 try {\r
573                         writer.name(name);\r
574                         writer.array();\r
575                 } catch (IOException ex) {\r
576                         throw new SerializationException(ex);\r
577                 }\r
578         }\r
579 \r
580         public void writeArrayStart () {\r
581                 try {\r
582                         writer.array();\r
583                 } catch (IOException ex) {\r
584                         throw new SerializationException(ex);\r
585                 }\r
586         }\r
587 \r
588         public void writeArrayEnd () {\r
589                 try {\r
590                         writer.pop();\r
591                 } catch (IOException ex) {\r
592                         throw new SerializationException(ex);\r
593                 }\r
594         }\r
595 \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
600                 try {\r
601                         writer.set(typeName, className);\r
602                 } catch (IOException ex) {\r
603                         throw new SerializationException(ex);\r
604                 }\r
605                 if (debug) System.out.println("Writing type: " + type.getName());\r
606         }\r
607 \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
612         }\r
613 \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
619         }\r
620 \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
625         }\r
626 \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
632         }\r
633 \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
637                 try {\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
641                 }\r
642         }\r
643 \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
648                 try {\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
652                 }\r
653         }\r
654 \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
659         }\r
660 \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
666         }\r
667 \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
672         }\r
673 \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
678         }\r
679 \r
680         public void readField (Object object, String name, JsonValue jsonData) {\r
681                 readField(object, name, name, null, jsonData);\r
682         }\r
683 \r
684         public void readField (Object object, String name, Class elementType, JsonValue jsonData) {\r
685                 readField(object, name, name, elementType, jsonData);\r
686         }\r
687 \r
688         public void readField (Object object, String fieldName, String jsonName, JsonValue jsonData) {\r
689                 readField(object, fieldName, jsonName, null, jsonData);\r
690         }\r
691 \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
703                 try {\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
709                         throw ex;\r
710                 } catch (RuntimeException runtimeEx) {\r
711                         SerializationException ex = new SerializationException(runtimeEx);\r
712                         ex.addTrace(field.getName() + " (" + type.getName() + ")");\r
713                         throw ex;\r
714                 }\r
715         }\r
716 \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
726                                         continue;\r
727                                 } else\r
728                                         throw new SerializationException("Field not found: " + child.name() + " (" + type.getName() + ")");\r
729                         }\r
730                         Field field = metadata.field;\r
731                         // if (entry.value == null) continue; // I don't remember what this did. :(\r
732                         try {\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
738                                 throw ex;\r
739                         } catch (RuntimeException runtimeEx) {\r
740                                 SerializationException ex = new SerializationException(runtimeEx);\r
741                                 ex.addTrace(field.getName() + " (" + type.getName() + ")");\r
742                                 throw ex;\r
743                         }\r
744                 }\r
745         }\r
746 \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
751         }\r
752 \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
759         }\r
760 \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
766         }\r
767 \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
775         }\r
776 \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
782         }\r
783 \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
788         }\r
789 \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
795 \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
800                                 try {\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
805                                 }\r
806                         }\r
807 \r
808                         Object object;\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
814                                 }\r
815 \r
816                                 Serializer serializer = classToSerializer.get(type);\r
817                                 if (serializer != null) return (T)serializer.read(this, jsonData, type);\r
818 \r
819                                 object = newInstance(type);\r
820 \r
821                                 if (object instanceof Serializable) {\r
822                                         ((Serializable)object).read(this, jsonData);\r
823                                         return (T)object;\r
824                                 }\r
825 \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
830                                         return (T)result;\r
831                                 }\r
832                         } else if (defaultSerializer != null) {\r
833                                 return (T)defaultSerializer.read(this, jsonData, type);\r
834                         } else\r
835                                 return (T)jsonData;\r
836 \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
841                                 return (T)result;\r
842                         }\r
843                         readFields(object, jsonData);\r
844                         return (T)object;\r
845                 }\r
846 \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
850                 }\r
851 \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
858                         }\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
864                         }\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
869                                 int i = 0;\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
873                         }\r
874                         throw new SerializationException("Unable to convert value to required type: " + jsonData + " (" + type.getName() + ")");\r
875                 }\r
876 \r
877                 if (jsonData.isNumber()) {\r
878                         try {\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
887                         }\r
888                         jsonData = new JsonValue(jsonData.asString());\r
889                 }\r
890 \r
891                 if (jsonData.isBoolean()) {\r
892                         try {\r
893                                 if (type == null || type == boolean.class || type == Boolean.class) return (T)(Boolean)jsonData.asBoolean();\r
894                         } catch (NumberFormatException ignored) {\r
895                         }\r
896                         jsonData = new JsonValue(jsonData.asString());\r
897                 }\r
898 \r
899                 if (jsonData.isString()) {\r
900                         String string = jsonData.asString();\r
901                         if (type == null || type == String.class) return (T)string;\r
902                         try {\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
910                         }\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
917                         }\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
920                 }\r
921 \r
922                 return null;\r
923         }\r
924 \r
925         private String convertToString (Object object) {\r
926                 if (object instanceof Class) return ((Class)object).getName();\r
927                 return String.valueOf(object);\r
928         }\r
929 \r
930         private Object newInstance (Class type) {\r
931                 try {\r
932                         return ClassReflection.newInstance(type);\r
933                 } catch (Exception ex) {\r
934                         try {\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
943                                 }\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
948                                 else\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
952                         }\r
953                         throw new SerializationException("Error constructing instance of class: " + type.getName(), ex);\r
954                 }\r
955         }\r
956 \r
957         public String prettyPrint (Object object) {\r
958                 return prettyPrint(object, 0);\r
959         }\r
960 \r
961         public String prettyPrint (String json) {\r
962                 return prettyPrint(json, 0);\r
963         }\r
964 \r
965         public String prettyPrint (Object object, int singleLineColumns) {\r
966                 return prettyPrint(toJson(object), singleLineColumns);\r
967         }\r
968 \r
969         public String prettyPrint (String json, int singleLineColumns) {\r
970                 return new JsonReader().parse(json).prettyPrint(outputType, singleLineColumns);\r
971         }\r
972 \r
973         static private class FieldMetadata {\r
974                 Field field;\r
975                 Class elementType;\r
976 \r
977                 public FieldMetadata (Field field) {\r
978                         this.field = field;\r
979                         this.elementType = field.getElementType();\r
980                 }\r
981         }\r
982 \r
983         static public interface Serializer<T> {\r
984                 public void write (Json json, T object, Class knownType);\r
985 \r
986                 public T read (Json json, JsonValue jsonData, Class type);\r
987         }\r
988 \r
989         static abstract public class ReadOnlySerializer<T> implements Serializer<T> {\r
990                 public void write (Json json, T object, Class knownType) {\r
991                 }\r
992 \r
993                 abstract public T read (Json json, JsonValue jsonData, Class type);\r
994         }\r
995 \r
996         static public interface Serializable {\r
997                 public void write (Json json);\r
998 \r
999                 public void read (Json json, JsonValue jsonData);\r
1000         }\r
1001 }\r