How to exclude fields during serialization of gson - java

I have two entities: Order and Item in a OneToMany relationship. Item belongs an Order, and the Order has a set of Items.
class Order{
#OneToMany(fetch = FetchType.EAGER, mappedBy = "id_order")
Set<Item> items;
}
class Item{
#ManyToOne(targetEntity = Order.class, fetch = FetchType.LAZY)
#JoinColumn(name = "id_order")
Order id_order;
}
I use gson to serialize Orders and send them to another machine, but a loop is being created during serialization, due to both orders and items having a reference to each other.
My goal is that when an Item is loaded, the field id_order should either be null or contain only the id, to avoid propagation. Does hibernate support this feature? Or can I exclude the field during serialization?
I already tried FetchType.LAZY on Item and catching Item inside an onLoad() Interceptor and setting its id_order to null. But it didn't work. I am trying to avoid writing a custom adapter or manually parsing all Items inside all Orders at every query.

I believe it has not much with Hibernate, but more with GSON. You should define serialization/deserialization rule.
Easiest way is to use #Expose annotation, to include/exclude given property from serialization/deserialization:
#Expose(serialize = false, deserialize = false)
Another way is to define custom adapters for givem class. WIth Adapter you can completely override the way GSON serialize or deserialize object.
For example:
public class YourAdapter implements JsonDeserializer<Order>, JsonSerializer<Order> {
#Override
public Orderde serialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
// your logic
}
#Override
public JsonElement serialize(Ordersrc, Type typeOfSrc, JsonSerializationContext context) {
// your logic
}
}
Finally initialize instance of your Gson parser:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Order.class, new YourAdapter())
.build();
If you want to avoid writting whole serialization, you could exclude your field by using #Expose, and then write adapter with pure instance of GSON in it, serialize/deserialize using that pure one and manually add that one field.
Another way of dealing with serialization/deserialization problem is to use ExclusionStrategy described here: https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/ExclusionStrategy.html

I know this doesnt answer the question derectly but might help others, GSON has an option to exclude based on a vairiables modifier.
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE).create();

Related

gson.toJson() throws StackOverflowError in Servlet

I have list of objects clients
List<Client> clientsList=new ArrayList<Client>();
clientsList=clientDao.GetAllClients();
Entity Client has others list as attributes:
#ManyToOne(optional=false)
private User createdBy;
#ManyToMany(mappedBy = "Clients")
private Set<ClientType> Types=new HashSet();
#ManyToOne(optional=false)
private LeadSource id_LeadSource;
#ManyToOne(optional=false)
private Agencie id_Agencie;
#OneToMany(cascade=CascadeType.ALL,mappedBy="Owner")
private Set<Propertie> properties=new HashSet();
#OneToMany(cascade=CascadeType.ALL,mappedBy="buyer")
private Set<Sale> sales=new HashSet();
#OneToMany(cascade=CascadeType.ALL,mappedBy = "client")
private Set<Rent> Rents=new HashSet();
#OneToMany(cascade=CascadeType.ALL,mappedBy = "clientDoc")
private Set<Document> Docuements=new HashSet();
and when i try to convert list of clients to json format
out.write(new Gson().toJson(clientsList));
i get this error :
java.lang.StackOverflowError
at com.google.gson.stream.JsonWriter.beforeName(JsonWriter.java:603)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:401)
at com.google.gson.stream.JsonWriter.value(JsonWriter.java:512)
at com.google.gson.internal.bind.TypeAdapters$8.write(TypeAdapters.java:270)
at com.google.gson.internal.bind.TypeAdapters$8.write(TypeAdapters.java:255)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240)
That is because your entities have bidirectional connections. So for example Client has a set of Rents and each rent has a reference to Client. When you try serializing a Client you serialize its Rents and then you have to serialize each Client in Rent and so on. This is what causes the StackOverflowError.
To solve this problem you will have to mark some properties as transient (or use some similar anotation), for example use transient Client in Rent Then any marshalling lib will just ignore this property.
In case of Gson you can do the other way around marking those field you do want to be included in json with #Expose and creating the gson object with:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
P.S. Also, I would like to mention that converting your JPA entity to json and sending it somewhere is generally not a very good idea. I'd recommend creating a DTO(Data Transfer Object) class where you include only the info you need and ideally using only simple types like int, Date, String and so on. If you have questions about this approach you can google for DTO, Data Transfer Object or follow this link: https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm
After some time fighting with this issue, I believe i have a solution.
As #Nestor Sokil explained problem is in unresolved bidirectional connections, and how to represent connections when they are being serialized.
The way to fix that behavior is to "tell" gson how to serialize objects. For that purpose we use Adapters.
By using Adapters we can tell gson how to serialize every property from your Entity class as well as which properties to serialize.
Let Foo and Bar be two entities where Foo has OneToMany relation to Bar and Bar has ManyToOne relation to Foo. We define Bar adapter so when gson serializes Bar, by defining how to serialize Foo from perspective of Bar cyclic referencing will not be possible.
public class BarAdapter implements JsonSerializer<Bar> {
#Override
public JsonElement serialize(Bar bar, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", bar.getId());
jsonObject.addProperty("name", bar.getName());
jsonObject.addProperty("foo_id", bar.getFoo().getId());
return jsonObject;
}
}
Here foo_id is used to represent Foo entity which would be serialized and which would cause our cyclic referencing problem. Now when we use adapter Foo will not be serialized again from Bar only its id will be taken and put in JSON.
Now we have Bar adapter and we can use it to serialize Foo. Here is idea:
public String getSomething() {
//getRelevantFoos() is some method that fetches foos from database, and puts them in list
List<Foo> fooList = getRelevantFoos();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Bar.class, new BarAdapter());
Gson gson = gsonBuilder.create();
String jsonResponse = gson.toJson(fooList);
return jsonResponse;
}
One more thing to clarify, foo_id is not mandatory and it can be skipped. Purpose of adapter in this example is to serialize Bar and by putting foo_id we showed that Bar can trigger ManyToOne without causing Foo to trigger OneToMany again...
Answer is based on personal experience, therefore feel free to comment, to prove me wrong, to fix mistakes, or to expand answer. Anyhow I hope someone will find this answer useful.

