Strange Jackson exception being thrown when serializing Hibernate object
Asked Answered
M

15

62

Jackson is throwing a weird exception that I don't know how to fix. I'm using Spring, Hibernate and Jackson.

I have already considered that lazy-loading is causing the problem, but I have taken measures to tell Jackson to NOT process various properties as follows:

@JsonIgnoreProperties({ "sentMessages", "receivedMessages", "educationFacility" })
public class Director extends UserAccount implements EducationFacilityUser {
   ....
}

I have done the same thing for all the other UserAccount subclasses as well.

Here's the exception being thrown:

org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.ArrayList[46]->jobprep.domain.educationfacility.Director_$$_javassist_2["handler"])
    at org.codehaus.jackson.map.ser.StdSerializerProvider$1.serialize(StdSerializerProvider.java:62)
    at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:268)
    at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:146)
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:118)
    at org.codehaus.jackson.map.ser.ContainerSerializers$IndexedListSerializer.serializeContents(ContainerSerializers.java:236)
    at org.codehaus.jackson.map.ser.ContainerSerializers$IndexedListSerializer.serializeContents(ContainerSerializers.java:189)
    at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:111)
    at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:296)
    at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:224)
    at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:925)
    at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:153)

Suggestions on how I can get more info to see what's causing this? Anyone know how to fix it?

EDIT: I discovered that getHander() and other get*() methods exist on the proxy object. GRR!! Is there any way I can tell Jackson to not process anything on the proxy, or am I sol? This is really weird because the method that spits out the JSON only crashes under certain circumstances, not all the time. Nonetheless, it's due to the get*() methods on the proxy object.

Aside: Proxies are evil. They disrupt Jackson, equals() and many other parts of regular Java programming. I am tempted to ditch Hibernate altogether :/

Missionary answered 5/12, 2010 at 23:57 Comment(7)
Arr you returning an object of type Director from your controller?Boutonniere
@btiernay: Looks like it, yeah. The good news is that Tatu is pretty good at addressing these things.Boutonniere
@skaffman: I am returning a list of all UserAccount types, one of which is a Director... so yeah.Missionary
@btiernay: I don't know if it's Jira related. Is this a bug, or is this how it's supposed to work? If anything, I think this is Hibernate's fault. Or is it? I have no idea. I just want it fixed :(Missionary
The solution for me was simply adding the appropriate getters and setters as mentioned by OhadR.Algy
Can't agree more. Proxies are evil. They give Java a bad name. As does Hibernate.Clause
I you don't like proxies simply deactivate lazy loading.Chinua
B
38

It's not ideal, but you could disable Jackson's auto-discovery of JSON properties, using @JsonAutoDetect at the class level. This would prevent it from trying to handle the Javassist stuff (and failing).

This means that you then have to annotate each getter manually (with @JsonProperty), but that's not necessarily a bad thing, since it keeps things explicit.

Boutonniere answered 6/12, 2010 at 0:10 Comment(8)
Yep, that's pretty much what I did. It's not so bad I guess, but it makes some of my classes a little bloated with annotations all over the place. I guess it can't be helped.Missionary
@egervari: It's the way of the future, you know, like silver suits and robots.Boutonniere
For what it's worth, you can also use mix-in annotations (wiki.fasterxml.com/JacksonMixInAnnotations) to keep annotations separate; not more light-weight but bit cleaner.Bumble
@Missionary how did you disable auto-discovery of JSON properties using @JsonAutoDetect?Dodgson
You can disable Jackson Auto Discovery by @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAudtoDetect.Visibility.NONE)Dodgson
This helped me. I resolved this same issue by actually defining getters (I expected it to find attributes via reflection). As soon as there were getters, this error disappeared :)Yount
I also had to add isGetterVisibility = JsonAutoDetect.Visibility.NONEComa
If you use @ JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAudtoDetect.Visibility.NONE), the result will be a JSON without properties. I don't think it's the ideal solution.Odelet
T
74

I had a similar problem with lazy loading via the hibernate proxy object. Got around it by annotating the class having lazyloaded private properties with:

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

I assume you can add the properties on your proxy object that breaks the JSON serialization to that annotation.

Avoid Jackson serialization on non fetched lazy objects

Thermometry answered 4/1, 2011 at 7:27 Comment(1)
In my case, the exception was about a lazy proxy, but it was misleading. From the debugger I saw that the exception was thrown when it tried to serialize a property named handler. Adding this annotation, naturally, prevented jackson from attempting to serialize it. Right on the spot! Thanks a lot!Gallipot
B
38

It's not ideal, but you could disable Jackson's auto-discovery of JSON properties, using @JsonAutoDetect at the class level. This would prevent it from trying to handle the Javassist stuff (and failing).

This means that you then have to annotate each getter manually (with @JsonProperty), but that's not necessarily a bad thing, since it keeps things explicit.

