Feature: JSON Views
Idea
"Views" in this context mean ability to define subsets of logical properties (things accessed via getters or fields) to serialize. Views are defined statically (using annotations), but view to use for serialization is chosen dynamically (per serialization).
Design
- Views themselves are identified by using Classes, instead of Strings (or dedicated objects). Reasons:
- With classes, can use inheritance to naturally (?) represent View hierarchies (if a field is part of a View, it'll be also part of parent view)
- Classes can be used as annotation values: Enums not (Enums would have been one other obvious possibility)
- For future extensibility, classes can also be annotated if need be (no plans for such annotations yet)
- View membership defined using annotations: specify which view(s)property will be included in. If no view annotation, assumed to mean View identified by Object.class: that is, included in all views
View to use for serialization specified dynamically; active view a property of SerializationConfig. Conceptually defaults to Object.class; as if no View functionality was used at all.
- Only single active view per serialization; but due to inheritance of Views, can combine Views via aggregation.
- All view membership inclusive, no need for exclusions? (however: could add an option to change default handling of 'unmarked' properties to mean "don't include unless specifically identified)
Implementation
1.4 implementation is used as follows.
First, defining views means declaring classes; you can reuse existing ones, or just create bogus classes -- they are just view identifiers with relationship information (child inherits view membership from parents):
// View definitions:
class Views {
static class Public { }
static class ExtendedPublic extends PublicView { }
static class Internal extends ExtendedPublicView { }
}
public class Bean {
// Name is public
@JsonView(Views.Public.class) String name;
// Address semi-public
@JsonView(Views.ExtendPublic.class) Address address;
// SSN only for internal usage
@JsonView(Views.Internal.class) SocialSecNumber ssn;
}With such view definitions, serialization would be done like so:
// short-cut: objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class); // or fully exploded: objectMapper.getSerializationConfig().setSerializationView(Views.Public.class); // (note: can also pre-construct config object with 'mapper.copySerializationConfig'; reuse) objectMapper.writeValue(out, beanInstance); // will use active view set via Config // or, starting with 1.5, more convenient (ObjectWriter is reusable too) objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
and result would only contain 'name', not 'address' or 'ssn'.
Customization
Although default implementation is not very customizable, underlying design does allow for implementing elaborate custom filtering, using alternative means of defining custom views. Here is how.
Enabling "view" processing
Depending on exactly how custom serialization is implemented, you may (or may not) need to enable view processing. If it is needed, you will just do something like:
objectMapper.getSerializationConfig().setSerializationView(SomeClass.class);
or, with 1.5 and beyond:
ObjectWriter w = objectMapper.viewWriter(SomeClass.class);
which offers same set of writeValue (and writeValueAsString, writeValueAsBytes, ...) write methods and can be reused easily (or passed).
How to plug your stuff in
What you need to implement is a sub-class of org.codehaus.jackson.map.ser.BeanPropertyWriter: this is what standard JsonView does; and make SerializerFactory (usually BeanSerializerFactory) construct BeanSerializer with customized versions. Once this is done, you can control output during serialization as you want.
There is some sample code (src/sample/CustomSerializationView.java) to show how exactly this is done.