Jackson converting Map<CustomEntity, Integer> to JSON creates toString() from CustomEntity

I have custom Entity that i want to put as Json to my view page
But when i serialize it in map using ObjectMapper from Jackson i receive String created from toString() method
#Test
public void test() throws JsonProcessingException {
Map<ProductEntity, Integer> map = new HashMap<ProductEntity, Integer>();
ProductEntity prod = new ProductEntity();
prod.setIdProduct(1);
map.put(prod, 1);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(map));
}
Received: {"com.onlineshop.entity.ProductEntity#2":1}
where "com.onlineshop.entity.ProductEntity#2" is a String, not an object.
So how can i make it to be an Object?
I need exactly Map, not another type of Collection
You either need to annotate your ProductEntity object so Jackson knows how to serialize it or use a Mix In annotation if you are not able to modify the ProductEntity class. IIRC there are also global Jackson options you can set that tell it how to handle POJOs.
Since you didn't specify which version of Jackson you're using I can't link to the correct documents but there is a ton of information available on the Jackson sites on how to use annotations and mix ins.
Thanks to all for your answers.
I solved it by creating new DTO which contains :
private ProductEntity
private Integer
fields.

gson.toJson() throws StackOverflowError

I would like to generate a JSON String from my object:
Gson gson = new Gson();
String json = gson.toJson(item);
Everytime I try to do this, I get this error:
14:46:40,236 ERROR [[BomItemToJSON]] Servlet.service() for servlet BomItemToJSON threw exception
java.lang.StackOverflowError
at com.google.gson.stream.JsonWriter.string(JsonWriter.java:473)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:347)
at com.google.gson.stream.JsonWriter.value(JsonWriter.java:440)
at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:235)
at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:220)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:200)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:843)
These are the attributes of my BomItem class:
private int itemId;
private Collection<BomModule> modules;
private boolean deprecated;
private String partNumber;
private String description; //LOB
private int quantity;
private String unitPriceDollar;
private String unitPriceEuro;
private String discount;
private String totalDollar;
private String totalEuro;
private String itemClass;
private String itemType;
private String vendor;
private Calendar listPriceDate;
private String unitWeight;
private String unitAveragePower;
private String unitMaxHeatDissipation;
private String unitRackSpace;
Attributes of my referenced BomModule class:
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
Any idea what causes this error? How can I fix it?
That problem is that you have a circular reference.
In the BomModule class you are referencing to:
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
That self reference to BomModule, obviously, not liked by GSON at all.
A workaround is just set the modules to null to avoid the recursive looping. This way I can avoid the StackOverFlow-Exception.
item.setModules(null);
Or mark the fields you don't want to show up in the serialized json by using the transient keyword, eg:
private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;
I had this problem when I had a Log4J logger as a class property, such as:
private Logger logger = Logger.getLogger(Foo.class);
This can be solved by either making the logger static or simply by moving it into the actual function(s).
If you're using Realm and you get this error, and the object giving the trouble extends RealmObject, don't forget to do realm.copyFromRealm(myObject) to create a copy without all the Realm bindings before passing through to GSON for serialization.
I'd missed doing this for just one amongst a bunch of objects being copied... took me ages to realise as the stack trace doesn't name the object class/type. Thing is, the issue is caused by a circular reference, but it's a circular reference somewhere in the RealmObject base class, not your own subclass, which makes it harder to spot!
As SLaks said StackOverflowError happen if you have circular reference in your object.
To fix it you could use TypeAdapter for your object.
For example, if you need only generate String from your object you could use adapter like this:
class MyTypeAdapter<T> extends TypeAdapter<T> {
public T read(JsonReader reader) throws IOException {
return null;
}
public void write(JsonWriter writer, T obj) throws IOException {
if (obj == null) {
writer.nullValue();
return;
}
writer.value(obj.toString());
}
}
and register it like this:
Gson gson = new GsonBuilder()
.registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
.create();
or like this, if you have interface and want to use adapter for all its subclasses:
Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>())
.create();
My answer is a little bit late, but I think this question doesn't have a good solution yet. I found it originally here.
With Gson you can mark the fields you do want to be included in json with #Expose like this:
#Expose
String myString; // will be serialized as myString
and create the gson object with:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Circular references you just do not expose. That did the trick for me!
This error is common when you have a logger in your super class. As #Zar suggested before, you can use static for your logger field, but this also works:
protected final transient Logger logger = Logger.getLogger(this.getClass());
P.S. probably it will work and with #Expose annotation check more about this here: https://stackoverflow.com/a/7811253/1766166
I have the same problem. In my case the reason was that constructor of my serialized class take context variable, like this:
public MetaInfo(Context context)
When I delete this argument, error has gone.
public MetaInfo()
Edit: Sorry for my bad, this is my first answer. Thanks for your advises.
I create my own Json Converter
The main solution I used is to create a parents object set for each object reference. If a sub-reference points to existed parent object, it will discard.
Then I combine with an extra solution, limiting the reference time to avoid infinitive loop in bi-directional relationship between entities.
My description is not too good, hope it helps you guys.
This is my first contribution to Java community (solution to your problem). You can check it out ;)
There is a README.md file
https://github.com/trannamtrung1st/TSON
For Android users, you cannot serialize a Bundle due to a self-reference to Bundle causing a StackOverflowError.
To serialize a bundle, register a BundleTypeAdapterFactory.
In Android, gson stack overflow turned out to be the declaration of a Handler. Moved it to a class that isn't being deserialized.
Based on Zar's recommendation, I made the the handler static when this happened in another section of code. Making the handler static worked as well.
BomItem refers to BOMModule (Collection<BomModule> modules), and BOMModule refers to BOMItem (Collection<BomItem> items). Gson library doesn't like circular references. Remove this circular dependency from your class. I too had faced same issue in the past with gson lib.
I had this problem occur for me when I put:
Logger logger = Logger.getLogger( this.getClass().getName() );
in my object...which made perfect sense after an hour or so of debugging!
Avoid unnecessary workarounds, like setting values to null or making fields transient. The right way to do this, is to annotate one of the fields with #Expose and then tell Gson to serialize only the fields with the annotation:
private Collection<BomModule> parentModules;
#Expose
private Collection<BomModule> subModules;
...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
I had a similar issue where the class had an InputStream variable which I didn't really have to persist. Hence changing it to Transient solved the issue.
After some time fighting with this issue, I believe i have a solution.
Problem is in unresolved bidirectional connections, and how to represent connections when they are being serialized.
The way to fix that behavior is to "tell" gson how to serialize objects. For that purpose we use Adapters.
By using Adapters we can tell gson how to serialize every property from your Entity class as well as which properties to serialize.
Let Foo and Bar be two entities where Foo has OneToMany relation to Bar and Bar has ManyToOne relation to Foo. We define Bar adapter so when gson serializes Bar, by defining how to serialize Foo from perspective of Bar cyclic referencing will not be possible.
public class BarAdapter implements JsonSerializer<Bar> {
#Override
public JsonElement serialize(Bar bar, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", bar.getId());
jsonObject.addProperty("name", bar.getName());
jsonObject.addProperty("foo_id", bar.getFoo().getId());
return jsonObject;
}
}
Here foo_id is used to represent Foo entity which would be serialized and which would cause our cyclic referencing problem. Now when we use adapter Foo will not be serialized again from Bar only its id will be taken and put in JSON.
Now we have Bar adapter and we can use it to serialize Foo. Here is idea:
public String getSomething() {
//getRelevantFoos() is some method that fetches foos from database, and puts them in list
List<Foo> fooList = getRelevantFoos();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Bar.class, new BarAdapter());
Gson gson = gsonBuilder.create();
String jsonResponse = gson.toJson(fooList);
return jsonResponse;
}
One more thing to clarify, foo_id is not mandatory and it can be skipped. Purpose of adapter in this example is to serialize Bar and by putting foo_id we showed that Bar can trigger ManyToOne without causing Foo to trigger OneToMany again...
Answer is based on personal experience, therefore feel free to comment, to prove me wrong, to fix mistakes, or to expand answer. Anyhow I hope someone will find this answer useful.

