2011-07-03

Gson v Jackson - Part 4

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

Part 3 of this short series of articles ended with section 5.10.1 of the Gson user guide, "InstanceCreator for a Parameterized Type". This fourth part continues with section 5.11 on "Compact Vs. Pretty Printing for JSON Output Format", and ends with section 5.14.3 on "User Defined Exclusion Strategies". Part 5 will continue with section 5.15 on "JSON Field Naming Support".
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/RpFIE

See part 6 of this series for a complete listing of and links to the various sections of this Gson user guide review.

For cross reference, readers will likely find it useful to also have the Gson user guide open in another browser window. (An archive of the Gson user guide as of 2011.06.26 is available at https://sites.google.com/site/programmerbruce/downloads/Gson_User_Guide_2011.06.26.zip.)

This information is based on Gson release 1.7.1 and Jackson release 1.8.2.

The Gson User Guide Walk-through Continued...


Compact Vs. Pretty Printing for JSON Output Format


Gson and Jackson have similar built-in features to pretty-print JSON.
// input: {"a":"A","b":{"c":"C","d":["D","E","F"]}}
String json = "{\"a\":\"A\",\"b\":{\"c\":\"C\",\"d\":" +
"[\"D\",\"E\",\"F\"]}}";

Gson gson = new Gson();
System.out.println(
gson.toJson(gson.fromJson(json, Foo.class)));
// output: {"a":"A","b":{"c":"C","d":["D","E","F"]}}

gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println(
gson.toJson(gson.fromJson(json, Foo.class)));
// output:
// {
// "a": "A",
// "b": {
// "c": "C",
// "d": [
// "D",
// "E",
// "F"
// ]
// }
// }

ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(
mapper.readValue(json, Foo.class)));
// output: {"a":"A","b":{"c":"C","d":["D","E","F"]}}
ObjectWriter prettyWriter =
mapper.defaultPrettyPrintingWriter();
System.out.println(prettyWriter.writeValueAsString(
mapper.readValue(json, Foo.class)));
// output:
// {
// "a" : "A",
// "b" : {
// "c" : "C",
// "d" : [ "D", "E", "F" ]
// }
//}
Gson, however, for some reason did not expose the pretty printing feature to allow for user customizing. Jackson did.

Comparison Ratings:
  • COMPARABLE for built-in pretty printing capability
  • +1 Jackson for allowing easy plug-in of custom pretty printing

Null Object Support


(Some additional and overlapping information on null handling was covered in the "Finer Points with Objects" section of the Gson user guide, reviewed in the first part of this series.)

When generating JSON, by default Gson skips inclusion of elements for which the corresponding Java field is a null reference. By default, Jackson includes them. Gson has a setting to include them. Jackson has a setting to exclude them.
class Foo
{
public String a;
public String b;
}

Gson gson = new Gson();
ObjectMapper mapper = new ObjectMapper();

// Default null serializing behavior...

Foo foo = null;

System.out.println(gson.toJson(foo));
// output: NOTHING -- EMPTY STRING
System.out.println("".equals(gson.toJson(foo))); // true
System.out.println(mapper.writeValueAsString(foo));
// output: null

foo = new Foo();

System.out.println(gson.toJson(foo));
// output: {}
System.out.println(mapper.writeValueAsString(foo));
// output: {"a":null,"b":null}

foo.a = "42";

System.out.println(gson.toJson(foo));
// output: {"a":"42"}
System.out.println(mapper.writeValueAsString(foo));
// output: {"a":"42","b":null}

System.out.println();

// Reversing behaviors...

gson = new GsonBuilder().serializeNulls().create();
mapper = new ObjectMapper();
mapper.getSerializationConfig()
.setSerializationInclusion(NON_NULL);

foo = null;

System.out.println(gson.toJson(foo));
// output: null
System.out.println(mapper.writeValueAsString(foo));
// output: null

foo = new Foo();

System.out.println(gson.toJson(foo));
// output: {"a":null,"b":null}
System.out.println(mapper.writeValueAsString(foo));
// output: {}

foo.a = "42";

