diff --git a/Troubleshooting.md b/Troubleshooting.md index 29835e5b21..3bc40135f8 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -85,7 +85,7 @@ If you cannot switch the classes you are using, see the library-specific solutio this.jsonElementAdapter = jsonElementAdapter; } - protected abstract T readJsonOrgValue(String json); + protected abstract T readJsonOrgValue(String json) throws JSONException; @Override public T read(JsonReader in) throws IOException { @@ -100,7 +100,13 @@ If you cannot switch the classes you are using, see the library-specific solutio // However, unlike JSONObject this will not prevent duplicate member names JsonElement jsonElement = jsonElementAdapter.read(in); String json = jsonElementAdapter.toJson(jsonElement); - return readJsonOrgValue(json); + try { + return readJsonOrgValue(json); + } + // For Android this is a checked exception; for the latest JSON-java artifacts it isn't anymore + catch (JSONException e) { + throw new RuntimeException(e); + } } @Override @@ -132,14 +138,14 @@ If you cannot switch the classes you are using, see the library-specific solutio if (rawType == JSONArray.class) { adapter = new JsonOrgAdapter(jsonElementAdapter) { @Override - protected JSONArray readJsonOrgValue(String json) { + protected JSONArray readJsonOrgValue(String json) throws JSONException { return new JSONArray(json); } }; } else { adapter = new JsonOrgAdapter(jsonElementAdapter) { @Override - protected JSONObject readJsonOrgValue(String json) { + protected JSONObject readJsonOrgValue(String json) throws JSONException { return new JSONObject(json); } }; @@ -177,7 +183,7 @@ If you cannot switch the classes you are using, see the library-specific solutio this.wrappedTypeAdapter = wrappedTypeAdapter; } - protected abstract T createJsonOrgValue(W wrapped); + protected abstract T createJsonOrgValue(W wrapped) throws JSONException; @Override public T read(JsonReader in) throws IOException { @@ -191,7 +197,14 @@ If you cannot switch the classes you are using, see the library-specific solutio if (!name.equals(fieldName)) { throw new IllegalArgumentException("Unexpected name '" + name + "', expected '" + fieldName + "' at " + in.getPath()); } - T value = createJsonOrgValue(wrappedTypeAdapter.read(in)); + T value; + try { + value = createJsonOrgValue(wrappedTypeAdapter.read(in)); + } + // For Android this is a checked exception; for the latest JSON-java artifacts it isn't anymore + catch (JSONException e) { + throw new RuntimeException(e); + } in.endObject(); return value; @@ -243,11 +256,13 @@ If you cannot switch the classes you are using, see the library-specific solutio TypeAdapter> wrappedAdapter = gson.getAdapter(new TypeToken> () {}); adapter = new JsonOrgBackwardCompatibleAdapter, JSONArray>("myArrayList", wrappedAdapter) { @Override - protected JSONArray createJsonOrgValue(List wrapped) { - JSONArray jsonArray = new JSONArray(wrapped.size()); - // Unlike JSONArray(Collection) constructor, putAll does not wrap elements and is therefore closer + protected JSONArray createJsonOrgValue(List wrapped) throws JSONException { + JSONArray jsonArray = new JSONArray(); + // Unlike JSONArray(Collection) constructor, `put` does not wrap elements and is therefore closer // to original Gson reflection-based behavior - jsonArray.putAll(wrapped); + for (Object element : wrapped) { + jsonArray.put(element); + } return jsonArray; } @@ -256,7 +271,9 @@ If you cannot switch the classes you are using, see the library-specific solutio protected List getWrapped(JSONArray jsonArray) { // Cannot use JSONArray.toList() because that converts elements List list = new ArrayList<>(jsonArray.length()); - for (Object element : jsonArray) { + for (int i = 0; i < jsonArray.length(); i++) { + // Use opt(int) because get(int) cannot handle null values + Object element = jsonArray.opt(i); list.add(element); } @@ -267,7 +284,7 @@ If you cannot switch the classes you are using, see the library-specific solutio TypeAdapter> wrappedAdapter = gson.getAdapter(new TypeToken> () {}); adapter = new JsonOrgBackwardCompatibleAdapter, JSONObject>("map", wrappedAdapter) { @Override - protected JSONObject createJsonOrgValue(Map map) { + protected JSONObject createJsonOrgValue(Map map) throws JSONException { // JSONObject(Map) constructor wraps elements, so instead put elements separately to be closer // to original Gson reflection-based behavior JSONObject jsonObject = new JSONObject(); @@ -282,7 +299,10 @@ If you cannot switch the classes you are using, see the library-specific solutio protected Map getWrapped(JSONObject jsonObject) { // Cannot use JSONObject.toMap() because that converts elements Map map = new LinkedHashMap<>(jsonObject.length()); - for (String name : jsonObject.keySet()) { + @SuppressWarnings("unchecked") // Old JSON-java versions return just `Iterator` instead of `Iterator` + Iterator names = jsonObject.keys(); + while (names.hasNext()) { + String name = names.next(); // Use opt(String) because get(String) cannot handle null values // Most likely null values cannot occur normally though; they would be JSONObject.NULL map.put(name, jsonObject.opt(name)); diff --git a/gson/pom.xml b/gson/pom.xml index 4e4708c0d9..cd21541c19 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -53,7 +53,9 @@ org.json json - 20230618 + + 20090211 test diff --git a/gson/src/test/java/com/google/gson/functional/JsonOrgInteropTest.java b/gson/src/test/java/com/google/gson/functional/JsonOrgInteropTest.java index bc0cc943a8..567408f5ea 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonOrgInteropTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonOrgInteropTest.java @@ -19,17 +19,18 @@ import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import java.io.IOException; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.checkerframework.checker.nullness.qual.Nullable; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.json.JSONString; import org.junit.Test; @@ -212,7 +213,7 @@ public JsonOrgAdapter(TypeAdapter jsonElementAdapter) { this.jsonElementAdapter = jsonElementAdapter; } - protected abstract T readJsonOrgValue(String json); + protected abstract T readJsonOrgValue(String json) throws JSONException; @Override public T read(JsonReader in) throws IOException { @@ -227,7 +228,13 @@ public T read(JsonReader in) throws IOException { // However, unlike JSONObject this will not prevent duplicate member names JsonElement jsonElement = jsonElementAdapter.read(in); String json = jsonElementAdapter.toJson(jsonElement); - return readJsonOrgValue(json); + try { + return readJsonOrgValue(json); + } + // For Android this is a checked exception; for the latest JSON-java artifacts it isn't anymore + catch (JSONException e) { + throw new RuntimeException(e); + } } @Override @@ -259,14 +266,14 @@ public TypeAdapter create(Gson gson, TypeToken type) { if (rawType == JSONArray.class) { adapter = new JsonOrgAdapter(jsonElementAdapter) { @Override - protected JSONArray readJsonOrgValue(String json) { + protected JSONArray readJsonOrgValue(String json) throws JSONException { return new JSONArray(json); } }; } else { adapter = new JsonOrgAdapter(jsonElementAdapter) { @Override - protected JSONObject readJsonOrgValue(String json) { + protected JSONObject readJsonOrgValue(String json) throws JSONException { return new JSONObject(json); } }; @@ -286,7 +293,7 @@ protected JSONObject readJsonOrgValue(String json) { * as expected. */ @Test - public void testCustomAdapters() { + public void testCustomAdapters() throws JSONException { Gson gson = new GsonBuilder() .serializeNulls() .registerTypeAdapterFactory(new JsonOrgAdapterFactory()) @@ -297,7 +304,7 @@ public void testCustomAdapters() { JSONObject.NULL, new CustomClass(), new CustomJsonStringClass(), - new BigDecimal("123.4"), + 123.4, true, new JSONObject(Collections.singletonMap("key", 1)), new JSONArray(Arrays.asList(2)), @@ -306,14 +313,14 @@ public void testCustomAdapters() { new boolean[] {false} )); assertThat(gson.toJson(array)).isEqualTo( - "[null,null,{},\"custom\",123.4,true,{\"key\":1},[2],{\"key\":3},[4],[false]]"); + "[null,null,\"custom-toString\",\"custom\",123.4,true,{\"key\":1},[2],{\"key\":3},[4],[false]]"); assertThat(gson.toJson(null, JSONArray.class)).isEqualTo("null"); JSONObject object = new JSONObject(); object.put("1", JSONObject.NULL); object.put("2", new CustomClass()); object.put("3", new CustomJsonStringClass()); - object.put("4", new BigDecimal("123.4")); + object.put("4", 123.4); object.put("5", true); object.put("6", new JSONObject(Collections.singletonMap("key", 1))); object.put("7", new JSONArray(Arrays.asList(2))); @@ -369,7 +376,7 @@ public JsonOrgBackwardCompatibleAdapter(String fieldName, TypeAdapter wrapped this.wrappedTypeAdapter = wrappedTypeAdapter; } - protected abstract T createJsonOrgValue(W wrapped); + protected abstract T createJsonOrgValue(W wrapped) throws JSONException; @Override public T read(JsonReader in) throws IOException { @@ -383,7 +390,14 @@ public T read(JsonReader in) throws IOException { if (!name.equals(fieldName)) { throw new IllegalArgumentException("Unexpected name '" + name + "', expected '" + fieldName + "' at " + in.getPath()); } - T value = createJsonOrgValue(wrappedTypeAdapter.read(in)); + T value; + try { + value = createJsonOrgValue(wrappedTypeAdapter.read(in)); + } + // For Android this is a checked exception; for the latest JSON-java artifacts it isn't anymore + catch (JSONException e) { + throw new RuntimeException(e); + } in.endObject(); return value; @@ -435,11 +449,13 @@ public void write(JsonWriter out, T value) throws IOException { TypeAdapter> wrappedAdapter = gson.getAdapter(new TypeToken> () {}); adapter = new JsonOrgBackwardCompatibleAdapter, JSONArray>("myArrayList", wrappedAdapter) { @Override - protected JSONArray createJsonOrgValue(List wrapped) { - JSONArray jsonArray = new JSONArray(wrapped.size()); - // Unlike JSONArray(Collection) constructor, putAll does not wrap elements and is therefore closer + protected JSONArray createJsonOrgValue(List wrapped) throws JSONException { + JSONArray jsonArray = new JSONArray(); + // Unlike JSONArray(Collection) constructor, `put` does not wrap elements and is therefore closer // to original Gson reflection-based behavior - jsonArray.putAll(wrapped); + for (Object element : wrapped) { + jsonArray.put(element); + } return jsonArray; } @@ -448,7 +464,9 @@ protected JSONArray createJsonOrgValue(List wrapped) { protected List getWrapped(JSONArray jsonArray) { // Cannot use JSONArray.toList() because that converts elements List list = new ArrayList<>(jsonArray.length()); - for (Object element : jsonArray) { + for (int i = 0; i < jsonArray.length(); i++) { + // Use opt(int) because get(int) cannot handle null values + Object element = jsonArray.opt(i); list.add(element); } @@ -459,7 +477,7 @@ protected List getWrapped(JSONArray jsonArray) { TypeAdapter> wrappedAdapter = gson.getAdapter(new TypeToken> () {}); adapter = new JsonOrgBackwardCompatibleAdapter, JSONObject>("map", wrappedAdapter) { @Override - protected JSONObject createJsonOrgValue(Map map) { + protected JSONObject createJsonOrgValue(Map map) throws JSONException { // JSONObject(Map) constructor wraps elements, so instead put elements separately to be closer // to original Gson reflection-based behavior JSONObject jsonObject = new JSONObject(); @@ -474,7 +492,10 @@ protected JSONObject createJsonOrgValue(Map map) { protected Map getWrapped(JSONObject jsonObject) { // Cannot use JSONObject.toMap() because that converts elements Map map = new LinkedHashMap<>(jsonObject.length()); - for (String name : jsonObject.keySet()) { + @SuppressWarnings("unchecked") // Old JSON-java versions return just `Iterator` instead of `Iterator` + Iterator names = jsonObject.keys(); + while (names.hasNext()) { + String name = names.next(); // Use opt(String) because get(String) cannot handle null values // Most likely null values cannot occur normally though; they would be JSONObject.NULL map.put(name, jsonObject.opt(name)); @@ -501,7 +522,7 @@ protected Map getWrapped(JSONObject jsonObject) { * as expected. */ @Test - public void testCustomBackwardCompatibleAdapters() { + public void testCustomBackwardCompatibleAdapters() throws JSONException { Gson gson = new GsonBuilder() .serializeNulls() .registerTypeAdapterFactory(new JsonOrgBackwardCompatibleAdapterFactory()) @@ -510,7 +531,7 @@ public void testCustomBackwardCompatibleAdapters() { JSONArray array = new JSONArray(Arrays.asList( null, JSONObject.NULL, - new BigDecimal("123.4"), + 123.4, true, new JSONObject(Collections.singletonMap("key", 1)), new JSONArray(Arrays.asList(2)), @@ -519,12 +540,12 @@ public void testCustomBackwardCompatibleAdapters() { new boolean[] {false} )); assertThat(gson.toJson(array)).isEqualTo( - "{\"myArrayList\":[null,null,123.4,true,{\"map\":{\"key\":1}},{\"myArrayList\":[2]},{\"map\":{\"key\":3}},{\"myArrayList\":[4]},{\"myArrayList\":[false]}]}"); + "{\"myArrayList\":[null,null,123.4,true,{\"map\":{\"key\":1}},{\"myArrayList\":[2]},{\"key\":3},[4],[false]]}"); assertThat(gson.toJson(null, JSONArray.class)).isEqualTo("null"); JSONObject object = new JSONObject(); object.put("1", JSONObject.NULL); - object.put("2", new BigDecimal("123.4")); + object.put("2", 123.4); object.put("3", true); object.put("4", new JSONObject(Collections.singletonMap("key", 1))); object.put("5", new JSONArray(Arrays.asList(2)));