Jackson vs Gson for simple deserialisation

For parsing JSON like this twitter API users/show response I've been using Jackson and Gson Java libraries as candidates to do this work. I'm only interested in a small subset of properties of the JSON so Gson was nice because of its very concise syntax but I'm losing an internal battle to continue to use Gson as Jackson is already used elsewhere in our application and it has documented better performance (which I concede are both good reasons to lose Gson).
For a POJO like
public class TwitterUser {
private String id_str;
private String screen_name;
public String getId_str() {
return id_str;
}
public void setId_str(String id_str) {
this.id_str = id_str;
}
public String getScreen_name() {
return screen_name;
}
public void setScreen_name(String screen_name) {
this.screen_name = screen_name;
}
}
The only code for Gson needed to build this is one line,
TwitterUser user = new Gson().fromJson(jsonStr, TwitterUser.class);
That's pretty nice to me; scales well and is opt-in for the properties you want. Jackson on the other hand is a little more laborious for building a POJO from selected fields.
Map<String,Object> userData = new ObjectMapper().readValue(jsonStr, Map.class);
//then build TwitterUser manually
or
TwitterUser user = new ObjectMapper().readValue(jsonStr, TwitterUser.class);
//each unused property must be marked as ignorable. Yikes! For 30 odd ignored fields thats too much configuration.
So after that long winded explanation, is there a way I can use Jackson with less code than is demonstrated above?
With Jackson 1.4+ you can use the class-level #JsonIgnoreProperties annotation to silently ignore unknown fields, with ignoreUnknown set to true.
#JsonIgnoreProperties(ignoreUnknown = true)
public class TwitterUser {
// snip...
}
http://wiki.fasterxml.com/JacksonAnnotations
http://wiki.fasterxml.com/JacksonHowToIgnoreUnknown

