How to solve circular reference when serializing an object which have a class member with the same type of...
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.
json rest serialization gson circular-reference
add a comment |
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.
json rest serialization gson circular-reference
Please edit your question to contain your actual question.
– ruakh
Jan 2 at 9:23
add a comment |
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.
json rest serialization gson circular-reference
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.
json rest serialization gson circular-reference
json rest serialization gson circular-reference
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
add a comment |
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
add a comment |
3 Answers
3
active
oldest
votes
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 withStructId root = gson.fromJson(new FileReader("example.json"), StructId.class);
,
then by serializing that withSystem.out.println(gson.toJson(root));
and got the original JSON again.
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 usedaddProperty("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 theParentId
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
|
show 1 more comment
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()
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
|
show 4 more comments
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();
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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 withStructId root = gson.fromJson(new FileReader("example.json"), StructId.class);
,
then by serializing that withSystem.out.println(gson.toJson(root));
and got the original JSON again.
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 usedaddProperty("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 theParentId
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
|
show 1 more comment
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 withStructId root = gson.fromJson(new FileReader("example.json"), StructId.class);
,
then by serializing that withSystem.out.println(gson.toJson(root));
and got the original JSON again.
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 usedaddProperty("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 theParentId
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
|
show 1 more comment
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 withStructId root = gson.fromJson(new FileReader("example.json"), StructId.class);
,
then by serializing that withSystem.out.println(gson.toJson(root));
and got the original JSON again.
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 withStructId root = gson.fromJson(new FileReader("example.json"), StructId.class);
,
then by serializing that withSystem.out.println(gson.toJson(root));
and got the original JSON again.
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 usedaddProperty("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 theParentId
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
|
show 1 more comment
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 usedaddProperty("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 theParentId
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
|
show 1 more comment
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()
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
|
show 4 more comments
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()
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
|
show 4 more comments
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()
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()
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
|
show 4 more comments
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
|
show 4 more comments
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();
add a comment |
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();
add a comment |
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();
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();
edited Jan 8 at 12:42
answered Jan 8 at 9:35
zbeedatmzbeedatm
829
829
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Please edit your question to contain your actual question.
– ruakh
Jan 2 at 9:23