Does Jackson Without Annotations Absolutely Require Setters? - java

I'm using Jackson 1.6.4 and Java JDK 6.
I don't want to use Jackson annotations; I want to have immutable Java objects without setters.
The two requirements appear to conflict.
If I add private setters deserialization works fine.
I'm trying to not resort to private setters for my immutable objects - I'm stubborn that way.
I'm in the process of trying a custom implementation of VisibilityChecker to allow ANY field access.
But if anyone has some advice or lessons learned they can share I'd appreciate hearing them.
UPDATE: It's working.
Builder pattern, private constructor - a la Bloch "Effective Java".
It took setting deserialization configuration and visibility, but now it's good to go.
public class JsonMapper
{
private static final int INITIAL_SIZE = 2048;
/** See http://wiki.fasterxml.com/JacksonBestPracticeThreadSafety?highlight=(\bCategoryJackson\b) */
private static ObjectMapper mapper;
static
{
mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
SerializationConfig serializationConfig = mapper.getSerializationConfig();
serializationConfig.setDateFormat(Person.DEFAULT_FORMATTER);
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
deserializationConfig.setDateFormat(Person.DEFAULT_FORMATTER);
deserializationConfig.enable(DeserializationConfig.Feature.AUTO_DETECT_FIELDS);
mapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance().withFieldVisibility(JsonAutoDetect.Visibility.ANY));
}
public static <T> String serialize(T o) throws IOException
{
StringWriter sw = new StringWriter(INITIAL_SIZE);
mapper.writeValue(sw, o);
return sw.toString();
}
public static <T> T deserialize(String source, Class<T> targetClass) throws IOException
{
ByteArrayInputStream stream = new ByteArrayInputStream(source.getBytes());
TreeTraversingParser treeTraversingParser = new TreeTraversingParser(mapper.readTree(stream));
treeTraversingParser.setCodec(mapper);
return treeTraversingParser.readValueAs(targetClass);
}
}

Glad to hear you made it work -- ability to change auto-detection visibility levels is a very powerful feature, but there are so many features that it is not trivial to find all there is.
Couple of additional pointers: if you do not want to add Jackson annotations in POJOs, you can still use mix-in annotations. With this, you can use #JsonCreator to specify non-default constructor to use which allows true immutable value types (more on Jackson and immutable types on this article).
And finally: while builder pattern is not yet directly supported, it has been planned as per this Jira entry.

Related

Jackson force serialization of final fields without annotations

I have a class with a private constructor that I cannot modify containing many final fields that I wish to serialize with Jackson. Is there any way to force Jackson to serialize all the final fields?
I've tried using a custom filter provider like so: new ObjectMapper().setFilterProvider(new SimpleFilterProvider().addFilter("serialize-final", SimpleBeanPropertyFilter.serializeAll())) but Jackson seems to filter out the final fields before/after it applies my filter.
The use case is my program depends on a library that uses feature flags to indicate which features are enabled. The distributor of the library has compiled the feature flags into final fields on a singleton object that I am able to access at runtime. I wish to add the ability to dump the feature flags when requested to allow for easier debugging and simply serializing them as JSON seems like the easiest solution.
I assume the singleton does not have getters for the final fields (else it works out of the box). You could use the field visibility checker as suggested in this answer:
public class SerializeFinalFields {
#Test
public void doTest() throws JsonProcessingException {
final TestClass t = new TestClass("flag");
final ObjectMapper om = new ObjectMapper();
// Older jackson versions:
// om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// om.setVisibility(
// om.getSerializationConfig().getDefaultVisibilityChecker()
// .withFieldVisibility(JsonAutoDetect.Visibility.ANY));
// newer jackson versions:
om.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
final String json = om.writeValueAsString(t);
System.out.println(json);
}
}
class TestClass {
private final String myField;
/* package */ TestClass(final String myField) {
this.myField = myField;
}
}

Jackson - Custom TypeId Resolver