How to solve circular reference in json serializer caused by hibernate bidirectional mapping?

I am writing a serializer to serialize POJO to JSON but stuck in circular reference problem. In hibernate bidirectional one-to-many relation, parent references child and child references back to parent and here my serializer dies. (see example code below)
How to break this cycle? Can we get owner tree of an object to see whether object itself exists somewhere in its own owner hierarchy? Any other way to find if the reference is going to be circular? or any other idea to resolve this problem?
I rely on Google JSON To handle this kind of issue by using The feature
Excluding Fields From Serialization and Deserialization
Suppose a bi-directional relationship between A and B class as follows
public class A implements Serializable {
private B b;
}
And B
public class B implements Serializable {
private A a;
}
Now use GsonBuilder To get a custom Gson object as follows (Notice setExclusionStrategies method)
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return (clazz == B.class);
}
/**
* Custom field exclusion goes here
*/
public boolean shouldSkipField(FieldAttributes f) {
return false;
}
})
/**
* Use serializeNulls method if you want To serialize null values
* By default, Gson does not serialize null values
*/
.serializeNulls()
.create();
Now our circular reference
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
String json = gson.toJson(a);
System.out.println(json);
Take a look at GsonBuilder class
Jackson 1.6 (released september 2010) has specific annotation-based support for handling such parent/child linkage, see http://wiki.fasterxml.com/JacksonFeatureBiDirReferences. (Wayback Snapshot)
You can of course already exclude serialization of parent link already using most JSON processing packages (jackson, gson and flex-json at least support it), but the real trick is in how to deserialize it back (re-create parent link), not just handle serialization side. Although sounds like for now just exclusion might work for you.
EDIT (April 2012): Jackson 2.0 now supports true identity references (Wayback Snapshot), so you can solve it this way also.
Can a bi-directional relationship even be represented in JSON? Some data formats are not good fits for some types of data modelling.
One method for dealing with cycles when dealing with traversing object graphs is to keep track of which objects you've seen so far (using identity comparisons), to prevent yourself from traversing down an infinite cycle.
In addressing this problem, I took the following approach (standardizing the process across my application, making the code clear and reusable):
Create an annotation class to be used on fields you'd like excluded
Define a class which implements Google's ExclusionStrategy interface
Create a simple method to generate the GSON object using the GsonBuilder (similar to Arthur's explanation)
Annotate the fields to be excluded as needed
Apply the serialization rules to your com.google.gson.Gson object
Serialize your object
Here's the code:
1)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD})
public #interface GsonExclude {
}
2)
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
public class GsonExclusionStrategy implements ExclusionStrategy{
private final Class<?> typeToExclude;
public GsonExclusionStrategy(Class<?> clazz){
this.typeToExclude = clazz;
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return ( this.typeToExclude != null && this.typeToExclude == clazz )
|| clazz.getAnnotation(GsonExclude.class) != null;
}
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(GsonExclude.class) != null;
}
}
3)
static Gson createGsonFromBuilder( ExclusionStrategy exs ){
GsonBuilder gsonbuilder = new GsonBuilder();
gsonbuilder.setExclusionStrategies(exs);
return gsonbuilder.serializeNulls().create();
}
4)
public class MyObjectToBeSerialized implements Serializable{
private static final long serialVersionID = 123L;
Integer serializeThis;
String serializeThisToo;
Date optionalSerialize;
#GsonExclude
#ManyToOne(fetch=FetchType.LAZY, optional=false)
#JoinColumn(name="refobj_id", insertable=false, updatable=false, nullable=false)
private MyObjectThatGetsCircular dontSerializeMe;
...GETTERS AND SETTERS...
}
5)
In the first case, null is supplied to the constructor, you can specify another class to be excluded - both options are added below
Gson gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(null) );
Gson _gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(Date.class) );
6)
MyObjectToBeSerialized _myobject = someMethodThatGetsMyObject();
String jsonRepresentation = gsonObj.toJson(_myobject);
or, to exclude the Date object
String jsonRepresentation = _gsonObj.toJson(_myobject);
If you are using Jackon to serialize, just apply #JsonBackReference to your bi-directinal mapping
It will solve the circular reference problem.
Note : #JsonBackReference is used to solve the Infinite recursion (StackOverflowError)
Used a solution similar to Arthur's but instead of setExclusionStrategies I used
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
and used #Expose gson annotation for fields which I need in the json, other fields are excluded.
if you are using spring boot,Jackson throws error while creating response from circular/bidirectional data, so use
#JsonIgnoreProperties
to ignore circularity
At Parent:
#OneToMany(mappedBy="dbApp")
#JsonIgnoreProperties("dbApp")
private Set<DBQuery> queries;
At child:
#ManyToOne
#JoinColumn(name = "db_app_id")
#JsonIgnoreProperties("queries")
private DBApp dbApp;
If you are using Javascript, there's a very easy solution to that using the replacer parameter of JSON.stringify() method where you can pass a function to modify the default serialization behavior.
Here's how you can use it. Consider the below example with 4 nodes in a cyclic graph.
// node constructor
function Node(key, value) {
this.name = key;
this.value = value;
this.next = null;
}
//create some nodes
var n1 = new Node("A", 1);
var n2 = new Node("B", 2);
var n3 = new Node("C", 3);
var n4 = new Node("D", 4);
// setup some cyclic references
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n1;
function normalStringify(jsonObject) {
// this will generate an error when trying to serialize
// an object with cyclic references
console.log(JSON.stringify(jsonObject));
}
function cyclicStringify(jsonObject) {
// this will successfully serialize objects with cyclic
// references by supplying #name for an object already
// serialized instead of passing the actual object again,
// thus breaking the vicious circle :)
var alreadyVisited = [];
var serializedData = JSON.stringify(jsonObject, function(key, value) {
if (typeof value == "object") {
if (alreadyVisited.indexOf(value.name) >= 0) {
// do something other that putting the reference, like
// putting some name that you can use to build the
// reference again later, for eg.
return "#" + value.name;
}
alreadyVisited.push(value.name);
}
return value;
});
console.log(serializedData);
}
Later, you can easily recreate the actual object with the cyclic references by parsing the serialized data and modifying the next property to point to the actual object if it's using a named reference with a # like in this example.
This is how i finally solved it in my case. This works at least with Gson & Jackson.
private static final Gson gson = buildGson();
private static Gson buildGson() {
return new GsonBuilder().addSerializationExclusionStrategy( getExclusionStrategy() ).create();
}
private static ExclusionStrategy getExclusionStrategy() {
ExclusionStrategy exlStrategy = new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes fas) {
return ( null != fas.getAnnotation(ManyToOne.class) );
}
#Override
public boolean shouldSkipClass(Class<?> classO) {
return ( null != classO.getAnnotation(ManyToOne.class) );
}
};
return exlStrategy;
}
Jackson provides JsonIdentityInfo annotation to prevent circular references. You can check the tutorial here.
This error can appened when you have two objects :
class object1{
private object2 o2;
}
class object2{
private object1 o1;
}
With using GSon for serialization, i have got this error :
java.lang.IllegalStateException: circular reference error
Offending field: o1
To solved this, just add key word transient :
class object1{
private object2 o2;
}
class object2{
transient private object1 o1;
}
As you can see here : Why does Java have transient fields?
The transient keyword in Java is used to indicate that a field should not be serialized.
If you use GSON to convert Java class in JSON you can avoid the fields that cause the circular reference and the infinitive loop, you only have to put the annotation #Expose in the fields that you want to appear in the JSON, and the fields without the annotation #Expose do not appear in the JSON.
The circular reference appears for example if we try to serialize the class User with the field routes of class Route, and the class Route have the field user of the class User, then GSON try to serialize the class User and when try to serialize routes, serialize the class Route and in the class Route try to serialize the field user, and again try to serialize the class User, there is a circular reference that provoke the infinitive loop. I show the class User and Route that mentioned.
import com.google.gson.annotations.Expose;
Class User
#Entity
#Table(name = "user")
public class User {
#Column(name = "name", nullable = false)
#Expose
private String name;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<Route> routes;
#ManyToMany(fetch = FetchType.EAGER)
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinTable(name = "like_", joinColumns = #JoinColumn(name = "id_user"),
inverseJoinColumns = #JoinColumn(name = "id_route"),
foreignKey = #ForeignKey(name = ""),
inverseForeignKey = #ForeignKey(name = ""))
private Set<Route> likes;
Class Route
#Entity
#Table(name = "route")
public class Route {
#ManyToOne()
#JoinColumn(nullable = false, name = "id_user", foreignKey =
#ForeignKey(name = "c"))
private User user;
To avoid the infinitive loop, we use the annotation #Expose that offer GSON.
I show in format JSON the result of serialize with GSON the class User.
{
"name": "ignacio"
}
We can see that the field route and likes do not exist in the format JSON, only the field name. Because of this, the circular reference is avoid.
If we want to use that, we have to create an object GSON on a specific way.
Gson converterJavaToJson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
In the end, we transform the java class of the model of hibernate user using the conversor GSON created.
User user = createUserWithHibernate();
String json = converterJavaToJson.toJson(user);
the answer number 8 is the better, i think so if you know what field is throwing a error the you only set the fild in null and solved.
List<RequestMessage> requestMessages = lazyLoadPaginated(first, pageSize, sortField, sortOrder, filters, joinWith);
for (RequestMessage requestMessage : requestMessages) {
Hibernate.initialize(requestMessage.getService());
Hibernate.initialize(requestMessage.getService().getGroupService());
Hibernate.initialize(requestMessage.getRequestMessageProfessionals());
for (RequestMessageProfessional rmp : requestMessage.getRequestMessageProfessionals()) {
Hibernate.initialize(rmp.getProfessional());
rmp.setRequestMessage(null); // **
}
}
To make the code readable a big comment is moved from the comment // ** to below.
java.lang.StackOverflowError [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError) (through reference chain: com.service.pegazo.bo.RequestMessageProfessional["requestMessage"]->com.service.pegazo.bo.RequestMessage["requestMessageProfessionals"]
For example, ProductBean has got serialBean. The mapping would be bi-directional relationship. If we now try to use gson.toJson(), it will end up with circular reference. In order to avoid that problem, you can follow these steps:
Retrieve the results from datasource.
Iterate the list and make sure the serialBean is not null, and then
Set productBean.serialBean.productBean = null;
Then try to use gson.toJson();
That should solve the problem

Categories

Resources