System.out.println(gson.toJson(foo));
// output: {"a":"42","b":null}
System.out.println(mapper.writeValueAsString(foo));
// output: {"a":"42"}
Additional Code Notes: While there is a difference between how Gson and Jackson deserialize a direct null reference, I'm not a fan of either approach. Both generate JSON that is invalid. Valid JSON must start with either '{' or '['. I would rather see "{}" as the output of deserializing a null reference directly.

For serializing nulls, Jackson offers configurability beyond the global on/off configuration so far demonstrated. The @JsonSerialize annotation can be applied to individual class definitions, or even individual fields to turn null serialization on/off with greater granularity. Also, null serialization can be further controlled by specifying a null value serializer, as demonstrated at http://wiki.fasterxml.com/JacksonHowToCustomSerializers

Comparison Ratings:
  • COMPARABLE for global on/off configurability of whether to serialize null references
  • +1 Jackson for greater configurability of null reference serialization

Versioning Support


Jackson currently has no built-in feature similar to Gson's versioning support, though the next release reportedly will have something comparable.

Comparison Rating: +1 Gson for built-in versioning support

Excluding Fields From Serialization and Deserialization - Java Modifier Exclusion


Gson and Jackson offer similar mechanisms to globally specify exclusion of fields by modifier. (Note the examples in the Gson user guide don't actually compile, as the method name is "excludeFieldsWithModifiers" not "excludeFieldsWithModifier". Also, while it says, "you can use any number of the Modifier constants," only seven of the constants are actually applicable, and there is no constant for the "default" modifier -- that is, no modifier.)

A demo of Gson's ability to exclude by modifiers:
public class GsonExclusionByModifierExamples
{
public static void main(String[] args) throws Exception
{
Foo foo = new Foo();

Gson gson = new Gson();
System.out.println(gson.toJson(foo));
// output: {"a":"final","b":"private","c":"protected",
// "d":"public","g":"volatile","h":"default"}

gson = gsonWithModifierIncluded(Modifier.FINAL);
System.out.println(gson.toJson(foo));
// output: {"a":"final","h":"default"}
gson = gsonWithModifierIncluded(Modifier.PRIVATE);
System.out.println(gson.toJson(foo));
// output: {"b":"private","h":"default"}
gson = gsonWithModifierIncluded(Modifier.PROTECTED);
System.out.println(gson.toJson(foo));
// output: {"c":"protected","h":"default"}
gson = gsonWithModifierIncluded(Modifier.PUBLIC);
System.out.println(gson.toJson(foo));
// output: {"d":"public","h":"default"}
gson = gsonWithModifierIncluded(Modifier.STATIC);
System.out.println(gson.toJson(foo));
// output: {"e":"static","h":"default"}
gson = gsonWithModifierIncluded(Modifier.TRANSIENT);
System.out.println(gson.toJson(foo));
// output: {"f":"transient","h":"default"}
gson = gsonWithModifierIncluded(Modifier.VOLATILE);
System.out.println(gson.toJson(foo));
// output: {"g":"volatile","h":"default"}
}

static Gson gsonWithModifierIncluded(int modifierToInclude)
{
List<Integer> modifiers = new ArrayList<Integer>();
modifiers.add(Modifier.FINAL);
modifiers.add(Modifier.PRIVATE);
modifiers.add(Modifier.PROTECTED);
modifiers.add(Modifier.PUBLIC);
modifiers.add(Modifier.STATIC);
modifiers.add(Modifier.TRANSIENT);
modifiers.add(Modifier.VOLATILE);
modifiers.remove(new Integer(modifierToInclude));
return new GsonBuilder()
.excludeFieldsWithModifiers(asIntArray(modifiers))
.create();
}

static int[] asIntArray(List<Integer> numbers)
{
int size = numbers.size();
int[] ints = new int[size];
for (int i = 0; i < size; i++)
{
ints[i] = numbers.get(i);
}
return ints;
}
}

class Foo
{
final String a = "final";
private String b = "private";
protected String c = "protected";
public String d = "public";
static String e = "static";
transient String f = "transient";
volatile String g = "volatile";
String h = "default";
}
Jackson's approach is somewhat different. Instead of offering a configuration feature to exclude fields by specified modifiers, it offers a feature to include fields by visibility, and there is no similar built-in feature to configure field inclusion by modifiers static, transient, or volatile. Doing so would require custom serialization/deserialization processing.

A demo of Jackson's ability to include fields by visibility:
public class JacksonIncludeFieldsByModifierExamples
{
public static void main(String[] args) throws Exception
{
Bar bar = new Bar();

ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(bar));
// output: {"a":"final","d":"public","g":"volatile"}

mapper = withModifierIncluded(Visibility.ANY);
System.out.println(mapper.writeValueAsString(bar));
// output: {"a":"final","b":"private","c":"protected",
// "d":"public","g":"volatile","h":"default"}
mapper = withModifierIncluded(Visibility.DEFAULT);
System.out.println(mapper.writeValueAsString(bar));
// output: {"a":"final","d":"public","g":"volatile"}
mapper = withModifierIncluded(Visibility.NON_PRIVATE);
System.out.println(mapper.writeValueAsString(bar));
// output: {"a":"final","c":"protected","d":"public",
// "g":"volatile","h":"default"}

mapper = withModifierIncluded(Visibility.NONE);
mapper.configure(FAIL_ON_EMPTY_BEANS, false);
System.out.println(mapper.writeValueAsString(bar));
// output: {}
// Visibility.NONE would exclude everything.
// Cannot be used unless exposing some fields through
// setters/getters, or disabling FAIL_ON_EMPTY_BEANS.

mapper =
withModifierIncluded(Visibility.PROTECTED_AND_PUBLIC);
System.out.println(mapper.writeValueAsString(bar));
// output: {"a":"final","c":"protected",
// "d":"public","g":"volatile"}
mapper = withModifierIncluded(Visibility.PUBLIC_ONLY);
System.out.println(mapper.writeValueAsString(bar));
// output: {"a":"final","d":"public","g":"volatile"}
}

static ObjectMapper
withModifierIncluded(Visibility visibility)
{
ObjectMapper mapper = new ObjectMapper();
VisibilityChecker<?> visibilityChecker =
mapper.getVisibilityChecker()
.withFieldVisibility(visibility);
mapper.setVisibilityChecker(visibilityChecker);
return mapper;
}
}

class Bar
{
public final String a = "final";
private String b = "private";
protected String c = "protected";
public String d = "public";
public static String e = "static";
public transient String f = "transient";
public volatile String g = "volatile";
String h = "default";
}
Comparison Ratings:
  • COMPARABLE for global configurability to include fields by modifier
  • +1 Gson for ability to configure field inclusion by modifiers static, transient, or volatile
  • +1 Jackson for ability to configure field inclusion by default visibility

Excluding Fields From Serialization and Deserialization - Gson's @Expose


Creating a Gson instance with new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() allows exclusion of all fields from participating in serialization or deserialization, except those marked with the @Expose annotation. This is arguably convenient if a Java structure has a lot of fields, only few of which are wanted for serializing or deserializing. However, this configuration mechanism is inconvenient, if alternatively a Java structure with a lot of fields only has a few that are not wanted for serializing or deserializing. (Note that the @Expose annotation can also optionally be specified for just serialization or for just deserialization.)

A demo of Gson's ability to only include fields marked with @Expose:
public class GsonExposeFieldsDemo
{
public static void main(String[] args)
{
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation().create();

String json1 = gson.toJson(new Foo());
System.out.println(json1);
// output: {"b":"B"}
// only b was exposed during serialization

// input: {"a":"xyz","b":"42"}
String json2 = "{\"a\":\"xyz\",\"b\":\"42\"}";
Foo foo = gson.fromJson(json2, Foo.class);
System.out.println(foo.a); // output: A
System.out.println(foo.b); // output: 42
// only b was exposed during deserialization
}
}

class Foo
{
public String a = "A";
@Expose
public String b = "B";
}
Jackson provides the opposite annotation-based configurability: it allows inclusion of all fields from participating in serialization or deserialization, except those marked with the @JsonIgnore annotation. This is arguably convenient if a Java structure has a lot of fields, only few of which are not wanted for serializing or deserializing. However, this configuration mechanism is inconvenient, if alternatively a Java structure with a lot of fields only has a few that are wanted for serializing or deserializing.

A demo of Jackson's ability to exclude fields marked with @JsonIgnore:
public class JacksonIgnoreFieldsDemo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();

String json1 = mapper.writeValueAsString(new Bar());
System.out.println(json1);
// output: {"a":"A"}
// b was ignored during serialization

// input: {"a":"xyz","b":"42"}
String json2 = "{\"a\":\"xyz\",\"b\":\"42\"}";
Bar bar = mapper.readValue(json2, Bar.class);
System.out.println(bar.a); // xyz
System.out.println(bar.b); // B
// b was ignored during deserialization
}
}

