How to solve circular reference when serializing an object which have a class member with the same type of...












1















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:
enter image description here



And Json that is generated is:
enter image description here



As you can see, the ParentId of Table is "Struct1" but the ChildId of "Struct1" is empty and it should be "Table"



B.R.










share|improve this question

























  • Please edit your question to contain your actual question.

    – ruakh
    Jan 2 at 9:23
















1















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:
enter image description here



And Json that is generated is:
enter image description here



As you can see, the ParentId of Table is "Struct1" but the ChildId of "Struct1" is empty and it should be "Table"



B.R.










share|improve this question

























  • Please edit your question to contain your actual question.

    – ruakh
    Jan 2 at 9:23














1












1








1








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:
enter image description here



And Json that is generated is:
enter image description here



As you can see, the ParentId of Table is "Struct1" but the ChildId of "Struct1" is empty and it should be "Table"



B.R.










share|improve this question
















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:
enter image description here



And Json that is generated is:
enter image description here



As you can see, the ParentId of Table is "Struct1" but the ChildId of "Struct1" is empty and it should be "Table"



B.R.







json rest serialization gson circular-reference






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 8 at 9:28







zbeedatm

















asked Jan 2 at 9:15









zbeedatmzbeedatm

829




829













  • Please edit your question to contain your actual question.

    – ruakh
    Jan 2 at 9:23



















  • Please edit your question to contain your actual question.

    – ruakh
    Jan 2 at 9:23

















Please edit your question to contain your actual question.

– ruakh
Jan 2 at 9:23





Please edit your question to contain your actual question.

– ruakh
Jan 2 at 9:23












3 Answers
3






active

oldest

votes


















1














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.






share|improve this answer


























  • Sounds good. I implemented the Serializer/Deserializer solution and I have few questions: 1) why did you use addProperty for Name and not add as value? 2) the json arriving to server controller is like this: {"++"Name"":"+"struct""} and when calling the id.Name = json.getAsJsonObject().get("Name").getAsString(); getting null

    – zbeedatm
    Jan 2 at 15:48













  • @zbeedatm 1) I used addProperty("Name", ...) because that is enough to add a JSON primitive. 2) This JSON looks weird. It would be better to add a formatted JSON example to your question instead of squeezing it into a comment. I have also added my JSON input example to the answer.

    – Thomas Fritsch
    Jan 2 at 16:22











  • These charecters apears because of: URLEncoder.encode(jsonDesttinationIdString, ENCODING). But it was working as expected before using the customized serializer... will find a away to handle it in other side by Encoder.decode maybe

    – zbeedatm
    Jan 2 at 16:30











  • Ok basically seems to be working. But without the ParentId and this data is needed too... so what do you suggest? to try the other way of TypeAdapter? maybe it's achievable there

    – zbeedatm
    Jan 2 at 16:42











  • @zbeedatm For me the ParentId was correctly set in the Java objects, and in JSON it is not neeeded. I suggest you stick to the serializer/deserializer approach and get it working. With the TypeAdapter approach you would get the same functionality, just only with better performance in case of huge JSON contents.

    – Thomas Fritsch
    Jan 2 at 16:56





















0














Just to show one way to make serialization (so I do not handle de-serialization) with TypeAdapter and ExclusionStrategy. This might not be the most beautiful implementation but it is quite generic anyway.



This solution makes use of the fact that your struct is some kind of a bi-directional linked list and given any node in that list we just need to separate the serialization of parents and children so that those are serialized just in one direction to avoid circular references.



First we need configurable ExclusionStrategy like:



public class FieldExclusionStrategy implements ExclusionStrategy {

private final List<String> skipFields;

public FieldExclusionStrategy(String... fieldNames) {
skipFields = Arrays.asList(fieldNames);
}

@Override
public boolean shouldSkipField(FieldAttributes f) {
return skipFields.contains(f.getName());
}

@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}

}


Then the TypeAdapter would be like:



public class LinkedListAdapter extends TypeAdapter<StructId> {

private static final String PARENT_ID = "ParentId";
private static final String CHILD_ID = "ChildId";
private Gson gson;

@Override
public void write(JsonWriter out, StructId value) throws IOException {
// First serialize everything but StructIds
// You could also use type based exclusion strategy
// but for brevity I use just this one
gson = new GsonBuilder()
.addSerializationExclusionStrategy(
new FieldExclusionStrategy(CHILD_ID, PARENT_ID))
.create();
JsonObject structObject = gson.toJsonTree(value).getAsJsonObject();
JsonObject structParentObject;
JsonObject structChildObject;

// If exists go through the ParentId side in one direction.
if(null!=value.ParentId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
.create();
structObject.add(PARENT_ID, gson.toJsonTree(value.ParentId));

if(null!=value.ParentId.ChildId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
.create();
structParentObject = structObject.get(PARENT_ID).getAsJsonObject();
structParentObject.add(CHILD_ID, gson.toJsonTree(value.ParentId.ChildId).getAsJsonObject());
}
}
// And also if exists go through the ChildId side in one direction.
if(null!=value.ChildId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
.create();
structObject.add(CHILD_ID, gson.toJsonTree(value.ChildId));

if(null!=value.ChildId.ParentId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
.create();

structChildObject = structObject.get(CHILD_ID).getAsJsonObject();
structChildObject.add(PARENT_ID, gson.toJsonTree(value.ChildId.ParentId).getAsJsonObject());
}
}

// Finally write the combined result out. No need to initialize gson anymore
// since just writing JsonElement
gson.toJson(structObject, out);
}

@Override
public StructId read(JsonReader in) throws IOException {
return null;
}}


