Reputation: 46904
I have a structure with circular references. And for debug purposes, I want to dump it. Basically as any format, but I chose JSON.
Since it can be any class, I chose GSON which doesn't needs JAXB annotations.
But GSON hits the circular references and recurses until StackOverflowError
.
How can I limit GSON to
ignore certain class members?
Both @XmlTransient
and @JsonIgnore
are not obeyed.
ignore certain object graph paths? E.g. I could instruct GSON not to serialize release.customFields.product
.
go to the depth of at most 2 levels?
Related: Gson.toJson gives StackOverFlowError, how to get proper json in this case? (public static class)
Upvotes: 12
Views: 10413
Reputation: 446
I know this question has a few years now, but I'd like to contribute with my solution. Although @fdreger's answer is completely valid in case you want to exclude a field always, it doesn't work if you want to exclude it just in certain cases, avoiding this way the recursion. The way I approached the problem is:
I write my own JsonSerializer
. In it, I define a static variable to control de number of times an object of this same class is serialize and depending on the value, the object can be serialized or not.
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class UserJsonSerializer extends JsonSerializer<User> {
private static final ThreadLocal<Integer> depth = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
@Override
public void serialize(User user, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
// Here, we limit the number of instances to return. In this case, just 1.
depth.set(depth.get() + 1);
if(depth.get() >= 1) {
generator.writeNull();
} else {
generator.writeObject(user);
}
}
public static void clear() {
depth.remove();
}
}
Bind the UserJsonSerializer
to the class you want to control
public class SomeClass implements Serializable {
@JsonSerialize(using = UserJsonSerializer.class)
private User user;
//...others fields...
}
Don't forget to call UserJsonSerializer#clear()
method to reinitialize the counter everytime you're going to parse a new entity.
I hope this helps.
Upvotes: 0
Reputation: 12505
Simply make the fields transient (as in private transient int field = 4;
). GSON understands that.
Edit
No need for a built-in annotation; Gson lets you plug in your own strategies for excluding fields and classes. They cannot be based on a path or nesting level, but annotations and names are fine.
If I wanted to skip fields that are named "lastName" on class "my.model.Person", I could write an exclusion strategy like this:
class MyExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes fa) {
String className = fa.getDeclaringClass().getName();
String fieldName = fa.getName();
return
className.equals("my.model.Person")
&& fieldName.equals("lastName");
}
@Override
public boolean shouldSkipClass(Class<?> type) {
// never skips any class
return false;
}
}
I could also make my own annotation:
@Retention(RetentionPolicy.RUNTIME)
public @interface GsonRepellent {
}
And rewrite the shouldSkipField
method as:
public boolean shouldSkipField(FieldAttributes fa) {
return fa.getAnnotation(GsonRepellent.class) != null;
}
This would enable me to do things like:
public class Person {
@GsonRepellent
private String lastName = "Troscianko";
// ...
To use a custom ExclusionStrategy, build Gson object using the builder:
Gson g = new GsonBuilder()
.setExclusionStrategies(new MyOwnExclusionStrategy())
.create();
Upvotes: 25