Jackson serialization and lazy loading in Spring MVC - json

I'm trying to serialize an object with several lazily loaded properties, and I'm getting the following error:
Could not write content: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
After some searching I have tried this in my #Configuration class, but it doesn't seem to help:
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
Hibernate4Module hibernateModule = new Hibernate4Module();
hibernateModule.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, false);
objectMapper.registerModule(hibernateModule);
return objectMapper;
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper());
return converter;
}
When I change to FetchType.EAGER the application works fine.

The problem is related to the fact that related objects to the main one are not really loaded when you execute the query and not use the FetchType.EAGER; by using the FetchType.EAGER you tell hibernate: load the main entity and all the related entities; this can have not too much sense (you can risk to load all the database in one query)
Returning to jackson marshaller, when objects are "proxied" it is not able in serializing them
IMHO i'd do the following:
i'd create a DTO object to be serialized (I'd not serialize the Hibernate object)
i'd put in this DTO only the needed properties and data
if user need to see related objects a specific call would be executed and data related to the selected object will be loaded from DB and serialized (in specific DTO objects)

Related

Error thrown: "No serializer found for class java.lang.Long..." from controller while serializing JPA entity containing lazy "many-to-one" property

I am on Spring Boot 2.0.6, where an entity pet do have a Lazy many-to-one relationship to another entity owner
Pet entity
#Entity
#Table(name = "pets")
public class Pet extends AbstractPersistable<Long> {
#NonNull
private String name;
private String birthday;
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
#JsonProperty("ownerId")
#ManyToOne(fetch=FetchType.LAZY)
private Owner owner;
But while submitting a request like /pets through a client(eg: PostMan), the controller.get() method run into an exception as is given below:-
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.lang.Long and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.petowner.entity.Pet["ownerId"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.9.7.jar:2.9.7]
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1191) ~[jackson-databind-2.9.7.jar:2.9.7]
Controller.get implementation
#GetMapping("/pets")
public #ResponseBody List<Pet> get() {
List<Pet> pets = petRepository.findAll();
return pets;
}
My observations
Tried to invoke explicitly the getters within owner through pet to force the lazy-loading from the javaassist proxy object of owner within the pet. But did not work.
#GetMapping("/pets")
public #ResponseBody List<Pet> get() {
List<Pet> pets = petRepository.findAll();
pets.forEach( pet -> pet.getOwner().getId());
return pets;
}
Tried as suggested by this stackoverflow answer at https://stackoverflow.com/a/51129212/5107365 to have controller call to delegate to a service bean within the transaction scope to force lazy-loading. But that did not work too.
#Service
#Transactional(readOnly = true)
public class PetServiceImpl implements PetService {
#Autowired
private PetRepository petRepository;
#Override
public List<Pet> loadPets() {
List<Pet> pets = petRepository.findAll();
pets.forEach(pet -> pet.getOwner().getId());
return pets;
}
}
It works when Service/Controller returning a DTO created out from the entity. Obviously, the reason is JSON serializer get to work with a POJO instead of an ORM entity without any mock objects in it.
Changing the entity fetch mode to FetchType.EAGER would solve the problem, but I did not want to change it.
I am curious to know why it is thrown the exception in case of (1) and (2). Those should have forced the explicit loading of lazy objects.
Probably the answer might be connected to the life and scope of that javassist objects got created to maintain the lazy objects. Yet, wondering how would Jackson serializer not find a serializer for a java wrapper type like java.lang.Long. Please do rememeber here that the exception thrown did indicate that Jackson serializer got access to owner.getId as it recognised the type of the property ownerId as java.lang.Long.
Any clues would be highly appreciated.
Edit
The edited part from the accepted answer explains the causes. Suggestion to use a custom serializer is very useful one in case if I don't need to go in DTO's path.
I did a bit of scanning through the Jackson sources to dig down to the root causes. Thought to share that too.
Jackson caches most of the serialization metadata on first use. Logic related to the use case in discussion starts at this method com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(Collection<?> value, JsonGenerator g, SerializerProvider provider). And, the respective code snippet is:-
The statement serializer = _findAndAddDynamic(serializers, cc, provider) at Line #140 trigger the flow to assign serializers for pet-level properties while skipping ownerId to be later processed through serializer.serializeWithType at line #147.
Assigning of serializers is done at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.resolve(SerializerProvider provider) method. The respective snippet is shown below:-
Serializers are assigned at line #340 only for those properties which are confirmed as final through the check at line #333.
When owner comes here, its proxied properties are found to be of type com.fasterxml.jackson.databind.type.SimpleType. Had this associated entity been loaded eagerly, the proxied properties obviously won't be there. Instead, original properties would be found with the values that are typed with final classes like Long, String, etc. (just like the pet properties).
Wondering why can't Jackson address this from their end by using the getter's type instead of using that of the proxied property. Anyway, that could be a different topic to discuss :-)
This has to do with the way that Hibernate (internally what spring boot uses for JPA by default) hydrates objects. A lazy object is not loaded until some parameter of the object is requested. Hibernate returns a proxy which delegates to the dto after firing queries to hydrate the objects.
In your scenario, loading OwnerId does not help because it is the key via which you are referencing the owner object i.e. the OwnerId is already present in the Pet object, so the hydration will not take place.
In both 1 and 2, you have not actually loaded the owner object, so when Jackson tries to serialize it at the controller level it fails. In 3 and 4, the owner object has been loaded explicitly, which is why Jackson does not run into any issues.
If you want 2 to work then load some parameter of owner, other than id, and hibernate will hydrate the object, and then jackson will be able to serialize it.
Edited Answer
The problem here is with the default Jackson serializer. This inspects the class returned and fetches the value of each attribute via reflection. In the case of hibernate entities, the object returned is a delegator proxy class in which all parameters are null, but all getters are redirected to the contained instance. When the object is inspected, the values of each attribute are still null, which is defaulted to an error as explained here
So basically, you need to tell jackson how to serialize this object. You can do so by creating a serializer class
public class OwnerSerializer extends StdSerializer<Owner> {
public OwnerSerializer() {
this(null);
}
public OwnerSerializer(Class<Owner> t) {
super(t);
}
#Override
public void serialize(Owner value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", value.getId());
jgen.writeStringField("firstName", value.getFirstName());
jgen.writeStringField("lastName", value.getLastName());
jgen.writeEndObject();
}
}
And setting it as the default serializer for the object
#JsonSerialize(using = OwnerSerializer.class)
public class Owner extends AbstractPersistable<Long> {
Alternatively, you can create a new Object of type Owner from the proxy class, manually populate it and set it in the response.
It is a little roundabout, but as a general practice you should not expose your DTO's externally anyway. The controller/domain should be decoupled from the storage layer.

Use two differently configured ObjectMappers in one Spring Boot application

I am working on a middleware-app which deserializes values received via RestTemplate as json-String from a legacy-API (so, no influence on "their" data model and thus needing some custom config for my objectmapper consuming this api), and the app itself serves a restful API with (partially enriched and composited) data based on the legacydata as json, too.
Now, my legacy-Mapping-Classes' Constructors are all sharing a common structure like this at the moment:
...
private ObjectMapper mapper;
public MyMapper() {
this.mapper = new ObjectMapper();
this.mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
this.mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
...
because I use Jackson to de-serialize the json from the legacysystem. Basically I want to refactor this redundance using Springs DI Container.
So I tried to create my own Objectmapper #Component which just extends ObjectMapper like it is stated in some answers in this thread:
Configuring ObjectMapper in Spring - lets call it FromLegacyObjectMapper - instead of initializing my mapper in every class, so I created one and used
#Autowired
private FromLegacyObjectMapper
(or the constructorinjection-equivalent, but for simplicitys sake..).
But this had some serious sideeffects. Actually, I wasn't able to deserialize clientjson to viewmodels in my controllers anymore because of the rootvalue-wrapping, because the autowiring overwrites the spring boot standard objectmapper which I actually need when deserializing viewModels from my frontend.
I try to get it up and running like this:
frontend <---> My Api using Standard ObjectMapper <--> viewModel created by consuming legacy-Api-json using FromLegacyObjectMapper
So, what I surely could do is using a baseclass for my mappingclasses and just add the code above to the base constructor, and let every Mapperclass extend this base, but actually I hoped to find a way to use springs dependency injection container instead. I am out of ideas for now, so I hope anyone could help me out!
edit: To make it perhaps a bit clearer please see Moritz' answer below and our discussion in the comments. I am well aware I am able to use #Qualifier annotation, but this would just solve the problem if there is a way to add the #Qualifier to the standard objectmapper used in spring controllers. I'll do some research myself, but other answers are highly welcome.
I would try adding two different ObjectMappers to the Spring container. You could add something like this, for example to your Application class (assuming that is the one annotated with #SpringBootApplication):
#Bean
#Qualifier("fromLegacy")
public ObjectMapper fromLegacyObjectMapper() {
// create and customize your "from legacy" ObjectMapper here
return objectMapper;
}
#Bean
#Qualifier("default")
public ObjectMapper defaultObjectMapper() {
// create your default ObjectMapper here
return objectMapper;
}
Then you can inject the "from legacy" ObjectMapper in classes that use the legacy API like this:
public class SomeServiceUsingLegacyApi {
private final ObjectMapper objectMapper;
#Autowired
public SomeServiceUsingLegacyApi(#Qualifier("fromLegacy") ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
// [...]
}
And in other classes, using the other API accordingly:
public class SomeServiceUsingOtherApi {
private final ObjectMapper objectMapper;
#Autowired
public SomeServiceUsingOtherApi(#Qualifier("default") ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
// [...]
}

JSONObject Alternative in Spring and Jackson

I need to pass a map back to the web application.
I'm used to encapsulating the map in a JSONObject
http://json.org/java/
But since I am using Spring and Jackson Haus.
is there an easier way to maintain the pojo? May I can just annotate the MAP ?
Jackson has com.fasterxml.jackson.core.JsonNode, and specific subtypes like ObjectNode.
These form so-called Tree Model, which is one of 3 ways to handle JSON with Jackson -- some other libraries (like org.json) only offer this way.
So you should be able to just use JsonNode instead; there is little point in using org.json library; it is slow, and has outdated API.
Alternatively you can just use java.util.Map, and return that. Jackson can handle standard Lists, Maps and other JDK types just fine.
If you need to manipulate the output, ie, you don't want to provide all the fields of the object you can use JSonArray:
#RequestMapping(value = "/api/users", method = RequestMethod.GET)
public
#ResponseBody
String listUsersJson(ModelMap model) throws JSONException {
JSONArray userArray = new JSONArray();
for (User user : userRepository.findAll()) {
JSONObject userJSON = new JSONObject();
userJSON.put("id", user.getId());
userJSON.put("firstName", user.getFirstName());
userJSON.put("lastName", user.getLastName());
userJSON.put("email", user.getEmail());
userArray.put(userJSON);
}
return userArray.toString();
}
Use the example from here
Otherwise if you add jackson to your dependencies and set the controller method anotatted with #ResponseBody the response will automatically mapped to JSON. Check here for a simple example.

Can I configure Jackson JSON pretty printing from annotations or from Spring MVC controller?

I'm using Jackson 1.9.6 (codehaus) for JSON serialization of my response bodies in a Spring MVC application, and I'm having trouble finding a way to configure pretty printing. All of the code examples I've been able to find (like this and this) involve playing with an instantiation of ObjectMapper or ObjectWriter, but I don't currently use an instantiation of these for anything else. I wouldn't even know where to put this code. All of my Jackson configurations are taken care of by annotating the POJOs being serialized to JSON.
Is there a way to specify pretty printing in an annotation? I would think they would have put that in #JsonSerialize, but it doesn't look like it.
My class to be serialized looks like this:
#JsonAutoDetect
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class JSONObject implements Serializable{...}
and my Spring controller method looks like this:
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody List<Object> getMessagesAndUpdates(HttpServletRequest request, HttpServletResponse response) {
JSONObject jsonResponse = new JSONObject();
.
.
.
//this will generate a non-pretty-printed json response. I want it to be pretty-printed.
return jsonResponse;
}
I searched and searched for something similar and the closest I could find was adding this bean to my Application context configuration (NOTE: I am using Spring Boot so I am not 100% certain this will work as-is in a non-Spring Boot app):
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder()
{
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true);
return builder;
}
In my opinion, its the cleanest available solution and works pretty well.
Adding this as a separate answer so I can format the output.
As luck would have it, the non-Spring Boot solution wasn't too far from the Spring Boot solution :)
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}

how to obtain an unproxied and EAGER fetched object in Hibernate?

I want to load an objet and forget that it comes from hibernate! That's it, I just do something as:
MyClass myObject = MyClassDAO.getUnproxiedObject(objectID);
and than I have a real instance of myObj (and not from a Hibernate proxy) with all attributes set with the values from the database, so that I can't distinguish it from a manually created object.
In this thread a method is present to create an unproxied object, but it does not treats the issue of eager loding the objects, what I suppose is necessary for achieving my ultimate goals.
For those who are wondering why would I want such objects, I need to serialize then to Json with Gson, but I think it would have many other uses for many people.
Use FetchType.EAGER to eagerly load all the relations. Specifically for JSON serialization, if you are building a web application consider using an OpenSessionInView interceptor for your HTTP requests.
after testing I found out that the method given in the citted post did exactly what I was looking for.
The reason hibernate doesn't de-proxy while rendering with GSON is that GSON uses reflection to serialize the fields rather than using the getters of the Hibernate object. To workaround, you need to register a new TypeHierarchyAdapter that will de-proxy the object as GSON is serializing.
Here's my approach:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeHierarchyAdapter(HibernateProxy.class, new HibernateProxySerializer());
String jsonLove = gson.toJson(objToSerialize);
Here's the HibernateProxySerializer:
public class HibernateProxySerializer implements JsonSerializer<HibernateProxy> {
#Override
public JsonElement serialize(HibernateProxy proxyObj, Type arg1, JsonSerializationContext arg2) {
try {
GsonBuilder gsonBuilder = new GsonBuilder();
//below ensures deep deproxied serialization
gsonBuilder.registerTypeHierarchyAdapter(HibernateProxy.class, new HibernateProxySerializer());
Object deProxied = proxyObj.getHibernateLazyInitializer().getImplementation();
return gsonBuilder.create().toJsonTree(deProxied);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}