I'm facing this issue when using Gson to serialize an object which has a class member with the same type:
https://github.com/google/gson/issues/1447
The object:
public class StructId implements Serializable {
private static final long serialVersionUID = 1L;
public String Name;
public StructType Type;
public StructId ParentId;
public StructId ChildId;
And since StructId contains ParentId/ChildId with the same type I was getting infinite loop when trying to serialize it, so what I did is:
private Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return false; //(clazz == StructId.class);
}
/**
* Custom field exclusion goes here
*/
public boolean shouldSkipField(FieldAttributes f) {
//Ignore inner StructIds to solve circular serialization
return ( f.getName().equals("ParentId") || f.getName().equals("ChildId") );
}
})
/**
* Use serializeNulls method if you want To serialize null values
* By default, Gson does not serialize null values
*/
.serializeNulls()
.create();
But this is not good enough cause I need the data inside Parent/Child and ignoring them while serializing is not a solution. How is it possible to solve it?
Related to the answer marked as Solution:
I have such a struct: - Struct1 -- Table --- Variable1
The object before serialization is:
And Json that is generated is:
As you can see, the ParentId of Table is "Struct1" but the ChildId of "Struct1" is empty and it should be "Table"
B.R.
I think using ExclusionStrategy
is not the right approach to solve this problem.
I would rather suggest to use JsonSerializer
and JsonDeserializer
customized for your StructId
class.
(May be an approach using TypeAdapter
would be even better,
but I didn't have enough Gson experience do get this working.)
So you would create your Gson
instance by:
Gson gson = new GsonBuilder()
.registerTypeAdapter(StructId.class, new StructIdSerializer())
.registerTypeAdapter(StructId.class, new StructIdDeserializer())
.setPrettyPrinting()
.create();
The StructIdSerializer
class below is responsible for converting a StructId
to JSON.
It converts its properties Name
, Type
and ChildId
to JSON.
Note that it does not convert the property ParentId
to JSON,
because doing that would produce infinite recursion.
public class StructIdSerializer implements JsonSerializer<StructId> {
@Override
public JsonElement serialize(StructId src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("Name", src.Name);
jsonObject.add("Type", context.serialize(src.Type));
jsonObject.add("ChildId", context.serialize(src.ChildId)); // recursion!
return jsonObject;
}
}
The StructIdDeserializer
class below is responsible for converting JSON to a StructId
.
It converts the JSON properties Name
, Type
and ChildId
to corresponding Java fields in StructId
.
Note that the ParentId
Java field is reconstructed from the JSON nesting structure,
because it is not directly contained as a JSON property.
public class StructIdDeserializer implements JsonDeserializer<StructId> {
@Override
public StructId deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
StructId id = new StructId();
id.Name = json.getAsJsonObject().get("Name").getAsString();
id.Type = context.deserialize(json.getAsJsonObject().get("Type"), StructType.class);
JsonElement childJson = json.getAsJsonObject().get("ChildId");
if (childJson != null) {
id.ChildId = context.deserialize(childJson, StructId.class); // recursion!
id.ChildId.ParentId = id;
}
return id;
}
}
I tested the code above with this JSON input example
{
"Name": "John",
"Type": "A",
"ChildId": {
"Name": "Jane",
"Type": "B",
"ChildId": {
"Name": "Joe",
"Type": "A"
}
}
}
by deserializing it with
StructId root = gson.fromJson(new FileReader("example.json"), StructId.class);
,
then by serializing that with
System.out.println(gson.toJson(root));
and got the original JSON again.