Testing it:



@Slf4j
public class TestIt extends BaseGsonTest {

@Test
public void test1() {
StructId grandParent = new StructId();

StructId parent = new StructId();
grandParent.ChildId = parent;
parent.ParentId = grandParent;

StructId child = new StructId();
parent.ChildId = child;
child.ParentId = parent;

Gson gson = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(StructId.class, new LinkedListAdapter())
.create();

log.info("n{}", gson.toJson(parent));
}}


Would give you something like:



{
"Name": "name1237598030",
"Type": {
"name": "name688766789"
},
"ParentId": {
"Name": "name1169146729",
"Type": {
"name": "name2040352617"
}
},
"ChildId": {
"Name": "name302155142",
"Type": {
"name": "name24606376"
}
}
}


Names in my test material are just by default initialized with "name"+hashCode()






share|improve this answer


























  • Working perfectly, thanks a lot.

    – zbeedatm
    Jan 3 at 10:12











  • Hi again, I realized that 2 levels is not good enough... I need to fill the whole structure tree till last node

    – zbeedatm
    Jan 3 at 13:37











  • Did you try it? Think it should work. The only thing is that the root node is always the node you give and it will then have parent & child tree

    – pirho
    Jan 3 at 13:42













  • I edited my post with more information related to your solution, pls take a look.

    – zbeedatm
    Jan 3 at 14:19











  • Is it possible to restrict it to 2-3 levels only and prevent the infinite loop? I think it will be enough if each object can hold it's complete filled Parent & Child objects

    – zbeedatm
    Jan 3 at 15:57



















0














Sorry for misleading you guys, based on this post :



Is there a solution about Gson "circular reference"?



"there is no automated solution for circular references in Gson. The only JSON-producing library I know of that handles circular references automatically is XStream (with Jettison backend)."



But that is case you don't use Jackson! If you are using Jackson already for building your REST API controllers so why not to use it for making the serialization. No need for external compopnents like: Gson or XStream.



The solution with Jackson:



Serialization:



ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
try {
jsonDesttinationIdString = ow.writeValueAsString(destinationId);
} catch (JsonProcessingException ex) {
throw new SpecificationException(ex.getMessage());
}


De-serialization:



ObjectMapper mapper = new ObjectMapper();
try {
destinationStructId = destinationId.isEmpty() ? null : mapper.readValue(URLDecoder.decode(destinationId, ENCODING), StructId.class);
} catch (IOException e) {
throw new SpecificationException(e.getMessage());
}


And most important, you must use the @JsonIdentityInfo annotation:



//@JsonIdentityInfo(
// generator = ObjectIdGenerators.PropertyGenerator.class,
// property = "Name")
@JsonIdentityInfo(
generator = ObjectIdGenerators.UUIDGenerator.class,
property = "id")
public class StructId implements Serializable {
private static final long serialVersionUID = 1L;

@JsonProperty("id") // I added this field to have a unique identfier
private UUID id = UUID.randomUUID();





share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54003779%2fhow-to-solve-circular-reference-when-serializing-an-object-which-have-a-class-me%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    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.






    share|improve this answer


























