|
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