I've been using a custom typeId resolver for one of my classes, so far I've been leaning on the annotation support:
#JsonTypeInfo(
use = JsonTypeInfo.Id.CUSTOM,
include = JsonTypeInfo.As.PROPERTY,
property = "#type")
#JsonTypeIdResolver(ColumnDefinitionTypeResolver.class)
But now I need to customize the creation of the type resolver by passing some other dependencies to it via constructor or setters, and since Jackson is the one who instantiate it I can't find a way around it.
Is there a way to configure the ObjectMapper to use a TypeIdResolver instead of relying on annotations?
Regards
So you have two options:
1) If you're set on using the #JsonTypeIdResolver your stuck using static state in your TypeIdResolver. This probably isn't what you want.
The default JacksonAnnotationIntrospector will try to create an instance of the type you provide using JsonTypeIdResolver per its default constructor. There is currently no way to configure it to do otherwise.
public final class ColumnDefinitionTypeResolver implements TypeIdResolver {
// You could rely on static state.
public static String SOME_ACCESSIBLE_OBJECT = null;
public ColumnDefinitionTypeResolver() {
// This is what gets called.
}
}
ColumnDefinitionTypeResolver.SOME_ACCESSIBLE_OBJECT = "I can affect the implementation from here, but using static state ... be careful";
2) Is create a module to handle deserialization of your type and subtypes.
SimpleModule columnDefinitionModule = new SimpleModule("colDefMod", new Version(1, 0, 0, null))
.addDeserializer(ColumnDefinition.class, new JsonDeserializer() {
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// Need to read the type out and then use ObjectMapper to deserialize using the correct token
}
})
.registerSubtypes(...); // add your subtypes here.
(new ObjectMapper()).registerModule(columnDefinitionModule);
For more detailed examples, see Jackson documentation How-To: Custom Deserializers.
You can also set a custom type id resolver programmatically. Look at the top answer here.
Look for this line:
typeResolver.init(JsonTypeInfo.Id.CLASS, null);
Replace null with your type id resolver.

Serialization Of Polymorphic Array Failing

I have enabled Polymorphic serialization support by adding annotations on the base class. I am able to seriazlize an individual object successfully and it is writing the type information as part of serialized data. However, the same is not happening if I store the objects in a list and serialize it.
It seems this issue was fixed in 1.6.3 (http://jira.codehaus.org/browse/JACKSON-362)
I am using Jackson 2.3.2 and still facing the issue.
Does somebody know how to fix this?
Code:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,property = "type")
#JsonSubTypes({#Type(value = Derived.class, name = "derived")})
public abstract class Base {
}
public class Derived extends Base {
private String field;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Derived d = new Derived();
d.setField("Name");
Base b = d;
System.out.println(mapper.writeValueAsString(b));
List<Base> list = new ArrayList<Base>();
list.add(d);
System.out.println(mapper.writeValueAsString(list));
}
}
Output:
{"type":"derived","field":"Name"}
[{"field":"Name"}]
Thanks,
Praveen
The answer is at https://github.com/FasterXML/jackson-databind/issues/699
This is due to Java type erasure: when serializing a List, all Jackson see as a type is List (roughly equivalent to List). And since type Object does not have polymorphic type information (annotation), none will be written.
So this is not a bug in Jackson, but an unfortunate feature of Java Type Erasure.
It does not apply to arrays, since they retain element type information (arrays are not generic; arrays of different types are different classes, whereas generic typing is mostly compile-time syntactic sugar).
There are three main ways to deal with this:
pass full generic type using TypeReference (ObjectMapper has method like mapper.writerFor(new TypeReference<List<Base>>() { }).writeValue(....)
Sub-class List to something like public class BaseList extends ArrayList<Base>() { }, and pass that: this type WILL retain type information
Avoid using root-level List and Maps
I personally recommend doing (3), since this avoids all related problems with type erasure.
In my opinion JSON root value should always be a JSON Object, usually serialized to/from POJO.
Approach (2) will however work, and this is what most users do. It does require use of an additional helper class.
Approach (1) may or may not work; problem being that forcing type information does also affect actual value serialization. So while it will add type id, it may result in some properties not being serialized.
This problem can be solved by using arrays, instead of list (since list does type erasure):
for example, your above test-case could be written as:
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Derived d = new Derived();
d.setField("Name");
Base b = d;
System.out.println(mapper.writeValueAsString(b));
List<Base> list = new ArrayList<Base>();
list.add(d);
System.out.println(mapper.writeValueAsString(
list.toArray(new Base[list.size]) // <--This Part
));
}
}
I had the same issue with object array. Object[] doesn't carry type information but individual objects do. It's a shame that jackson doesn't handle that automatically.
Two possible solutions:
1. Typed array serialization works just fine:
Base[] myArray = Base[]{d};
mapper.writeValueAsString(myArray)
this will actually produce expected result as Base[] has type information.
I solved that my issue with custom serializer.
Serializer:
public class ObjectArraySerializer extends StdSerializer<Object[]> {
public ObjectArraySerializer(final Class<Object[]> vc) {
super(vc);
}
#Override
public void serialize(
final Object[] data,
final JsonGenerator gen,
final SerializerProvider provider) throws IOException {
gen.writeStartArray();
for (Object obj : data) {
gen.writeObject(obj);
}
gen.writeEndArray();
}
}
ObjectMapper configuration:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(
Object[].class,
new ObjectArraySerializer(Object[].class));
objectMapper.registerModule(module);

Serialize one class in two different ways with Jackson

