Skip to content

Commit 72f2a13

Browse files
authored
Error serializing multiple JSON fields named 'map' with Gson (#1070)
* GAM: fix for the error: Class com.genexus.json.JSONObjectWrapper declares multiple JSON fields named 'map'; conflict is caused by fields com.genexus.json.JSONObjectWrapper#map and org.json.JSONObject#map * GAM: do not print the decimal point if it is a whole number * GAM: serialize null values * GAM: fix for multiple JSON fields named 'map'
1 parent fe56524 commit 72f2a13

File tree

1 file changed

+89
-37
lines changed

1 file changed

+89
-37
lines changed
Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.genexus.gam.utils;
22

33
import com.genexus.GxUserType;
4-
import com.google.gson.Gson;
5-
import com.google.gson.GsonBuilder;
6-
import com.google.gson.JsonParser;
7-
import com.google.gson.JsonElement;
8-
import com.google.gson.JsonPrimitive;
9-
import com.google.gson.JsonSyntaxException;
4+
import com.google.gson.*;
105
import com.google.gson.reflect.TypeToken;
6+
import com.google.gson.stream.JsonReader;
7+
import com.google.gson.stream.JsonWriter;
118

9+
import java.io.IOException;
1210
import java.lang.reflect.Type;
1311
import java.util.LinkedHashMap;
1412
import java.util.List;
@@ -23,7 +21,42 @@ public class Dictionary {
2321

2422
public Dictionary() {
2523
this.userMap = new LinkedHashMap<>();
26-
this.gson = new GsonBuilder().serializeNulls().create();
24+
this.gson = new GsonBuilder().serializeNulls()
25+
.addSerializationExclusionStrategy(new ExclusionStrategy() {
26+
@Override
27+
public boolean shouldSkipField(FieldAttributes f) {
28+
// exclude only the field 'map' inherited from org.json.JSONObject
29+
return f.getName().equals("map") &&
30+
f.getDeclaringClass() == org.json.JSONObject.class;
31+
}
32+
33+
@Override
34+
public boolean shouldSkipClass(Class<?> clazz) {
35+
return false;
36+
}
37+
})
38+
.registerTypeHierarchyAdapter(Number.class, new TypeAdapter<Number>() {
39+
@Override
40+
public void write(JsonWriter out, Number value) throws IOException {
41+
if (value == null) {
42+
out.nullValue();
43+
return;
44+
}
45+
46+
double d = value.doubleValue();
47+
if (d == Math.rint(d)) {
48+
out.value(value.longValue()); // 1.0 → 1
49+
} else {
50+
out.value(d); // 1.5 → 1.5
51+
}
52+
}
53+
54+
@Override
55+
public Number read(JsonReader in) throws IOException {
56+
return in.nextDouble();
57+
}
58+
})
59+
.create();
2760
}
2861

2962
public Object get(String key) {
@@ -54,40 +87,59 @@ private Map<String, Object> jsonStringToMap(String jsonString) {
5487
return this.gson.fromJson(jsonString, type);
5588
}
5689

57-
private void objectToMap(String key, Object value) {
58-
if (value == null) {
59-
this.userMap.put(key, null);
60-
} else if (value instanceof Number || value instanceof Boolean || value instanceof Map || value instanceof List) {
61-
this.userMap.put(key, value);
62-
} else if (value instanceof GxUserType) {
63-
this.userMap.put(key, jsonStringToMap(((GxUserType) value).toJSonString()));
64-
} else if (value instanceof String) {
65-
String str = (String) value;
66-
67-
// Try to parse as JSON
90+
private Object deepConvert(Object value) {
91+
if (value == null) return null;
92+
93+
if (value instanceof Number || value instanceof Boolean) {
94+
return value;
95+
}
96+
97+
if (value instanceof String) {
6898
try {
69-
JsonElement parsed = JsonParser.parseString(str);
99+
JsonElement parsed = JsonParser.parseString((String) value);
70100
if (parsed.isJsonObject()) {
71-
this.userMap.put(key, this.gson.fromJson(parsed, Map.class));
72-
} else if (parsed.isJsonArray()) {
73-
this.userMap.put(key, this.gson.fromJson(parsed, List.class));
74-
} else if (parsed.isJsonPrimitive()) {
75-
JsonPrimitive primitive = parsed.getAsJsonPrimitive();
76-
if (primitive.isBoolean()) {
77-
this.userMap.put(key, primitive.getAsBoolean());
78-
} else if (primitive.isNumber()) {
79-
this.userMap.put(key, primitive.getAsNumber());
80-
} else if (primitive.isString()) {
81-
this.userMap.put(key, primitive.getAsString());
82-
}
101+
return gson.fromJson(parsed, Map.class);
83102
}
84-
} catch (JsonSyntaxException e) {
85-
// Invalid JSON: it is left as string
86-
this.userMap.put(key, str);
103+
if (parsed.isJsonArray()) {
104+
return gson.fromJson(parsed, List.class);
105+
}
106+
return ((JsonPrimitive) parsed).getAsString();
107+
} catch (Exception e) {
108+
return value;
87109
}
88-
} else {
89-
// Any other object: it is converted to string
90-
this.userMap.put(key, value.toString());
91110
}
111+
112+
// SDT => Map
113+
if (value instanceof GxUserType) {
114+
String s = ((GxUserType) value).toJSonString();
115+
return jsonStringToMap(s);
116+
}
117+
118+
// Map => deep convert values
119+
if (value instanceof Map) {
120+
Map<String,Object> newMap = new LinkedHashMap<>();
121+
((Map<?,?>) value).forEach((k,v) -> newMap.put(k.toString(), deepConvert(v)));
122+
return newMap;
123+
}
124+
125+
// List => deep convert each element
126+
if (value instanceof List) {
127+
List<Object> newList = new java.util.ArrayList<>();
128+
for (Object o : (List<?>) value) newList.add(deepConvert(o));
129+
return newList;
130+
}
131+
132+
// Catch: JSONObjectWrapper
133+
if (value.getClass().getName().contains("JSONObjectWrapper") ||
134+
value instanceof org.json.JSONObject) {
135+
// Convert org.json to Map
136+
return jsonStringToMap(value.toString());
137+
}
138+
139+
return value.toString();
140+
}
141+
142+
private void objectToMap(String key, Object value) {
143+
this.userMap.put(key, deepConvert(value));
92144
}
93145
}

0 commit comments

Comments
 (0)