Reputation: 60211
I have a class MySimpleObject that have various member fields. Given a json, it will populate the field accordingly. However if the json is stated as "nil", I plan to set it to null instead of string "nil".
The below example should result is an MySimpleObject with null for all it's fields, and a 0 length list of subItemList
. myObj1
should be equal to myObj2
.
@Test
public void myTestFunction() {
String myJson1 = "{\"item1\":\"nil\",\"item2\":\"nil\",\"subItemList\":[{\"subItem1\":\"nil\",\"subItem2\":\"nil\"}]}";
String myJson2 = "{\"subItemList\":[]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken<List<MySubItems>>(){ }.getType(), new MyOwnListDeserializer());
gsonBuilder.registerTypeAdapter(String.class, new MyOwnStringDeserializer());
Gson gson = gsonBuilder.create();
MySimpleObject myObj1 = gson.fromJson(myJson1, MySimpleObject.class);
MySimpleObject myObj2 = gson.fromJson(myJson2, MySimpleObject.class);
assertThat(myObj1.equals((myObj2))).isTrue();
}
class MySimpleObject implements Serializable {
String item1 = null;
String item2 = null;
List<MySubItems> subItemList;
@Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((item1 == null)? 0 :item1.hashCode());
hash = 31*hash + ((item2 == null)? 0 :item2.hashCode());
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MySimpleObject) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
class MySubItems implements Serializable {
String subItem1 = null;
String subItem2 = null;
@Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((subItem1 == null)? 0 :subItem1.hashCode());
hash = 31*hash + ((subItem2 == null)? 0 :subItem2.hashCode());
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MySubItems) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
How to write the custom serializer without having to loop through each jsonObject and check for "nil" to set to null?
Upvotes: 0
Views: 473
Reputation: 60211
I manage to come up with some custom deserialization that do the job as below.
@Test
public void myTestFunction() {
String myJson1 = "{\"item1\":\"nil\",\"item2\":\"nil\",\"subItemList\":[{\"subItem1\":\"nil\",\"subItem2\":\"nil\"}]}";
String myJson2 = "{\"subItemList\":[]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken<List<MySubItems>>(){ }.getType(), new MyOwnListDeserializer());
gsonBuilder.registerTypeAdapter(String.class, new MyOwnStringDeserializer());
Gson gson = gsonBuilder.create();
MySimpleObject myObj1 = gson.fromJson(myJson1, MySimpleObject.class);
MySimpleObject myObj2 = gson.fromJson(myJson2, MySimpleObject.class);
assertThat(myObj1.equals((myObj2))).isTrue();
}
class MySimpleObject implements Serializable {
String item1 = null;
String item2 = null;
List<MySubItems> subItemList;
@Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((item1 == null)? 0 :item1.hashCode());
hash = 31*hash + ((item2 == null)? 0 :item2.hashCode());
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MySimpleObject) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
class MySubItems implements Serializable {
String subItem1 = null;
String subItem2 = null;
@Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((subItem1 == null)? 0 :subItem1.hashCode());
hash = 31*hash + ((subItem2 == null)? 0 :subItem2.hashCode());
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MySubItems) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
class MyOwnStringDeserializer implements JsonDeserializer<String> {
@Override
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return (json.getAsString().equals("nil"))? null : json.getAsString();
}
}
class MyOwnListDeserializer implements JsonDeserializer<List<MySubItems>> {
@Override
public List<MySubItems> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<MySubItems> list = new ArrayList<>();
for (JsonElement element : json.getAsJsonArray()) {
JsonObject subObj = element.getAsJsonObject();
MySubItems subItems = new MySubItems();
if (!subObj.get("subItem1").getAsString().equals("nil")) {
subItems.subItem1 = subObj.get("subItem1").getAsString();
}
if (!subObj.get("subItem2").getAsString().equals("nil")) {
subItems.subItem2 = subObj.get("subItem1").getAsString();
}
if (subItems.subItem1 != null || subItems.subItem2 != null) {
list.add(subItems);
}
}
return (list.size() == 0)? null : list;
}
}
I'm still not very happy that MyOwnListDeserializer
, has to manually handle subItem1 and subItem2, despite they should have the same rule as defined by MyOwnStringDeserializer
. But I just don't know how to apply MyOwnStringDeserializer
to MyOwnListDeserializer
.
Would be still open for other better answers than mine.
Update A more optimized answer could be found in https://stackoverflow.com/a/39671580/3286489
Upvotes: 0
Reputation: 9881
I looked at the Gson library and also the gson-fire project, but none of them seem to allow for a real generic (and performant) solution.
One way to go is to systematically replace "nil"
by "null"
in the json string before passing it to the gson object. It is not very clean, but it is quite performant and could work.
Here is a basic method (must be refined):
public static String convertNil( String json ){
return json.replaceAll( ":\\s*\"nil\"", ": null" );
}
Then use it like:
MySimpleObject myObj1 = gson.fromJson( convertNil( myJson1 ), MySimpleObject.class );
MySimpleObject myObj2 = gson.fromJson( convertNil( myJson2 ), MySimpleObject.class );
Upvotes: 1