In one of our projects we use a java webapp talking to a MongoDB instance. In the database, we use DBRefs to keep track of some object relations. We (de)serialize with POJO objects using jackson (using mongodb-jackson-mapper).
However, we use the same POJOs to then (de)serialize to the outside world, where our front end deals with presenting the JSON.
Now, we need a way for the serialization for the outside world to contain the referenced object from a DBRef (so that the UI can present the full object), while we obviously want to have the DBRef written to the database, and not the whole object.
Right now I wrote some untested static nested class code:
public static class FooReference {
public DBRef<Foo> foo;
// FIXME how to ensure that this doesn't go into the database?
public Foo getFoo() {
return foo.fetch();
}
}
Ideally I would like a way to annotate this so that I could (de)serialize it either with or without the getFoo() result, probably depending on some configuration object. Is this possible? Do you see a better way of going about doing this?
From looking at options, it seems you can annotate properties to only be shown if a given View is passed to the ObjectMapper used for serialization. You could thus edit the class:
public static class FooReference {
public DBRef<Foo> foo;
#JsonView(Views.WebView.class)
public Foo getFoo() {
return foo.fetch();
}
}
and provide:
class Views {
static class WebView { }
}
and then serialize after creating a configuration with the correct view:
SerializationConfig conf = objectMapper.getSerializationConfig().withView(Views.WebView.class);
objectMapper.setSerializationConfig(conf);
Which would then serialize it. Not specifying the view when serializing with the MongoDB wrapper would mean the method would be ignored. Properties without a JsonView annotation are serialized by default, a behaviour you can change by specifying:
objectMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
More info is available on the Jackson Wiki.
There are still other alternatives, too, it turns out: there are Jackson MixIns which would let you override (de)serialization behaviour of parts of a class without modifying the class itself, and as of Jackson 2.0 (very recent release) there are filters, too.
Use a custom JSONSerializer and apply your logic in the serialize method:
public static class FooReference {
public DBRef<Foo> foo;
#JsonSerialize(using = CustomSerializer.class)
public Foo getFoo() {
return foo.fetch();
}
}
public class CustomSerializer extends JsonSerializer<Object> {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
// jgen.writeObjectField ...
}
}

Xstream: Implicitly ignoring all fields

How do I tell Xstream to serialize only fields which are annotated explicitly and ignore the rest?
I am trying to serialize a hibernate persistent object and all proxy related fields get serialized which I don’t want in my xml.
e.g.
<createdBy class="com..domain.Users " reference="../../values/createdBy"/>
is not something I want in my xml.
Edit: I don’t think I made this question clear. A class may inherit from a base class on which I have no control (as in hibernate’s case) on the base class properties.
public class A {
private String ShouldNotBeSerialized;
}
public class B extends A {
#XStreamAlias("1")
private String ThisShouldbeSerialized;
}
In this case when I serialize class B, the base class field ShouldNotBeSerialized will also get serialized. This is not something I want. In most circumstances I will not have control on class A.
Therefore I want to omit all fields by default and serialize only fields for which I explicitly specify the annotation. I want to avoid what GaryF is doing, where I need to explicitly specify the fields I need to omit.
You can omit fields with the #XstreamOmitField annotation. Straight from the manual:
#XStreamAlias("message")
class RendezvousMessage {
#XStreamOmitField
private int messageType;
#XStreamImplicit(itemFieldName="part")
private List<String> content;
#XStreamConverter(SingleValueCalendarConverter.class)
private Calendar created = new GregorianCalendar();
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
I can take no credit for this answer, just sharing what I have found. You can override the wrapMapper method of the XStream class to achieve what you need.
This link explains in detail: http://pvoss.wordpress.com/2009/01/08/xstream/
Here is the code you need if you don't want the explanation:
// Setup XStream object so that it ignores any undefined tags
XStream xstream = new XStream() {
#Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new MapperWrapper(next) {
#Override
public boolean shouldSerializeMember(Class definedIn,
String fieldName) {
if (definedIn == Object.class) {
return false;
}
return super
.shouldSerializeMember(definedIn, fieldName);
}
};
}
};
You might want to do all your testing before you implement this code because the exceptions thrown by the default XStream object are useful for finding spelling mistakes.
There was already a ticket for the XStream people:
Again, this is by design. XStream is a serialization tool, not a data
binding tool. It is made to serialize Java objects to XML and back. It
will write anything into XML that is necessary to recreate an equal
object graph. The generated XML can be tweaked to some extend by
configuration for convenience, but this is already an add-on. What you
like to do can be done by implementing a custom mapper, but that's a
question for the user's list and cannot be handled here.
http://jira.codehaus.org/browse/XSTR-569
I guess the only direct way is to dive into writing a MapperWrapper and exclude all fields you have not annotated. Sounds like a feature request for XStream.

Categories