Two Jackson ObjectMapper generate different serializations - java

I have two ObjectMapper instances of Jackson. (Using v2.8.3)
The first one is instantiated as follows:
ObjectMapper objectMapper = new ObjectMapper();
The other one is autowired from the Spring Boot context via #Autowired.
Both have the same visibility configurations like this:
But they produce different serializations for the same object. The differences I realized are as follows:
Order of the serialized fields
Serialization of protected transient fields. (The autowired instance does not serialize them at all.)
Case differences in the serialized fields i.e. the former generates "monitoringUserID" while the latter "MonitoringUserID".
What I want is that the autowired mapper would also serialize the protected transient fields.
I'd also be glad if you tell me the reasons on the other differences in both serializations.
A simplified version of an example class being serialized:
public class ClauseType implements Serializable {
protected transient List<ClauseTypeNoteItem> noteItems;
public ClauseType() {
}
public List<ClauseTypeNoteItem> getNoteItems() {
...
}
public void setNoteItems(List<ClauseTypeNoteItem> value) {
...
}
}
Complete ClauseType class: https://pastebin.com/m3h1hesn
Complete ClauseTypeNoteItem class: https://pastebin.com/dmphNV4e
Edit:
I realized that both instances had difference mapper features. According to the docs, after enabling the DEFAULT_VIEW_INCLUSION featured of the autowired instance, they had the same mapper features represented as 1068991. However the differences are still there. I also realized that the autowired mapper has two registered modules: org.springframework.boot.jackson.JsonComponentModule and com.fasterxml.jackson.datatype.joda.JodaModule. I'm not sure whether the second module has an effect on the results I'm getting.
Thanks in advance.

It turned out that Spring injects a JaxbAnnotationIntrospector coming from jackson-module-jaxb-annotations maven dependency. During the instantiations of BeanDescriptions, the Jaxb introspector overrides the visibility checker settings that I provided. So, as a solution I will inject a new ObjectMapper bean and mark it as #Primary.
If you think of any other better solution, let me know

In Spring Boot the Jackson ObjectMapper is build and customised by Jackson2ObjectMapperBuilder object. The main source of configuration are the spring.jackson.* properties as explained in the docs:
74.3 Customize the Jackson ObjectMapper
Appendix A. Common application properties

Related

PMD Ignore Spring Field Injections in gradle config

Currently I have several Controllers that inject dependencies through a field marked with #Autowired. When I try to run gradle build the following violations come up.
These all correspond to the instances of where a spring component has been injected.
I am aware of the ignoreAnnotations property that seems to exist for PMD, however, I am not sure if one can actually specify this in a gradle configuration?
Any help would be appreciated.
The root cause is: field injection is code smell and bad design practice.
When #Autowired is used on a non-transient field it can cause issues with serialization. This is because the field will not be properly initialized when the object is deserialized, leading to a NullPointerException when the object is used.
To fix this issue either make the fields transient or -- much better -- use constructor injection. To use constructor injection add a constructor to your class that takes all of the required dependencies as arguments, and annotate the constructor with #Autowired (the annotation is optional in modern Spring versions).
#Component
class MyClass {
private final Dependency1 dependency1;
private final Dependency2 dependency2;
#Autowired
public MyClass(Dependency1 dependency1, Dependency2 dependency2) {
this.dependency1 = dependency1;
this.dependency2 = dependency2;
}
}

How to Serialize Interface inside Serializable Bean

I have a Serializable Bean class which consist of an interface instance dozerMapper of MapperIF. Everything was working fine before I added PersistentManager in the context.xm file in my tomcat server. With the PersistentManager I am storing all the Objects in session as a file to the folder. But after i added the PersistentManager it started throwing NotSerializableException because of the MapperIF interface inside my Bean class. Adding transient keyword to the MapperIF could solve the NotSerializableException, But it ends up with the NullPointerException as the dozerMapper.map is coming null in the below code. So how can i handle this situation for serializing my bean class.
#Autowired
private transient MapperIF dozerMapper;
public Preferences getUiPreferences() {
if (this.uiPreferences == null) {
this.uiPreferences = ((Preferences) this.dozerMapper.map(
getPrefernces(), Preferences.class));
}
return this.uiPreferences;
}
The MapperIF interface (or its Dozer 5.x replacement Mapper) does not extend Serializable. Its standard implementation classes do not implement it either. Therefore the standard implementations are not going to be serializable.
I can think of ways to solve this:
Don't put the MapperIF reference into an object that you save in the session. It doesn't really belong there. Here's what the javadocs for the DozerBeanMapper class say:
This should be used/defined as a singleton within your application. This class performs several one-time initializations and loads the custom xml mappings, so you will not want to create many instances of it for performance reasons. Typically a system will only have one DozerBeanMapper instance per VM. If you are using an IOC framework (i.e Spring), define the Mapper as singleton="true". If you are not using an IOC framework, a DozerBeanMapperSingletonWrapper convenience class has been provided in the Dozer jar.
This implies that you shouldn't need to put a MapperIF object into a session.
Declare the field as transient and implement a custom readObject method that will repopulate the field (from somewhere) when you deserialize.
Implement your own custom MapperIF / Mapper class that is serializable. (I haven't looked, but this could be a lot of work ... or impossible.)

Spring+Jackson: How to set visibility of a class without using #JsonAutoDetect annotation

In my spring-boot application, I have a global configuration on Jackson's ObjectMapper which told Jackson not to serialize object by fields but getters:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder()
{
return new Jackson2ObjectMapperBuilder()
{
#Override
public void configure(ObjectMapper objectMapper)
{
super.configure(objectMapper);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY);
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, Visibility.PUBLIC_ONLY);
}
};
}
However, now I'm dealing with a special case. I need to serialize a class which is not written by myself (a class form dependnecy library). Since the class does not declared getters, Jackson will ignore those fields.
Here's how the external class look like:
public class DirectionsResult
{
public GeocodedWaypoint geocodedWaypoints[];
public DirectionsRoute routes[];
}
Although using #JsonAutoDetect annotation can customerize a class's visibility for Jackson, this does not work with external classes.
So how can I set visibility of a class without using #JsonAutoDetect annotation and also not to change the global configuration?
You should be able to use jacksons MixIn feature. With this approach you can control all the configuration of a class by another class definition of your choice.
https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations
You can also check out one of my github projects to see the use of that feature:
https://github.com/Antibrumm/jackson-antpathfilter

How can I register the Scala Jackson Module in Riak-Java?

Needed to have support for Scala Lists and Options when storing to Riak. Looks like the Scala Module for Jackson would work for this. However, how would it be hooked into the object mapper in Riak? Not sure if I need to override something or if there's an annotation that can easily solve it.
https://github.com/FasterXML/jackson-module-scala
A few releases ago I added a getObjectMapper() method to the JSONConverter as someone had requested it. This is the Converter that is used for fetch/store operations if you're passing in POJOs and haven't created and passed in your own.
You'd want to instantiate the JSONConverter yourself and get the ObjectMapper from it:
JsonConverter<MyClass> converter = new JSONConverter<MyClass>(MyClass.class, bucketName);
ObjectMapper om = converter.getObjectMapper();
You can now register the module with the ObjectMapper and then use the JSONConverter with your fetch/store operations (using the withConverter() method of the StoreObject and FetchObject).
I think that's what you're looking for. If you needed more control over serializing/deserializing your objects you could also write your own Converter- I've written a cookbook entry on the subject here: https://github.com/basho/riak-java-client/wiki/Using-a-custom-Converter

Jackson JSON library: how to instantiate a class that contains abstract fields

I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info

Categories

Resources