Boutonniere answered 6/12, 2010 at 0:10 Comment(8)
Yep, that's pretty much what I did. It's not so bad I guess, but it makes some of my classes a little bloated with annotations all over the place. I guess it can't be helped.Missionary
@egervari: It's the way of the future, you know, like silver suits and robots.Boutonniere
For what it's worth, you can also use mix-in annotations (wiki.fasterxml.com/JacksonMixInAnnotations) to keep annotations separate; not more light-weight but bit cleaner.Bumble
@Missionary how did you disable auto-discovery of JSON properties using @JsonAutoDetect?Dodgson
You can disable Jackson Auto Discovery by @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAudtoDetect.Visibility.NONE)Dodgson
This helped me. I resolved this same issue by actually defining getters (I expected it to find attributes via reflection). As soon as there were getters, this error disappeared :)Yount
I also had to add isGetterVisibility = JsonAutoDetect.Visibility.NONEComa
If you use @ JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAudtoDetect.Visibility.NONE), the result will be a JSON without properties. I don't think it's the ideal solution.Odelet
A
23

i got the same error, but with no relation to Hibernate. I got scared here from all frightening suggestions, which i guess relevant in case of Hibernate and lazy loading... However, in my case i got the error since in an inner class i had no getters/setters, so the BeanSerializer could not serialize the data...

Adding getters & setters resolved the problem.

Aurlie answered 21/11, 2011 at 14:26 Comment(2)
This is really helped me !Gooseberry
How might I do the opposite? ie. I don't want to add a getter/setter but I want Jackson to skip that property.Wendall
B
9

For what it's worth, there is Jackson Hibernate module project that just started, and which should solve this problem and hopefully others as well. Project is related to Jackson project, although not part of core source. This is mostly to allow simpler release process; it will require Jackson 1.7 as that's when Module API is being introduced.

Bumble answered 7/12, 2010 at 17:6 Comment(0)
D
6

I had the same problem. See if you are using hibernatesession.load(). If so, try converting to hibernatesession.get(). This solved my problem.

Disinter answered 29/3, 2012 at 16:45 Comment(1)
This is exactly what was wrong with my code. Thanks.Hamnet
D
6

Similar to other answers, the problem for me was declaring a many-to-one column to do lazy fetching. Switching to eager fetching fixed the problem. Before:

@ManyToOne(targetEntity = StatusCode.class, fetch = FetchType.LAZY)

After:

@ManyToOne(targetEntity = StatusCode.class, fetch = FetchType.EAGER)
Duodecillion answered 17/10, 2016 at 15:24 Comment(0)
C
5

I had the same error message from spring's @RestController. My rest controller class was using spring's JpaRepository class and by replacing repository.getOne(id) method call with repository.findOne(id) problem was gone.

Collimore answered 25/4, 2014 at 6:27 Comment(1)
@Sllouyssgortyou can check my answer for Spring Boot solution. https://mcmap.net/q/89255/-spring-rest-exception-producing-json-data-duplicateMaretz
E
4

You can use jackson-datatype-hibernate module to solve this problem. It work for me. reference: https://github.com/FasterXML/jackson-datatype-hibernate

Encarnalize answered 10/2, 2014 at 2:26 Comment(0)
E
3

You could use @JsonIgnoreProperties(value = { "handler", "hibernateLazyInitializer" }) annotation on your class "Director"

Eddieeddina answered 20/7, 2012 at 8:50 Comment(0)
S
2

You can add a Jackson mixin on Object.class to always ignore hibernate-related properties. If you are using Spring Boot put this in your Application class:

@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
    Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
    b.mixIn(Object.class, IgnoreHibernatePropertiesInJackson.class);
    return b;
}


@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private abstract class IgnoreHibernatePropertiesInJackson{ }
Satiated answered 24/5, 2017 at 7:15 Comment(0)
K
1

Also you can make your domain object Director final. It is not perfect solution but it prevent creating proxy-subclass of you domain class.

Kodak answered 15/9, 2016 at 21:16 Comment(0)
A
0

I am New to Jackson API, when i got the "org.codehaus.jackson.map.JsonMappingException: No serializer found for class com.company.project.yourclass" , I added the getter and setter to com.company.project.yourclass, that helped me to use the ObjectMapper's mapper object to write the java object into a flat file.

Adhesion answered 8/6, 2012 at 9:52 Comment(0)
G
0

I faced the same issue and It is really strange that the same code works in few case whereas it failed in some random cases.

I got it fixed by just making sure the proper setter/getter (Making sure the case sensitivity)

Gooseberry answered 6/11, 2013 at 7:14 Comment(0)
B
0

I tried @JsonDetect and

@JsonIgnoreProperties(value = { "handler", "hibernateLazyInitializer" })

Neither of them worked for me. Using a third-party module seemed like a lot of work to me. So I just tried making a get call on any property of the lazy object before passing to jackson for serlization. The working code snippet looked something like this :

@RequestMapping(value = "/authenticate", produces = "application/json; charset=utf-8")
    @ResponseBody
    @Transactional
    public Account authenticate(Principal principal) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;
        LoggedInUserDetails loggedInUserDetails = (LoggedInUserDetails) usernamePasswordAuthenticationToken.getPrincipal();
        User user = userRepository.findOne(loggedInUserDetails.getUserId());
        Account account = user.getAccount();
        account.getFullName();      //Since, account is lazy giving it directly to jackson for serlization didn't worked & hence, this quick-fix.
        return account;
    }
Butterandeggs answered 12/3, 2015 at 20:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.