class Bar
{
public String a = "A";
@JsonIgnore
public String b = "B";
}
Gson does not currently have a built-in feature comparable to Jackson's @JsonIgnore annotation (though the next section of the Gson user guide demonstrates an easy way to add this functionality with a custom ExclusionStrategy). To implement functionality comparable to Gson's @Expose feature, Jackson provides a few options.
  • The Jackson feature that most closely resembles the combination of Gson's excludeFieldsWithoutExposeAnnotation() & @Expose configurations is the ObjectMapper.setVisibilityChecker() & @JsonProperty combination. Instead of using setVisibilityChecker() to change visibility access globally, @JsonAutoDetect can be employed to configure visibility access per class.
  • For serialization only, Jackson provides custom Views configurations, which can be used to mimic Gson's @Expose.
  • Custom processing to exclude all fields except those marked with a particular annotation can also be implemented using a custom AnnotationIntrospector (demonstrated in the next section).
Following is an example of using the ObjectMapper.setVisibilityChecker() & @JsonProperty combination. (Note: This approach will be made slightly simpler once issue 621 is implemented.)
public class JacksonExposeFieldsDemo1
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setVisibilityChecker(
mapper.getVisibilityChecker()
.withCreatorVisibility(Visibility.NONE)
.withFieldVisibility(Visibility.NONE)
.withGetterVisibility(Visibility.NONE)
.withIsGetterVisibility(Visibility.NONE)
.withSetterVisibility(Visibility.NONE));

