Reputation: 494
I am trying to render
a list as json
in play framework 1.2.x
. As I search from net, I found Gosn
was used. but it didn't help me anyway. I need to render any object list
as json
in my controller action
. Here is my code and error trace.
@Entity(name="country")
public class Country extends Model{
public String name;
public String code;
@OneToMany(mappedBy="country", fetch=FetchType.EAGER, cascade = CascadeType.ALL)
public List<City> cities;
}
@Entity
public class City extends Model{
public String name;
public String code;
@ManyToOne
public Country country;
}
public static void ajaxGetCities(long countryid){
List<City> cities = City.find("byCountry_id", countryid).fetch();
renderJSON(cities);
}
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:879)
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.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:195)
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:195)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:879)
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.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:195)
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:195)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:879)
Upvotes: 0
Views: 786
Reputation: 1076
It seems like a circular reference problem. When gson tries to serialize City object (say, City A) in list, it finds Country object so it tries to serialize Country object too. However, there are many City object references in Country object and one of these references points to City A. So there is a reference loop here.
There are two options to resolve circular reference in your case:
A. Remove reference of City object in Country object.
@OneToMany(mappedBy="country", fetch=FetchType.EAGER, cascade = CascadeType.ALL)
public List<City> cities;
OneToMany
is kind of redundant to me since I could always find cities of the Country by a static method in City Object.
B. Keep cities
fields and implement CountrySerializer
to prevent circular reference in gson.
public class CountrySerializer implements JsonSerializer<Country> {
public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new LocalExclusionStrategy()).create();
return gson.toJsonTree(src);
}
public static class LocalExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
// add exlusion rules here:
// exclude field whose name is "cities"
return f.getName().toLowerCase().equals("cities");
}
}
}
to use it:
public static void ajaxGetCities(long countryid){
List<City> cities = City.find("byCountry_id", countryid).fetch();
renderJSON(cities, new CountrySerializer());
}
if you have complicated structure in return object, you could have multiple serializer in use at the same time.
renderJSON(cities, new CountrySerializer(), new CodeSerializer());
Upvotes: 1