Skip to content

Commit

Permalink
Switch to old JSON-java version to match Android API
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcono1234 committed Jul 26, 2023
1 parent 920366e commit ac57016
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 36 deletions.
46 changes: 33 additions & 13 deletions Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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<JSONArray>(jsonElementAdapter) {
@Override
protected JSONArray readJsonOrgValue(String json) {
protected JSONArray readJsonOrgValue(String json) throws JSONException {
return new JSONArray(json);
}
};
} else {
adapter = new JsonOrgAdapter<JSONObject>(jsonElementAdapter) {
@Override
protected JSONObject readJsonOrgValue(String json) {
protected JSONObject readJsonOrgValue(String json) throws JSONException {
return new JSONObject(json);
}
};
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -243,11 +256,13 @@ If you cannot switch the classes you are using, see the library-specific solutio
TypeAdapter<List<Object>> wrappedAdapter = gson.getAdapter(new TypeToken<List<Object>> () {});
adapter = new JsonOrgBackwardCompatibleAdapter<List<Object>, JSONArray>("myArrayList", wrappedAdapter) {
@Override
protected JSONArray createJsonOrgValue(List<Object> wrapped) {
JSONArray jsonArray = new JSONArray(wrapped.size());
// Unlike JSONArray(Collection) constructor, putAll does not wrap elements and is therefore closer
protected JSONArray createJsonOrgValue(List<Object> 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;
}
Expand All @@ -256,7 +271,9 @@ If you cannot switch the classes you are using, see the library-specific solutio
protected List<Object> getWrapped(JSONArray jsonArray) {
// Cannot use JSONArray.toList() because that converts elements
List<Object> 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);
}

Expand All @@ -267,7 +284,7 @@ If you cannot switch the classes you are using, see the library-specific solutio
TypeAdapter<Map<String, Object>> wrappedAdapter = gson.getAdapter(new TypeToken<Map<String, Object>> () {});
adapter = new JsonOrgBackwardCompatibleAdapter<Map<String, Object>, JSONObject>("map", wrappedAdapter) {
@Override
protected JSONObject createJsonOrgValue(Map<String, Object> map) {
protected JSONObject createJsonOrgValue(Map<String, Object> 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();
Expand All @@ -282,7 +299,10 @@ If you cannot switch the classes you are using, see the library-specific solutio
protected Map<String, Object> getWrapped(JSONObject jsonObject) {
// Cannot use JSONObject.toMap() because that converts elements
Map<String, Object> map = new LinkedHashMap<>(jsonObject.length());
for (String name : jsonObject.keySet()) {
@SuppressWarnings("unchecked") // Old JSON-java versions return just `Iterator` instead of `Iterator<String>`
Iterator<String> 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));
Expand Down
4 changes: 3 additions & 1 deletion gson/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230618</version>
<!-- Uses an ancient version because it matches roughly the API of the same classes from Android
See also https://stackoverflow.com/q/39564936 -->
<version>20090211</version>
<scope>test</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -212,7 +213,7 @@ public JsonOrgAdapter(TypeAdapter<JsonElement> 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 {
Expand All @@ -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
Expand Down Expand Up @@ -259,14 +266,14 @@ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (rawType == JSONArray.class) {
adapter = new JsonOrgAdapter<JSONArray>(jsonElementAdapter) {
@Override
protected JSONArray readJsonOrgValue(String json) {
protected JSONArray readJsonOrgValue(String json) throws JSONException {
return new JSONArray(json);
}
};
} else {
adapter = new JsonOrgAdapter<JSONObject>(jsonElementAdapter) {
@Override
protected JSONObject readJsonOrgValue(String json) {
protected JSONObject readJsonOrgValue(String json) throws JSONException {
return new JSONObject(json);
}
};
Expand All @@ -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())
Expand All @@ -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)),
Expand All @@ -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)));
Expand Down Expand Up @@ -369,7 +376,7 @@ public JsonOrgBackwardCompatibleAdapter(String fieldName, TypeAdapter<W> 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 {
Expand All @@ -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;
Expand Down Expand Up @@ -435,11 +449,13 @@ public void write(JsonWriter out, T value) throws IOException {
TypeAdapter<List<Object>> wrappedAdapter = gson.getAdapter(new TypeToken<List<Object>> () {});
adapter = new JsonOrgBackwardCompatibleAdapter<List<Object>, JSONArray>("myArrayList", wrappedAdapter) {
@Override
protected JSONArray createJsonOrgValue(List<Object> wrapped) {
JSONArray jsonArray = new JSONArray(wrapped.size());
// Unlike JSONArray(Collection) constructor, putAll does not wrap elements and is therefore closer
protected JSONArray createJsonOrgValue(List<Object> 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;
}
Expand All @@ -448,7 +464,9 @@ protected JSONArray createJsonOrgValue(List<Object> wrapped) {
protected List<Object> getWrapped(JSONArray jsonArray) {
// Cannot use JSONArray.toList() because that converts elements
List<Object> 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);
}

Expand All @@ -459,7 +477,7 @@ protected List<Object> getWrapped(JSONArray jsonArray) {
TypeAdapter<Map<String, Object>> wrappedAdapter = gson.getAdapter(new TypeToken<Map<String, Object>> () {});
adapter = new JsonOrgBackwardCompatibleAdapter<Map<String, Object>, JSONObject>("map", wrappedAdapter) {
@Override
protected JSONObject createJsonOrgValue(Map<String, Object> map) {
protected JSONObject createJsonOrgValue(Map<String, Object> 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();
Expand All @@ -474,7 +492,10 @@ protected JSONObject createJsonOrgValue(Map<String, Object> map) {
protected Map<String, Object> getWrapped(JSONObject jsonObject) {
// Cannot use JSONObject.toMap() because that converts elements
Map<String, Object> map = new LinkedHashMap<>(jsonObject.length());
for (String name : jsonObject.keySet()) {
@SuppressWarnings("unchecked") // Old JSON-java versions return just `Iterator` instead of `Iterator<String>`
Iterator<String> 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));
Expand All @@ -501,7 +522,7 @@ protected Map<String, Object> getWrapped(JSONObject jsonObject) {
* as expected.
*/
@Test
public void testCustomBackwardCompatibleAdapters() {
public void testCustomBackwardCompatibleAdapters() throws JSONException {
Gson gson = new GsonBuilder()
.serializeNulls()
.registerTypeAdapterFactory(new JsonOrgBackwardCompatibleAdapterFactory())
Expand All @@ -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)),
Expand All @@ -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)));
Expand Down

0 comments on commit ac57016

Please sign in to comment.