String json1 = mapper.writeValueAsString(new Foo());
System.out.println(json1);
// output: {"b":"B"}
// only b was exposed during serialization

// input: {"a":"xyz","b":"42"}
String json2 = "{\"a\":\"xyz\",\"b\":\"42\"}";
Foo foo = mapper.readValue(json2, Foo.class);
System.out.println(foo.a); // output: A
System.out.println(foo.b); // output: 42
// only b was exposed during deserialization
}
}

class Foo
{
public String a = "A";
@JsonProperty
public String b = "B";
}
Following is a similar approach using @JsonAutoDetect instead of configuring the ObjectMapper's VisibilityChecker.
public class JacksonExposeFieldsDemo2
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

String json1 = mapper.writeValueAsString(new Foo());
System.out.println(json1);
// output: {"b":"B"}
// only b was exposed during serialization

// input: {"a":"xyz","b":"42"}
String json2 = "{\"a\":\"xyz\",\"b\":\"42\"}";
Foo foo = mapper.readValue(json2, Foo.class);
System.out.println(foo.a); // output: A
System.out.println(foo.b); // output: 42
// only b was exposed during deserialization
}
}

@JsonAutoDetect(
creatorVisibility=Visibility.NONE,
fieldVisibility=Visibility.NONE,
getterVisibility=Visibility.NONE,
isGetterVisibility=Visibility.NONE,
setterVisibility=Visibility.NONE)
class Foo
{
public String a = "A";
@JsonProperty
public String b = "B";
}
Following is an example using a custom Views configuration. Again, note this approach applies to serialization only.
public class JacksonExposeFieldsForSerializationDemo
{
public static void main(String[] args) throws Exception
{
ObjectWriter writer = new ObjectMapper()
.configure(DEFAULT_VIEW_INCLUSION, false)
.viewWriter(Expose.class);

String json1 = writer.writeValueAsString(new Foo());
System.out.println(json1);
// output: {"b":"B"}

// Or, using ObjectMapper instead of ObjectWriter
ObjectMapper mapper = new ObjectMapper()
.configure(DEFAULT_VIEW_INCLUSION, false);
mapper.setSerializationConfig(
mapper.getSerializationConfig().withView(Expose.class));

String json2 = mapper.writeValueAsString(new Foo());
System.out.println(json2);
// output: {"b":"B"}
}
}

class Foo
{
public String a = "A";
@JsonView(Expose.class) public String b = "B";
}

// Used only as JsonView marker.
// Could use any existing class, like Object, instead.
class Expose {}
Comparison Ratings:
  • COMPARABLE for simple configurability to include only specified fields for participation in serialization/deserialization
  • +1 Jackson for simple configurability to exclude only specified fields for participation in serialization/deserialization (the Gson equivalent, demonstrated below, is almost as simple)
  • +1 Jackson for greater configurability options to include only specified fields for participation in serialization/deserialization (including global/class/field/getter/setter configuration options)

Excluding Fields From Serialization and Deserialization - User Defined Exclusion Strategies


Again, the Gson user guide provides us with an example that does not compile, along with incorrect example output, if the most obvious compiler error fixes were applied.

