|
16 | 16 |
|
17 | 17 | package com.google.firebase.database.util;
|
18 | 18 |
|
| 19 | +import com.google.gson.Gson; |
| 20 | +import com.google.gson.GsonBuilder; |
| 21 | +import com.google.gson.JsonSyntaxException; |
| 22 | +import com.google.gson.stream.JsonReader; |
| 23 | +import com.google.gson.stream.JsonToken; |
19 | 24 | import java.io.IOException;
|
| 25 | +import java.io.StringReader; |
20 | 26 | import java.util.ArrayList;
|
21 |
| -import java.util.Collection; |
22 | 27 | import java.util.HashMap;
|
23 |
| -import java.util.Iterator; |
24 | 28 | import java.util.List;
|
25 | 29 | import java.util.Map;
|
26 | 30 |
|
27 |
| -import org.json.JSONArray; |
28 |
| -import org.json.JSONException; |
29 |
| -import org.json.JSONObject; |
30 |
| -import org.json.JSONStringer; |
31 |
| -import org.json.JSONTokener; |
32 |
| - |
33 | 31 | /**
|
34 | 32 | * Helper class to convert from/to JSON strings. TODO: This class should ideally not live in
|
35 | 33 | * firebase-database-connection, but it's required by both firebase-database and
|
36 | 34 | * firebase-database-connection, so leave it here for now.
|
37 | 35 | */
|
38 | 36 | public class JsonMapper {
|
39 | 37 |
|
40 |
| - public static String serializeJson(Map<String, Object> object) throws IOException { |
41 |
| - return serializeJsonValue(object); |
42 |
| - } |
43 |
| - |
44 |
| - @SuppressWarnings("unchecked") |
45 |
| - public static String serializeJsonValue(Object object) throws IOException { |
46 |
| - if (object == null) { |
47 |
| - return "null"; |
48 |
| - } else if (object instanceof String) { |
49 |
| - return JSONObject.quote((String) object); |
50 |
| - } else if (object instanceof Number) { |
51 |
| - try { |
52 |
| - return JSONObject.numberToString((Number) object); |
53 |
| - } catch (JSONException e) { |
54 |
| - throw new IOException("Could not serialize number", e); |
55 |
| - } |
56 |
| - } else if (object instanceof Boolean) { |
57 |
| - return ((Boolean) object) ? "true" : "false"; |
58 |
| - } else { |
59 |
| - try { |
60 |
| - JSONStringer stringer = new JSONStringer(); |
61 |
| - serializeJsonValue(object, stringer); |
62 |
| - return stringer.toString(); |
63 |
| - } catch (JSONException e) { |
64 |
| - throw new IOException("Failed to serialize JSON", e); |
65 |
| - } |
66 |
| - } |
67 |
| - } |
| 38 | + private static final Gson GSON = new GsonBuilder().serializeNulls().create(); |
68 | 39 |
|
69 |
| - private static void serializeJsonValue(Object object, JSONStringer stringer) |
70 |
| - throws IOException, JSONException { |
71 |
| - if (object instanceof Map) { |
72 |
| - stringer.object(); |
73 |
| - @SuppressWarnings("unchecked") |
74 |
| - Map<String, Object> map = (Map<String, Object>) object; |
75 |
| - for (Map.Entry<String, Object> entry : map.entrySet()) { |
76 |
| - stringer.key(entry.getKey()); |
77 |
| - serializeJsonValue(entry.getValue(), stringer); |
78 |
| - } |
79 |
| - stringer.endObject(); |
80 |
| - } else if (object instanceof Collection) { |
81 |
| - Collection<?> collection = (Collection<?>) object; |
82 |
| - stringer.array(); |
83 |
| - for (Object entry : collection) { |
84 |
| - serializeJsonValue(entry, stringer); |
85 |
| - } |
86 |
| - stringer.endArray(); |
87 |
| - } else { |
88 |
| - stringer.value(object); |
| 40 | + public static String serializeJson(Object object) throws IOException { |
| 41 | + try { |
| 42 | + return GSON.toJson(object); |
| 43 | + } catch (JsonSyntaxException e) { |
| 44 | + throw new IOException(e); |
89 | 45 | }
|
90 | 46 | }
|
91 | 47 |
|
92 | 48 | public static Map<String, Object> parseJson(String json) throws IOException {
|
93 | 49 | try {
|
94 |
| - return unwrapJsonObject(new JSONObject(json)); |
95 |
| - } catch (JSONException e) { |
| 50 | + JsonReader jsonReader = new JsonReader(new StringReader(json)); |
| 51 | + return unwrapJsonObject(jsonReader); |
| 52 | + } catch (IllegalStateException | JsonSyntaxException e) { |
96 | 53 | throw new IOException(e);
|
97 | 54 | }
|
98 | 55 | }
|
99 | 56 |
|
100 | 57 | public static Object parseJsonValue(String json) throws IOException {
|
101 | 58 | try {
|
102 |
| - return unwrapJson(new JSONTokener(json).nextValue()); |
103 |
| - } catch (JSONException e) { |
| 59 | + JsonReader jsonReader = new JsonReader(new StringReader(json)); |
| 60 | + jsonReader.setLenient(true); |
| 61 | + return unwrapJson(jsonReader); |
| 62 | + } catch (IllegalStateException | JsonSyntaxException e) { |
104 | 63 | throw new IOException(e);
|
105 | 64 | }
|
106 | 65 | }
|
107 | 66 |
|
108 |
| - @SuppressWarnings("unchecked") |
109 |
| - private static Map<String, Object> unwrapJsonObject(JSONObject jsonObject) throws JSONException { |
110 |
| - Map<String, Object> map = new HashMap<>(jsonObject.length()); |
111 |
| - Iterator<String> keys = jsonObject.keys(); |
112 |
| - while (keys.hasNext()) { |
113 |
| - String key = keys.next(); |
114 |
| - map.put(key, unwrapJson(jsonObject.get(key))); |
| 67 | + private static Map<String, Object> unwrapJsonObject(JsonReader jsonReader) throws IOException { |
| 68 | + Map<String, Object> map = new HashMap<>(); |
| 69 | + jsonReader.beginObject(); |
| 70 | + while (jsonReader.peek() != JsonToken.END_OBJECT) { |
| 71 | + String key = jsonReader.nextName(); |
| 72 | + map.put(key, unwrapJson(jsonReader)); |
115 | 73 | }
|
| 74 | + jsonReader.endObject(); |
116 | 75 | return map;
|
117 | 76 | }
|
118 | 77 |
|
119 |
| - private static List<Object> unwrapJsonArray(JSONArray jsonArray) throws JSONException { |
120 |
| - List<Object> list = new ArrayList<>(jsonArray.length()); |
121 |
| - for (int i = 0; i < jsonArray.length(); i++) { |
122 |
| - list.add(unwrapJson(jsonArray.get(i))); |
| 78 | + private static List<Object> unwrapJsonArray(JsonReader jsonReader) throws IOException { |
| 79 | + List<Object> list = new ArrayList<>(); |
| 80 | + jsonReader.beginArray(); |
| 81 | + while (jsonReader.peek() != JsonToken.END_ARRAY) { |
| 82 | + list.add(unwrapJson(jsonReader)); |
123 | 83 | }
|
| 84 | + jsonReader.endArray(); |
124 | 85 | return list;
|
125 | 86 | }
|
126 | 87 |
|
127 |
| - private static Object unwrapJson(Object o) throws JSONException { |
128 |
| - if (o instanceof JSONObject) { |
129 |
| - return unwrapJsonObject((JSONObject) o); |
130 |
| - } else if (o instanceof JSONArray) { |
131 |
| - return unwrapJsonArray((JSONArray) o); |
132 |
| - } else if (o.equals(JSONObject.NULL)) { |
133 |
| - return null; |
134 |
| - } else { |
135 |
| - return o; |
| 88 | + private static Object unwrapJson(JsonReader jsonReader) throws IOException { |
| 89 | + switch (jsonReader.peek()) { |
| 90 | + case BEGIN_ARRAY: |
| 91 | + return unwrapJsonArray(jsonReader); |
| 92 | + case BEGIN_OBJECT: |
| 93 | + return unwrapJsonObject(jsonReader); |
| 94 | + case STRING: |
| 95 | + return jsonReader.nextString(); |
| 96 | + case NUMBER: |
| 97 | + String value = jsonReader.nextString(); |
| 98 | + if (value.matches("-?\\d+")) { |
| 99 | + long longValue = Long.parseLong(value); |
| 100 | + if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { |
| 101 | + return (int) longValue; |
| 102 | + } |
| 103 | + return Long.valueOf(value); |
| 104 | + } |
| 105 | + return Double.parseDouble(value); |
| 106 | + case BOOLEAN: |
| 107 | + return jsonReader.nextBoolean(); |
| 108 | + case NULL: |
| 109 | + jsonReader.nextNull(); |
| 110 | + return null; |
| 111 | + default: |
| 112 | + throw new IllegalStateException("unknown type " + jsonReader.peek()); |
136 | 113 | }
|
137 | 114 | }
|
| 115 | + |
138 | 116 | }
|
0 commit comments