Jackson - Custom TypeId Resolver - java

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.

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;
}
}

Custom annotation based ignore field from serialisation

Consider a case that I have 2 instance of Object Mapper.
I want one must exclude fields that are annotated with some custom annotation from serialization
While other mapper includes(ignores annotation)
Like class has 3 fields a,b,c and c is annotated with some annotation (say #IgnoreField)
(Their will n number of class, each will have their Fields that are not meant to be serialized)
Now 1st object mapper o1 must serialize only a and b.
While 2nd object mapper o2 can serialize a,b and c.
This can happen with any class having different fields some of which may be annotated.
You can always implement a custom JsonSerializer and register it with your ObjectMapper.
class Bean {
#Ignore
String a;
String b;
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface Ignore {
}
class BeanWithIgnoredFieldsSerializer extends JsonSerializer<Bean> {
#Override
public void serialize(final Bean value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeStartObject();
try {
for (final Field f : Bean.class.getFields()) {
if (f.isAnnotationPresent(Ignore.class)) {
gen.writeStringField(f.getName(), (String) f.get(value));
}
}
} catch (final Exception e) {
//
}
gen.writeEndObject();
}
}
class BeanModule extends SimpleModule {
BeanModule() {
addSerializer(Bean.class, new BeanWithIgnoredFieldsSerializer());
}
}
void configure(final ObjectMapper om) {
om.registerModule(new BeanModule());
}
Note I have not tested this code, but that is the general idea how you add custom serializers to the OM. Adjust the code within the serialize method however you want.
Try configure SimpleBeanPropertyFilter for different condition.
#JsonFilter("someBeanFilter")
public class SomeBean {
}
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("someBeanFilter",SimpleBeanPropertyFilter.serializeAllExcept("aFild"));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
A distinct non-answer:
This is most likely a terrible idea.
You write code to communicate your intention. When you use that annotation, then you are telling "everybody" that these fields should be ignored.
A human reader looking at your code might spend half a day asking himself later "it says #IgnoreField for a and c , so why the heck are a, and c showing up serialized data?"
In other words, whatever problem you are trying to solve here, the answer is most likely not by hacking your way into ignoring annotations sometimes.
The next best "reasonable" solution might be: to rely on different custom annotations, like #IgnoreAlways and something like #OnlyIncludeForXyz. In other words: clearly express what might happen. Instead of using declarative programming, to then "lie" about what you declared.

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 ...
}
}

Helper method for #JsonProperty mapping in Jackson JSON

Does Jackson have a helper method to return the #JsonProperty annotation value (i.e., the JSON property key) given a bean field name?
Context:
I'm using Jackson to convert client-supplied JSON into a Bean and then using JSR-303 to validate the bean. When validation fails, I need to report a meaningful error message back to the client. The validation objects reference the bean property; the error message should reference the JSON property. Hence the need to map from one to the other.
You can get quite a bit of information via BeanDescription object, although getting one is pretty tricky (mostly since it's designed for Jackson's internal use mostly).
But this is used by a few Jackson extension modules, so it is supported use case. So:
ObjectMapper mapper = ...;
JavaType type = mapper.constructType(PojoType.class); // JavaType to allow for generics
// use SerializationConfig to know setup for serialization, DeserializationConfig for deser
BeanDescription desc = mapper.getSerializationConfig().introspect(type);
you can also safely upcast it to BasicBeanDescription if necessary.
This gives you access to lots of information; either list of logical properties (through which you can find getter/setter/field/ctor-argument that represents it), fully resolved methods (with annotations) and such. So hopefully that is enough.
Logical properties are useful since they contain both external name (one expected from JSON) and internal name derived from getter/setter.
I'm not aware of anything in Jackson to make this particularly easy. A reflections-based solution might suffice.
import java.lang.reflect.Field;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
// {"$food":"Green Eggs and Ham"}
String jsonInput = "{\"$food\":\"Green Eggs and Ham\"}";
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
Bar bar = mapper.readValue(jsonInput, Bar.class);
new Jsr303().validate(bar);
// output:
// I do not like $food=Green Eggs and Ham
}
}
class Bar
{
#JsonProperty("$food")
String food;
}
class Jsr303
{
void validate(Bar bar) throws Exception
{
Field field = Bar.class.getDeclaredField("food");
JsonProperty annotation = field.getAnnotation(JsonProperty.class);
System.out.printf("I do not like %s=%s", annotation.value(), bar.food);
}
}

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