Following is a working example of Gson's custom ExclusionStrategy facility.
public class GsonExclusionStrategyDemo
{
public static void main(String[] args)
{
SampleObjectForTest src = new SampleObjectForTest();

Gson gson = new Gson();
System.out.println(gson.toJson(src));
// {"annotatedField":5,"stringField":"someDefaultValue",
// "longField":1234}

gson = new GsonBuilder().setExclusionStrategies(
new MyExclusionStrategy(String.class)).create();
System.out.println(gson.toJson(src));
// {"longField":1234}

// {"annotatedField":42,"stringField":"a string value",
// "longField":9876}
String json = "{\"annotatedField\":42," +
"\"stringField\":\"a string value\",\"longField\":9876}";

SampleObjectForTest srcCopy =
gson.fromJson(json, SampleObjectForTest.class);
System.out.println(srcCopy.annotatedField);
// output: 5 -- Field skipped during deserialization.
System.out.println(srcCopy.stringField);
// output: someDefaultValue -- Field skipped.
System.out.println(srcCopy.longField);
// output: 9876
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@interface Foo {}

class SampleObjectForTest
{
@Foo
public final int annotatedField;
public final String stringField;
public final long longField;

public SampleObjectForTest()
{
annotatedField = 5;
stringField = "someDefaultValue";
longField = 1234;
}
}

class MyExclusionStrategy implements ExclusionStrategy
{
private final Class<?> typeToSkip;

public MyExclusionStrategy(Class<?> typeToSkip)
{
this.typeToSkip = typeToSkip;
}

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

@Override
public boolean shouldSkipField(FieldAttributes f)
{
return f.getAnnotation(Foo.class) != null;
}
}
This example demonstrates two uses of custom exclusion strategies: 1. to exclude all fields of a particular type; and 2. to exclude all fields marked with a specified annotation. The comparable Jackson implementations of these two specific functionalities are: 1. the @JsonIgnoreType annotation; and 2. the @JsonIgnore annotation, briefly reviewed in the previous section.

Following is a Jackson solution that provides the same functionality as in the previous Gson demo to skip fields that are either marked with a particular annotation or of a particular type (java.lang.String in this case).
public class JacksonExcludeTypesAndPropertiesDemo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(new Bar()));
// {"stringField":"someDefaultValue","longField":1234}

mapper = new ObjectMapper();
mapper.getSerializationConfig().addMixInAnnotations(
String.class, IgnoreStringsMixIn.class);
mapper.getDeserializationConfig().addMixInAnnotations(
String.class, IgnoreStringsMixIn.class);
System.out.println(mapper.writeValueAsString(new Bar()));
// {"longField":1234}

// {"annotatedField":42,"stringField":"a string value",
// "longField":9876}
String json = "{\"annotatedField\":42," +
"\"stringField\":\"a string value\",\"longField\":9876}";

Bar bar = mapper.readValue(json, Bar.class);
System.out.println(bar.annotatedField);
// output: 5 -- Field skipped during deserialization.
System.out.println(bar.stringField);
// output: someDefaultValue -- Field skipped.
System.out.println(bar.longField);
// output: 9876
}
}

@JsonIgnoreType
abstract class IgnoreStringsMixIn {}

class Bar
{
@JsonIgnore
public final int annotatedField;
public final String stringField;
public final long longField;

public Bar()
{
annotatedField = 5;
stringField = "someDefaultValue";
longField = 1234;
}
}
This last example of course did not demonstrate a comparable Jackson solution to Gson's custom exclusion strategies. Gson's custom exclusion strategies provide the ability to exclude fields for conditions other than annotations or types. Any combination of factors including the field name, the applied annotations, the declared class, the declared type, and the declaring class can be considered. With Jackson, to exclude fields simply by name, the built-in feature is the @JsonIgnoreProperties annotation, to exclude fields simply by type is the @JsonIgnoreType annotation (as previously demonstrated), and to exclude fields simply by the declaring class is also the @JsonIgnoreType annotation. To equivalently combine arbitrary exclusion logic as the Gson exclusion strategy feature allows, a few different approaches with Jackson are available, partially including custom contextual serializers/deserializers, and/or (as "Unknown" commented below) custom Views, and/or custom Filters, and/or custom BeanSerializer and BeanDeserializer processing. But the simplest approach I've arrived at uses instead a custom AnnotationIntrospector, with which it's possible to implement exclusion logic based on any combination of the field name, the applied annotations, the parameterized generic type, the raw type, the declaring class, and the field modifiers, e.g., public, final. The following Jackson example closely replicates a Gson exclusion strategy.
class SampleObjectForTest
{
@Foo
public final int annotatedField;
public final String stringField;
public final long longField;

public SampleObjectForTest()
{
annotatedField = 5;
stringField = "someDefaultValue";
longField = 1234;
}
}