    • Sounds good. I implemented the Serializer/Deserializer solution and I have few questions: 1) why did you use addProperty for Name and not add as value? 2) the json arriving to server controller is like this: {"++"Name"":"+"struct""} and when calling the id.Name = json.getAsJsonObject().get("Name").getAsString(); getting null

      – zbeedatm
      Jan 2 at 15:48













    • @zbeedatm 1) I used addProperty("Name", ...) because that is enough to add a JSON primitive. 2) This JSON looks weird. It would be better to add a formatted JSON example to your question instead of squeezing it into a comment. I have also added my JSON input example to the answer.

      – Thomas Fritsch
      Jan 2 at 16:22











    • These charecters apears because of: URLEncoder.encode(jsonDesttinationIdString, ENCODING). But it was working as expected before using the customized serializer... will find a away to handle it in other side by Encoder.decode maybe

      – zbeedatm
      Jan 2 at 16:30











    • Ok basically seems to be working. But without the ParentId and this data is needed too... so what do you suggest? to try the other way of TypeAdapter? maybe it's achievable there

      – zbeedatm
      Jan 2 at 16:42











    • @zbeedatm For me the ParentId was correctly set in the Java objects, and in JSON it is not neeeded. I suggest you stick to the serializer/deserializer approach and get it working. With the TypeAdapter approach you would get the same functionality, just only with better performance in case of huge JSON contents.

      – Thomas Fritsch
      Jan 2 at 16:56


















    1














    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.






    share|improve this answer


























    • Sounds good. I implemented the Serializer/Deserializer solution and I have few questions: 1) why did you use addProperty for Name and not add as value? 2) the json arriving to server controller is like this: {"++"Name"":"+"struct""} and when calling the id.Name = json.getAsJsonObject().get("Name").getAsString(); getting null

      – zbeedatm
      Jan 2 at 15:48













    • @zbeedatm 1) I used addProperty("Name", ...) because that is enough to add a JSON primitive. 2) This JSON looks weird. It would be better to add a formatted JSON example to your question instead of squeezing it into a comment. I have also added my JSON input example to the answer.

      – Thomas Fritsch
      Jan 2 at 16:22











    • These charecters apears because of: URLEncoder.encode(jsonDesttinationIdString, ENCODING). But it was working as expected before using the customized serializer... will find a away to handle it in other side by Encoder.decode maybe

      – zbeedatm
      Jan 2 at 16:30











    • Ok basically seems to be working. But without the ParentId and this data is needed too... so what do you suggest? to try the other way of TypeAdapter? maybe it's achievable there

      – zbeedatm
      Jan 2 at 16:42











    • @zbeedatm For me the ParentId was correctly set in the Java objects, and in JSON it is not neeeded. I suggest you stick to the serializer/deserializer approach and get it working. With the TypeAdapter approach you would get the same functionality, just only with better performance in case of huge JSON contents.

      – Thomas Fritsch
      Jan 2 at 16:56
















    1












    1








    1







    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.






    share|improve this answer















    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.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 2 at 16:42

























    answered Jan 2 at 12:06









    Thomas FritschThomas Fritsch

    5,426122135




    5,426122135













    • Sounds good. I implemented the Serializer/Deserializer solution and I have few questions: 1) why did you use addProperty for Name and not add as value? 2) the json arriving to server controller is like this: {"++"Name"":"+"struct""} and when calling the id.Name = json.getAsJsonObject().get("Name").getAsString(); getting null

      – zbeedatm
      Jan 2 at 15:48













    • @zbeedatm 1) I used addProperty("Name", ...) because that is enough to add a JSON primitive. 2) This JSON looks weird. It would be better to add a formatted JSON example to your question instead of squeezing it into a comment. I have also added my JSON input example to the answer.

      – Thomas Fritsch
      Jan 2 at 16:22











    • These charecters apears because of: URLEncoder.encode(jsonDesttinationIdString, ENCODING). But it was working as expected before using the customized serializer... will find a away to handle it in other side by Encoder.decode maybe

      – zbeedatm
      Jan 2 at 16:30











    • Ok basically seems to be working. But without the ParentId and this data is needed too... so what do you suggest? to try the other way of TypeAdapter? maybe it's achievable there

      – zbeedatm
      Jan 2 at 16:42











    • @zbeedatm For me the ParentId was correctly set in the Java objects, and in JSON it is not neeeded. I suggest you stick to the serializer/deserializer approach and get it working. With the TypeAdapter approach you would get the same functionality, just only with better performance in case of huge JSON contents.

      – Thomas Fritsch
      Jan 2 at 16:56





















    • Sounds good. I implemented the Serializer/Deserializer solution and I have few questions: 1) why did you use addProperty for Name and not add as value? 2) the json arriving to server controller is like this: {"++"Name"":"+"struct""} and when calling the id.Name = json.getAsJsonObject().get("Name").getAsString(); getting null

      – zbeedatm
      Jan 2 at 15:48













    • @zbeedatm 1) I used addProperty("Name", ...) because that is enough to add a JSON primitive. 2) This JSON looks weird. It would be better to add a formatted JSON example to your question instead of squeezing it into a comment. I have also added my JSON input example to the answer.

      – Thomas Fritsch
      Jan 2 at 16:22











    • These charecters apears because of: URLEncoder.encode(jsonDesttinationIdString, ENCODING). But it was working as expected before using the customized serializer... will find a away to handle it in other side by Encoder.decode maybe

      – zbeedatm
      Jan 2 at 16:30











    • Ok basically seems to be working. But without the ParentId and this data is needed too... so what do you suggest? to try the other way of TypeAdapter? maybe it's achievable there

      – zbeedatm
      Jan 2 at 16:42











    • @zbeedatm For me the ParentId was correctly set in the Java objects, and in JSON it is not neeeded. I suggest you stick to the serializer/deserializer approach and get it working. With the TypeAdapter approach you would get the same functionality, just only with better performance in case of huge JSON contents.

      – Thomas Fritsch
      Jan 2 at 16:56



















    Sounds good. I implemented the Serializer/Deserializer solution and I have few questions: 1) why did you use addProperty for Name and not add as value? 2) the json arriving to server controller is like this: {"++"Name"":"+"struct""} and when calling the id.Name = json.getAsJsonObject().get("Name").getAsString(); getting null

    – zbeedatm
    Jan 2 at 15:48







    Sounds good. I implemented the Serializer/Deserializer solution and I have few questions: 1) why did you use addProperty for Name and not add as value? 2) the json arriving to server controller is like this: {"++"Name"":"+"struct""} and when calling the id.Name = json.getAsJsonObject().get("Name").getAsString(); getting null

    – zbeedatm
    Jan 2 at 15:48















    @zbeedatm 1) I used addProperty("Name", ...) because that is enough to add a JSON primitive. 2) This JSON looks weird. It would be better to add a formatted JSON example to your question instead of squeezing it into a comment. I have also added my JSON input example to the answer.

    – Thomas Fritsch
    Jan 2 at 16:22





    @zbeedatm 1) I used addProperty("Name", ...) because that is enough to add a JSON primitive. 2) This JSON looks weird. It would be better to add a formatted JSON example to your question instead of squeezing it into a comment. I have also added my JSON input example to the answer.

    – Thomas Fritsch
    Jan 2 at 16:22













    These charecters apears because of: URLEncoder.encode(jsonDesttinationIdString, ENCODING). But it was working as expected before using the customized serializer... will find a away to handle it in other side by Encoder.decode maybe

    – zbeedatm
    Jan 2 at 16:30





    These charecters apears because of: URLEncoder.encode(jsonDesttinationIdString, ENCODING). But it was working as expected before using the customized serializer... will find a away to handle it in other side by Encoder.decode maybe

    – zbeedatm
    Jan 2 at 16:30













    Ok basically seems to be working. But without the ParentId and this data is needed too... so what do you suggest? to try the other way of TypeAdapter? maybe it's achievable there

    – zbeedatm
    Jan 2 at 16:42





    Ok basically seems to be working. But without the ParentId and this data is needed too... so what do you suggest? to try the other way of TypeAdapter? maybe it's achievable there

    – zbeedatm
    Jan 2 at 16:42













    @zbeedatm For me the ParentId was correctly set in the Java objects, and in JSON it is not neeeded. I suggest you stick to the serializer/deserializer approach and get it working. With the TypeAdapter approach you would get the same functionality, just only with better performance in case of huge JSON contents.

    – Thomas Fritsch
    Jan 2 at 16:56







    @zbeedatm For me the ParentId was correctly set in the Java objects, and in JSON it is not neeeded. I suggest you stick to the serializer/deserializer approach and get it working. With the TypeAdapter approach you would get the same functionality, just only with better performance in case of huge JSON contents.

    – Thomas Fritsch
    Jan 2 at 16:56















    0














    Just to show one way to make serialization (so I do not handle de-serialization) with TypeAdapter and ExclusionStrategy. This might not be the most beautiful implementation but it is quite generic anyway.



    This solution makes use of the fact that your struct is some kind of a bi-directional linked list and given any node in that list we just need to separate the serialization of parents and children so that those are serialized just in one direction to avoid circular references.



    First we need configurable ExclusionStrategy like:



    public class FieldExclusionStrategy implements ExclusionStrategy {

    private final List<String> skipFields;

    public FieldExclusionStrategy(String... fieldNames) {
    skipFields = Arrays.asList(fieldNames);
    }

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
    return skipFields.contains(f.getName());
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
    return false;
    }

    }


    Then the TypeAdapter would be like:



    public class LinkedListAdapter extends TypeAdapter<StructId> {

    private static final String PARENT_ID = "ParentId";
    private static final String CHILD_ID = "ChildId";
    private Gson gson;

    @Override
    public void write(JsonWriter out, StructId value) throws IOException {
    // First serialize everything but StructIds
    // You could also use type based exclusion strategy
    // but for brevity I use just this one
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(
    new FieldExclusionStrategy(CHILD_ID, PARENT_ID))
    .create();
    JsonObject structObject = gson.toJsonTree(value).getAsJsonObject();
    JsonObject structParentObject;
    JsonObject structChildObject;

    // If exists go through the ParentId side in one direction.
    if(null!=value.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();
    structObject.add(PARENT_ID, gson.toJsonTree(value.ParentId));

    if(null!=value.ParentId.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structParentObject = structObject.get(PARENT_ID).getAsJsonObject();
    structParentObject.add(CHILD_ID, gson.toJsonTree(value.ParentId.ChildId).getAsJsonObject());
    }
    }
    // And also if exists go through the ChildId side in one direction.
    if(null!=value.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structObject.add(CHILD_ID, gson.toJsonTree(value.ChildId));

    if(null!=value.ChildId.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();

    structChildObject = structObject.get(CHILD_ID).getAsJsonObject();
    structChildObject.add(PARENT_ID, gson.toJsonTree(value.ChildId.ParentId).getAsJsonObject());
    }
    }

    // Finally write the combined result out. No need to initialize gson anymore
    // since just writing JsonElement
    gson.toJson(structObject, out);
    }

    @Override
    public StructId read(JsonReader in) throws IOException {
    return null;
    }}


    Testing it:



    @Slf4j
    public class TestIt extends BaseGsonTest {

    @Test
    public void test1() {
    StructId grandParent = new StructId();

    StructId parent = new StructId();
    grandParent.ChildId = parent;
    parent.ParentId = grandParent;

    StructId child = new StructId();
    parent.ChildId = child;
    child.ParentId = parent;

    Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .registerTypeAdapter(StructId.class, new LinkedListAdapter())
    .create();

    log.info("n{}", gson.toJson(parent));
    }}


    Would give you something like:



    {
    "Name": "name1237598030",
    "Type": {
    "name": "name688766789"
    },
    "ParentId": {
    "Name": "name1169146729",
    "Type": {
    "name": "name2040352617"
    }
    },
    "ChildId": {
    "Name": "name302155142",
    "Type": {
    "name": "name24606376"
    }
    }
    }


    Names in my test material are just by default initialized with "name"+hashCode()






    share|improve this answer


























    • Working perfectly, thanks a lot.

      – zbeedatm
      Jan 3 at 10:12











    • Hi again, I realized that 2 levels is not good enough... I need to fill the whole structure tree till last node

      – zbeedatm
      Jan 3 at 13:37











    • Did you try it? Think it should work. The only thing is that the root node is always the node you give and it will then have parent & child tree

      – pirho
      Jan 3 at 13:42













    • I edited my post with more information related to your solution, pls take a look.

      – zbeedatm
      Jan 3 at 14:19











    • Is it possible to restrict it to 2-3 levels only and prevent the infinite loop? I think it will be enough if each object can hold it's complete filled Parent & Child objects

      – zbeedatm
      Jan 3 at 15:57
















    0














    Just to show one way to make serialization (so I do not handle de-serialization) with TypeAdapter and ExclusionStrategy. This might not be the most beautiful implementation but it is quite generic anyway.



    This solution makes use of the fact that your struct is some kind of a bi-directional linked list and given any node in that list we just need to separate the serialization of parents and children so that those are serialized just in one direction to avoid circular references.



    First we need configurable ExclusionStrategy like:



    public class FieldExclusionStrategy implements ExclusionStrategy {

    private final List<String> skipFields;

    public FieldExclusionStrategy(String... fieldNames) {
    skipFields = Arrays.asList(fieldNames);
    }

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
    return skipFields.contains(f.getName());
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
    return false;
    }

    }


    Then the TypeAdapter would be like:



    public class LinkedListAdapter extends TypeAdapter<StructId> {

    private static final String PARENT_ID = "ParentId";
    private static final String CHILD_ID = "ChildId";
    private Gson gson;

    @Override
    public void write(JsonWriter out, StructId value) throws IOException {
    // First serialize everything but StructIds
    // You could also use type based exclusion strategy
    // but for brevity I use just this one
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(
    new FieldExclusionStrategy(CHILD_ID, PARENT_ID))
    .create();
    JsonObject structObject = gson.toJsonTree(value).getAsJsonObject();
    JsonObject structParentObject;
    JsonObject structChildObject;

    // If exists go through the ParentId side in one direction.
    if(null!=value.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();
    structObject.add(PARENT_ID, gson.toJsonTree(value.ParentId));

    if(null!=value.ParentId.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structParentObject = structObject.get(PARENT_ID).getAsJsonObject();
    structParentObject.add(CHILD_ID, gson.toJsonTree(value.ParentId.ChildId).getAsJsonObject());
    }
    }
    // And also if exists go through the ChildId side in one direction.
    if(null!=value.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structObject.add(CHILD_ID, gson.toJsonTree(value.ChildId));

    if(null!=value.ChildId.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();

    structChildObject = structObject.get(CHILD_ID).getAsJsonObject();
    structChildObject.add(PARENT_ID, gson.toJsonTree(value.ChildId.ParentId).getAsJsonObject());
    }
    }

    // Finally write the combined result out. No need to initialize gson anymore
    // since just writing JsonElement
    gson.toJson(structObject, out);
    }

    @Override
    public StructId read(JsonReader in) throws IOException {
    return null;
    }}


    Testing it:



    @Slf4j
    public class TestIt extends BaseGsonTest {

    @Test
    public void test1() {
    StructId grandParent = new StructId();

    StructId parent = new StructId();
    grandParent.ChildId = parent;
    parent.ParentId = grandParent;

    StructId child = new StructId();
    parent.ChildId = child;
    child.ParentId = parent;

    Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .registerTypeAdapter(StructId.class, new LinkedListAdapter())
    .create();

    log.info("n{}", gson.toJson(parent));
    }}


    Would give you something like:



    {
    "Name": "name1237598030",
    "Type": {
    "name": "name688766789"
    },
    "ParentId": {
    "Name": "name1169146729",
    "Type": {
    "name": "name2040352617"
    }
    },
    "ChildId": {
    "Name": "name302155142",
    "Type": {
    "name": "name24606376"
    }
    }
    }


    Names in my test material are just by default initialized with "name"+hashCode()






    share|improve this answer


























    • Working perfectly, thanks a lot.

      – zbeedatm
      Jan 3 at 10:12











    • Hi again, I realized that 2 levels is not good enough... I need to fill the whole structure tree till last node

      – zbeedatm
      Jan 3 at 13:37











    • Did you try it? Think it should work. The only thing is that the root node is always the node you give and it will then have parent & child tree

      – pirho
      Jan 3 at 13:42













    • I edited my post with more information related to your solution, pls take a look.

      – zbeedatm
      Jan 3 at 14:19











    • Is it possible to restrict it to 2-3 levels only and prevent the infinite loop? I think it will be enough if each object can hold it's complete filled Parent & Child objects

      – zbeedatm
      Jan 3 at 15:57














    0












    0








    0







    Just to show one way to make serialization (so I do not handle de-serialization) with TypeAdapter and ExclusionStrategy. This might not be the most beautiful implementation but it is quite generic anyway.



    This solution makes use of the fact that your struct is some kind of a bi-directional linked list and given any node in that list we just need to separate the serialization of parents and children so that those are serialized just in one direction to avoid circular references.



    First we need configurable ExclusionStrategy like:



    public class FieldExclusionStrategy implements ExclusionStrategy {

    private final List<String> skipFields;

    public FieldExclusionStrategy(String... fieldNames) {
    skipFields = Arrays.asList(fieldNames);
    }

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
    return skipFields.contains(f.getName());
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
    return false;
    }

    }


    Then the TypeAdapter would be like:



    public class LinkedListAdapter extends TypeAdapter<StructId> {

    private static final String PARENT_ID = "ParentId";
    private static final String CHILD_ID = "ChildId";
    private Gson gson;

    @Override
    public void write(JsonWriter out, StructId value) throws IOException {
    // First serialize everything but StructIds
    // You could also use type based exclusion strategy
    // but for brevity I use just this one
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(
    new FieldExclusionStrategy(CHILD_ID, PARENT_ID))
    .create();
    JsonObject structObject = gson.toJsonTree(value).getAsJsonObject();
    JsonObject structParentObject;
    JsonObject structChildObject;

    // If exists go through the ParentId side in one direction.
    if(null!=value.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();
    structObject.add(PARENT_ID, gson.toJsonTree(value.ParentId));

    if(null!=value.ParentId.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structParentObject = structObject.get(PARENT_ID).getAsJsonObject();
    structParentObject.add(CHILD_ID, gson.toJsonTree(value.ParentId.ChildId).getAsJsonObject());
    }
    }
    // And also if exists go through the ChildId side in one direction.
    if(null!=value.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structObject.add(CHILD_ID, gson.toJsonTree(value.ChildId));

    if(null!=value.ChildId.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();

    structChildObject = structObject.get(CHILD_ID).getAsJsonObject();
    structChildObject.add(PARENT_ID, gson.toJsonTree(value.ChildId.ParentId).getAsJsonObject());
    }
    }

    // Finally write the combined result out. No need to initialize gson anymore
    // since just writing JsonElement
    gson.toJson(structObject, out);
    }

    @Override
    public StructId read(JsonReader in) throws IOException {
    return null;
    }}


    Testing it:



    @Slf4j
    public class TestIt extends BaseGsonTest {

    @Test
    public void test1() {
    StructId grandParent = new StructId();

    StructId parent = new StructId();
    grandParent.ChildId = parent;
    parent.ParentId = grandParent;

    StructId child = new StructId();
    parent.ChildId = child;
    child.ParentId = parent;

    Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .registerTypeAdapter(StructId.class, new LinkedListAdapter())
    .create();

    log.info("n{}", gson.toJson(parent));
    }}


    Would give you something like:



    {
    "Name": "name1237598030",
    "Type": {
    "name": "name688766789"
    },
    "ParentId": {
    "Name": "name1169146729",
    "Type": {
    "name": "name2040352617"
    }
    },
    "ChildId": {
    "Name": "name302155142",
    "Type": {
    "name": "name24606376"
    }
    }
    }


    Names in my test material are just by default initialized with "name"+hashCode()






    share|improve this answer















    Just to show one way to make serialization (so I do not handle de-serialization) with TypeAdapter and ExclusionStrategy. This might not be the most beautiful implementation but it is quite generic anyway.



    This solution makes use of the fact that your struct is some kind of a bi-directional linked list and given any node in that list we just need to separate the serialization of parents and children so that those are serialized just in one direction to avoid circular references.



    First we need configurable ExclusionStrategy like:



    public class FieldExclusionStrategy implements ExclusionStrategy {

    private final List<String> skipFields;

    public FieldExclusionStrategy(String... fieldNames) {
    skipFields = Arrays.asList(fieldNames);
    }

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
    return skipFields.contains(f.getName());
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
    return false;
    }

    }


    Then the TypeAdapter would be like:



    public class LinkedListAdapter extends TypeAdapter<StructId> {

    private static final String PARENT_ID = "ParentId";
    private static final String CHILD_ID = "ChildId";
    private Gson gson;

    @Override
    public void write(JsonWriter out, StructId value) throws IOException {
    // First serialize everything but StructIds
    // You could also use type based exclusion strategy
    // but for brevity I use just this one
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(
    new FieldExclusionStrategy(CHILD_ID, PARENT_ID))
    .create();
    JsonObject structObject = gson.toJsonTree(value).getAsJsonObject();
    JsonObject structParentObject;
    JsonObject structChildObject;

    // If exists go through the ParentId side in one direction.
    if(null!=value.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();
    structObject.add(PARENT_ID, gson.toJsonTree(value.ParentId));

    if(null!=value.ParentId.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structParentObject = structObject.get(PARENT_ID).getAsJsonObject();
    structParentObject.add(CHILD_ID, gson.toJsonTree(value.ParentId.ChildId).getAsJsonObject());
    }
    }
    // And also if exists go through the ChildId side in one direction.
    if(null!=value.ChildId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
    .create();
    structObject.add(CHILD_ID, gson.toJsonTree(value.ChildId));

    if(null!=value.ChildId.ParentId) {
    gson = new GsonBuilder()
    .addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
    .create();

    structChildObject = structObject.get(CHILD_ID).getAsJsonObject();
    structChildObject.add(PARENT_ID, gson.toJsonTree(value.ChildId.ParentId).getAsJsonObject());
    }
    }

    // Finally write the combined result out. No need to initialize gson anymore
    // since just writing JsonElement
    gson.toJson(structObject, out);
    }

    @Override
    public StructId read(JsonReader in) throws IOException {
    return null;
    }}


    Testing it:



    @Slf4j
    public class TestIt extends BaseGsonTest {

    @Test
    public void test1() {
    StructId grandParent = new StructId();

    StructId parent = new StructId();
    grandParent.ChildId = parent;
    parent.ParentId = grandParent;

    StructId child = new StructId();
    parent.ChildId = child;
    child.ParentId = parent;

    Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .registerTypeAdapter(StructId.class, new LinkedListAdapter())
    .create();

    log.info("n{}", gson.toJson(parent));
    }}


    Would give you something like:



    {
    "Name": "name1237598030",
    "Type": {
    "name": "name688766789"
    },
    "ParentId": {
    "Name": "name1169146729",
    "Type": {
    "name": "name2040352617"
    }
    },
    "ChildId": {
    "Name": "name302155142",
    "Type": {
    "name": "name24606376"
    }
    }
    }


    Names in my test material are just by default initialized with "name"+hashCode()







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 6 at 14:31









    zbeedatm

    829




    829










    answered Jan 2 at 20:34









    pirhopirho

    5,011111832




    5,011111832













    • Working perfectly, thanks a lot.

      – zbeedatm
      Jan 3 at 10:12











    • Hi again, I realized that 2 levels is not good enough... I need to fill the whole structure tree till last node

      – zbeedatm
      Jan 3 at 13:37











    • Did you try it? Think it should work. The only thing is that the root node is always the node you give and it will then have parent & child tree

      – pirho
      Jan 3 at 13:42













    • I edited my post with more information related to your solution, pls take a look.

      – zbeedatm
      Jan 3 at 14:19











    • Is it possible to restrict it to 2-3 levels only and prevent the infinite loop? I think it will be enough if each object can hold it's complete filled Parent & Child objects

      – zbeedatm
      Jan 3 at 15:57



















    • Working perfectly, thanks a lot.

      – zbeedatm
      Jan 3 at 10:12











    • Hi again, I realized that 2 levels is not good enough... I need to fill the whole structure tree till last node

      – zbeedatm
      Jan 3 at 13:37











    • Did you try it? Think it should work. The only thing is that the root node is always the node you give and it will then have parent & child tree

      – pirho
      Jan 3 at 13:42













    • I edited my post with more information related to your solution, pls take a look.

      – zbeedatm
      Jan 3 at 14:19











    • Is it possible to restrict it to 2-3 levels only and prevent the infinite loop? I think it will be enough if each object can hold it's complete filled Parent & Child objects

      – zbeedatm
      Jan 3 at 15:57

















    Working perfectly, thanks a lot.

    – zbeedatm
    Jan 3 at 10:12





    Working perfectly, thanks a lot.

    – zbeedatm
    Jan 3 at 10:12













    Hi again, I realized that 2 levels is not good enough... I need to fill the whole structure tree till last node

    – zbeedatm
    Jan 3 at 13:37





    Hi again, I realized that 2 levels is not good enough... I need to fill the whole structure tree till last node

    – zbeedatm
    Jan 3 at 13:37













    Did you try it? Think it should work. The only thing is that the root node is always the node you give and it will then have parent & child tree

    – pirho
    Jan 3 at 13:42







    Did you try it? Think it should work. The only thing is that the root node is always the node you give and it will then have parent & child tree

    – pirho
    Jan 3 at 13:42















    I edited my post with more information related to your solution, pls take a look.

    – zbeedatm
    Jan 3 at 14:19





    I edited my post with more information related to your solution, pls take a look.

    – zbeedatm
    Jan 3 at 14:19













    Is it possible to restrict it to 2-3 levels only and prevent the infinite loop? I think it will be enough if each object can hold it's complete filled Parent & Child objects

    – zbeedatm
    Jan 3 at 15:57





    Is it possible to restrict it to 2-3 levels only and prevent the infinite loop? I think it will be enough if each object can hold it's complete filled Parent & Child objects

    – zbeedatm
    Jan 3 at 15:57











    0














    Sorry for misleading you guys, based on this post :



    Is there a solution about Gson "circular reference"?



    "there is no automated solution for circular references in Gson. The only JSON-producing library I know of that handles circular references automatically is XStream (with Jettison backend)."



    But that is case you don't use Jackson! If you are using Jackson already for building your REST API controllers so why not to use it for making the serialization. No need for external compopnents like: Gson or XStream.



    The solution with Jackson:



    Serialization:



    ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
    try {
    jsonDesttinationIdString = ow.writeValueAsString(destinationId);
    } catch (JsonProcessingException ex) {
    throw new SpecificationException(ex.getMessage());
    }


    De-serialization:



    ObjectMapper mapper = new ObjectMapper();
    try {
    destinationStructId = destinationId.isEmpty() ? null : mapper.readValue(URLDecoder.decode(destinationId, ENCODING), StructId.class);
    } catch (IOException e) {
    throw new SpecificationException(e.getMessage());
    }


    And most important, you must use the @JsonIdentityInfo annotation:



    //@JsonIdentityInfo(
    // generator = ObjectIdGenerators.PropertyGenerator.class,
    // property = "Name")
    @JsonIdentityInfo(
    generator = ObjectIdGenerators.UUIDGenerator.class,
    property = "id")
    public class StructId implements Serializable {
    private static final long serialVersionUID = 1L;

    @JsonProperty("id") // I added this field to have a unique identfier
    private UUID id = UUID.randomUUID();





    share|improve this answer






























      0














      Sorry for misleading you guys, based on this post :



      Is there a solution about Gson "circular reference"?



      "there is no automated solution for circular references in Gson. The only JSON-producing library I know of that handles circular references automatically is XStream (with Jettison backend)."



      But that is case you don't use Jackson! If you are using Jackson already for building your REST API controllers so why not to use it for making the serialization. No need for external compopnents like: Gson or XStream.



      The solution with Jackson:



      Serialization:



      ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
      try {
      jsonDesttinationIdString = ow.writeValueAsString(destinationId);
      } catch (JsonProcessingException ex) {
      throw new SpecificationException(ex.getMessage());
      }


      De-serialization:



      ObjectMapper mapper = new ObjectMapper();
      try {
      destinationStructId = destinationId.isEmpty() ? null : mapper.readValue(URLDecoder.decode(destinationId, ENCODING), StructId.class);
      } catch (IOException e) {
      throw new SpecificationException(e.getMessage());
      }


      And most important, you must use the @JsonIdentityInfo annotation:



      //@JsonIdentityInfo(
      // generator = ObjectIdGenerators.PropertyGenerator.class,
      // property = "Name")
      @JsonIdentityInfo(
      generator = ObjectIdGenerators.UUIDGenerator.class,
      property = "id")
      public class StructId implements Serializable {
      private static final long serialVersionUID = 1L;

      @JsonProperty("id") // I added this field to have a unique identfier
      private UUID id = UUID.randomUUID();





      share|improve this answer




























        0












        0








        0







        Sorry for misleading you guys, based on this post :



        Is there a solution about Gson "circular reference"?



        "there is no automated solution for circular references in Gson. The only JSON-producing library I know of that handles circular references automatically is XStream (with Jettison backend)."



        But that is case you don't use Jackson! If you are using Jackson already for building your REST API controllers so why not to use it for making the serialization. No need for external compopnents like: Gson or XStream.



        The solution with Jackson:



        Serialization:



        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        try {
        jsonDesttinationIdString = ow.writeValueAsString(destinationId);
        } catch (JsonProcessingException ex) {
        throw new SpecificationException(ex.getMessage());
        }


        De-serialization:



        ObjectMapper mapper = new ObjectMapper();
        try {
        destinationStructId = destinationId.isEmpty() ? null : mapper.readValue(URLDecoder.decode(destinationId, ENCODING), StructId.class);
        } catch (IOException e) {
        throw new SpecificationException(e.getMessage());
        }


        And most important, you must use the @JsonIdentityInfo annotation:



        //@JsonIdentityInfo(
        // generator = ObjectIdGenerators.PropertyGenerator.class,
        // property = "Name")
        @JsonIdentityInfo(
        generator = ObjectIdGenerators.UUIDGenerator.class,
        property = "id")
        public class StructId implements Serializable {
        private static final long serialVersionUID = 1L;

        @JsonProperty("id") // I added this field to have a unique identfier
        private UUID id = UUID.randomUUID();





        share|improve this answer















        Sorry for misleading you guys, based on this post :



        Is there a solution about Gson "circular reference"?



        "there is no automated solution for circular references in Gson. The only JSON-producing library I know of that handles circular references automatically is XStream (with Jettison backend)."



        But that is case you don't use Jackson! If you are using Jackson already for building your REST API controllers so why not to use it for making the serialization. No need for external compopnents like: Gson or XStream.



        The solution with Jackson:



        Serialization:



        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        try {
        jsonDesttinationIdString = ow.writeValueAsString(destinationId);
        } catch (JsonProcessingException ex) {
        throw new SpecificationException(ex.getMessage());
        }


        De-serialization:



        ObjectMapper mapper = new ObjectMapper();
        try {
        destinationStructId = destinationId.isEmpty() ? null : mapper.readValue(URLDecoder.decode(destinationId, ENCODING), StructId.class);
        } catch (IOException e) {
        throw new SpecificationException(e.getMessage());
        }


        And most important, you must use the @JsonIdentityInfo annotation:



        //@JsonIdentityInfo(
        // generator = ObjectIdGenerators.PropertyGenerator.class,
        // property = "Name")
        @JsonIdentityInfo(
        generator = ObjectIdGenerators.UUIDGenerator.class,
        property = "id")
        public class StructId implements Serializable {
        private static final long serialVersionUID = 1L;

        @JsonProperty("id") // I added this field to have a unique identfier
        private UUID id = UUID.randomUUID();






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 8 at 12:42

























        answered Jan 8 at 9:35









        zbeedatmzbeedatm

        829




        829






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54003779%2fhow-to-solve-circular-reference-when-serializing-an-object-which-have-a-class-me%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Monofisismo

            Angular Downloading a file using contenturl with Basic Authentication

            Olmecas