public class JacksonExclusionStrategyDemo
{
public static void main(String[] args) throws Exception
{
SampleObjectForTest src = new SampleObjectForTest();

ObjectMapper mapper = new ObjectMapper();

System.out.println(mapper.writeValueAsString(src));
// output: {"annotatedField":5,
// "stringField":"someDefaultValue","longField":1234}

Class[] ignorableClasses = { String.class };
Class[] ignorableAnnotations = { Foo.class };

mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(
new ExclusionAnnotationIntrospector(
ignorableClasses, ignorableAnnotations));

System.out.println(mapper.writeValueAsString(src));
// output: {"longField":1234}

// {"annotatedField":42,"stringField":"a string value",
// "longField":9876}
String json = "{\"annotatedField\":42," +
"\"stringField\":\"a string value\",\"longField\":9876}";

SampleObjectForTest srcCopy =
mapper.readValue(json, SampleObjectForTest.class);
System.out.println(srcCopy.annotatedField);
// output: 5 -- Field skipped during deserialization.
System.out.println(srcCopy.stringField);
// output: someDefaultValue -- Field skipped.
System.out.println(srcCopy.longField);
// output: 9876
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@interface Foo {}

class ExclusionAnnotationIntrospector
extends JacksonAnnotationIntrospector
{
Class[] ignorableClasses;
Class[] ignorableAnnotations;

ExclusionAnnotationIntrospector(
Class[] ignorableClasses, Class[] ignorableAnnotations)
{
this.ignorableClasses = ignorableClasses;
this.ignorableAnnotations = ignorableAnnotations;
}

public boolean shouldSkipClass(Class<?> clazz)
{
for (Class ignorableClass : ignorableClasses)
{
if (ignorableClass.equals(clazz))
return true;
}
return false;
}

public boolean shouldSkipField(AnnotatedField field)
{
for (Class ignorableAnnotation : ignorableAnnotations)
{
if (field.hasAnnotation(ignorableAnnotation))
return true;
}
return false;
}

@Override
public boolean isIgnorableField(AnnotatedField field)
{
if (shouldSkipClass(field.getRawType()) ||
shouldSkipField(field))
return true;
return super.isIgnorableField(field);
}

@Override
public boolean isHandled(Annotation ann)
{
Class clazz = ann.annotationType();
for (Class ignorableAnnotation : ignorableAnnotations)
if (ignorableAnnotation.equals(clazz))
return true;
return super.isHandled(ann);
}
}
With Jackson, another approach to filter properties during serialization is to make use of a FilterProvider. The following demonstrates using a FilterProvider to exclude all properties named "id" or "color" during serialization.
@JsonFilter("filter properties by name")
class PropertyFilterMixIn {}

class Bar
{
public String id = "42";
public String name = "Fred";
public String color = "blue";
public Foo foo = new Foo();
}

class Foo
{
public String id = "99";
public String size = "big";
public String height = "tall";
}

public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().addMixInAnnotations(
Object.class, PropertyFilterMixIn.class);

String[] ignorableFieldNames = { "id", "color" };
FilterProvider filters = new SimpleFilterProvider()
.addFilter("filter properties by name",
SimpleBeanPropertyFilter.serializeAllExcept(
ignorableFieldNames));
ObjectWriter writer = mapper.writer(filters);

System.out.println(writer.writeValueAsString(new Bar()));
// output:
// {"name":"James","foo":{"size":"big","height":"tall"}}
}
}
(I logged Jackson issue 724 to reduce the configuration code from this last example by a few lines. Don't hesitate to vote for its implementation.)

Comparison Ratings: COMPARABLE for simple configurability of arbitrary logic to exclude fields during serialization and deserialization

Continue to part 5...

References And Resources:

2 comments:

  1. Regarding Jackson, two parts of relevant functionality that might of interested would be @JsonView (http://wiki.fasterxml.com/JacksonJsonViews) and @JsonFilter (http://wiki.fasterxml.com/JacksonFeatureJsonFilter). And a general overview of options can be found at: [http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html].

    Of these, JsonView is more limited but simpler and more concise to use, and JsonFilter more powerful but also complicated. They may be more useful for cases where alternate configurations (views) are to be used, but at least JsonFilter could be used for using more flexible exclusion rules.

    ReplyDelete
  2. For a while I've wanted a model versioning feature in Jackson, so I recently sat down and wrote one. Hopefully this helps any Googlers who stumble in here.

    https://github.com/jonpeterson/jackson-module-model-versioning

    ReplyDelete