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 :/
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
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.
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.
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.
I had the same problem. See if you are using hibernatesession.load(). If so, try converting to hibernatesession.get(). This solved my problem.
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)
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.
You can use jackson-datatype-hibernate module to solve this problem. It work for me.
reference: https://github.com/FasterXML/jackson-datatype-hibernate
You could use #JsonIgnoreProperties(value = { "handler", "hibernateLazyInitializer" }) annotation on your class "Director"
I got the same issue, salutations are here
Avoid Jackson serialization on non fetched lazy objects
http://blog.pastelstudios.com/2012/03/12/spring-3-1-hibernate-4-jackson-module-hibernate/
https://github.com/nessonqk/jackson-datatype-hibernate
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{ }
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.
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)
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;
}
Also you can make your domain object Director final. It is not perfect solution but it prevent creating proxy-subclass of you domain class.
Related
I have a class ActivitiesModel which uses Lombok's SuperBuilder.
import jakarta.validation.NotBlank;
// other imports and statements omitted for brevity.
#Data
#SuperBuilder
#NoArgsConstructor
public class ActivitiesModel {
public static final String ACTIVITIES_NOT_NULL_MESSAGE = "Activities cannot be null";
public static final String ACTIVITY_NOT_BLANK_MESSAGE = "Activity cannot be blank";
#NotNull(message = ACTIVITIES_NOT_NULL_MESSAGE)
private List<#NotBlank(message = ACTIVITY_NOT_BLANK_MESSAGE) String> activities;
}
I am using this builder to create an object of ActivitiesModel, and then validating it using Hibernate's Validator interface:
// Somewhere else in the application.
// Create an object using the builder method.
ActivitiesModel activitiesModel = ActivitiesModel.builder()
.activities(List.of("hello", "world")) // <----- Point A
.build();
// Validate the object using Hibernate's validator.
validator.validate(activitiesModel);
However, running this code gives me the following error:
java.lang.Error:
Unresolved compilation problem:
Type mismatch: cannot convert from List<String> to List<E>
The stack trace seems to be pointing at Point A.
I have tried the following approaches:
Replacing the #SuperBuilder with #Builder and #AllArgsConstructor.
Replacing the message attribute with a string literal instead of a static final variable, i.e:
private List<#NotBlank(message = "Activity cannot be blank") String> activities;
1st approach seems to fix this error, however, it's not something I can use as I need to extend the builder functionality to a subclass of ActivitiesModel. Also, this issue is also present in another abstract class, so the super builder functionality for parent classes is definitely required.
2nd approach also works in solving the error. However, going with it is a bit problematic because I then need to have the same message string in the validation test for this model class, which is something I would like to avoid as it duplicates the string.
Another thing to note is that this error only seems to occur in the presence of an annotation on the generic type parameter of the container, which is NotBlank in this case. It is not influenced by any annotations which are present directly on the field itself (NotNull in this case).
So, all in all, these are the questions that I would like to get some answers to:
Somehow, Lombok is able to figure out the types in case of a string literal but not in case of a static final String. Why is that?
Am I going about this totally wrong? The problem occurs because I'm trying to store the message string in a variable, and I'm trying to re-use the same variable at two places: the annotation's message attribute, and in the validation test for the model class. Should I not be checking for the presence of the message in my validation tests, but be checking for something else instead?
For anyone who comes across this later on, the research for this issue has led me to believe that comparing message strings in tests is not the way to go about writing validation test cases. Another downside to this approach is that you might have different validation messages for different locales. In that case, the message string itself might be a template e.g. my.message.key with its values in a ResourceBundle provided to Hibernate, i.e. files such as ValidationMessages.properties and ValidationMessages_de.properties.
In such a scenario, you could compare message for one locale in your validation test case, however, a better approach might be to check the annotation and the field for which the validation has failed. We can get both of these pieces of information via the ConstraintViolation and subsequently the ConstraintDescriptor types, provided by Hibernate. This way we can circumvent checking the message itself, but rely on the actual validation annotation which has failed.
As for the solution to this question, it seems it was a build cache issue. Cleaning maven's build cache results in this code working perfectly fine, but VSCode still seems to have an issue. For now, I will choose to ignore that.
I'm generating Rest endpoints including adding Openapi/Swagger annotations to the generated code.
While it works quite well with basic types, I have some problems with custom classes.
Right now I have a lot of duplicate schema entries for the custom classes (using #Schema(implementation = MyClass.class)) but at least the needed information is there. However I'd like to find a way to remove the duplicate schema entries while retaining the additional information.
On a github-issue discussing the $ref and lack of sibling properties I found an example how you would write it manually in yaml in order to get the result I'm looking for, however I can't figure out how to set the annotations to produce it.
This is how I think the annotation should look like if I follow the example (just to be on the safe side it is added to both the getter and the setter):
import io.swagger.v3.oas.annotations.media.Schema;
...
public class SepaPaymentRequest {
...
#Schema(name = "w307BetrBeg", description = "BETRAG BEGUENSTIGTER ", allOf = { com.diesoftware.services.utils.Betrag.class }, required = true)
public void setW307BetrBeg(final Betrag w307BetrBeg) {
this.w307BetrBeg = w307BetrBeg;
}
...
}
However what I get when I fetch the openapi.yaml (snippet):
w307BetrBeg:
$ref: '#/components/schemas/Betrag'
What I'd like to have:
w307BetrBeg:
title: 'Betrag'
description: 'BETRAG BEGUENSTIGTER'
allOf:
- $ref: '#/components/schemas/Betrag'
Any hints are more than welcome.
I haven't found a way to do it using annotations, i.e. by annotating the class.
I think it's possible to do, by:
Creating a model
Injecting the model using a ModelConverter
When I say "a model" I mean an instance of io.swagger.v3.oas.models.media.Schema.
In particular I think you'd want to create and inject a io.swagger.v3.oas.models.media.ComposedSchema instance, which supports allOf.
Doing this (i.e. creating model instances) isn't very different from hand-writing the YAML.
Another possibility -- which I haven't tried -- might be to write a slightly different ModelConverter, which you install into the chain of converters. Then, intercept calls to resolve which return a SchemaObject whose name is Betrag, and (sometimes?) replace that with a ComposedSchema instance which uses allOf.
After detecting a flaw in one of my web services I tracked down the error to the following one-liner:
return this.getTemplate().getDomains().stream().anyMatch(domain -> domain.getName().equals(name));
This line was returning false when I positively knew that the list of domains contained a domain which name was equal to the provided name. So after scratching my head for a while, I ended up splitting the whole line to see what was going on. I got the following in my debugging session:
Please notice the following line:
List<Domain> domains2 = domains.stream().collect(Collectors.toList());
According to the debugger, domains is a list with two elements. But after applying .stream().collect(Collectors.toList()) I get a completely empty list. Correct me if I'm wrong, but from what I understand, that should be the identity operation and return the same list (or a copy of it if we are strict). So what is going on here???
Before you ask: No, I haven't manipulated that screenshot at all.
To put this in context, this code is executed in a stateful request scoped EJB using JPA managed entities with field access in a extended persistence context. Here you have some parts of the code relevant to the problem at hand:
#Stateful
#RequestScoped
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class DomainResources {
#PersistenceContext(type = PersistenceContextType.EXTENDED) #RequestScoped
private EntityManager entityManager;
public boolean templateContainsDomainWithName(String name) { // Extra code included to diagnose the problem
MetadataTemplate template = this.getTemplate();
List<Domain> domains = template.getDomains();
List<Domain> domains2 = domains.stream().collect(Collectors.toList());
List<String> names = domains.stream().map(Domain::getName).collect(Collectors.toList());
boolean exists1 = names.contains(name);
boolean exists2 = this.getTemplate().getDomains().stream().anyMatch(domain -> domain.getName().equals(name));
return this.getTemplate().getDomains().stream().anyMatch(domain -> domain.getName().equals(name));
}
#POST
#RolesAllowed({"root"})
public Response createDomain(#Valid #EmptyID DomainDTO domainDTO, #Context UriInfo uriInfo) {
if (this.getTemplate().getLastVersionState() != State.DRAFT) {
throw new UnmodifiableTemplateException();
} else if (templateContainsDomainWithName(domainDTO.name)) {
throw new DuplicatedKeyException("name", domainDTO.name);
} else {
Domain domain = this.getTemplate().createNewDomain(domainDTO.name);
this.entityManager.flush();
return Response.created(uriInfo.getAbsolutePathBuilder().path(domain.getId()).build()).entity(new DomainDTO(domain)).type(MediaType.APPLICATION_JSON).build();
}
}
}
#Entity
public class MetadataTemplate extends IdentifiedObject {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "metadataTemplate", orphanRemoval = true) #OrderBy(value = "creationDate")
private List<Version> versions = new LinkedList<>();
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) #OrderBy(value = "name")
private List<Domain> domains = new LinkedList<>();
public List<Version> getVersions() {
return Collections.unmodifiableList(versions);
}
public List<Domain> getDomains() {
return Collections.unmodifiableList(domains);
}
}
I've included both getVersions and getDomains methods because I have similar operations running flawlessly on versions. The only significant difference I'm able to find is that versions are eagerly fetched while domains are lazily fetched. But as far as I know the code is being executed inside a transaction and the list of domains is being loaded. If not I'd get a lazy initialization exception, wouldn't I?
UPDATE: Following #Ferrybig 's suggestion I've investigated the issue a bit further, and there doesn't seem to have anything to do with improper lazy loading. If I traverse the collection in a classic way I still can't get proper results using streams:
boolean found = false;
for (Domain domain: this.getTemplate().getDomains()) {
if (domain.getName().equals(name)) {
found = true;
}
}
List<Domain> domains = this.getTemplate().getDomains();
long estimatedSize = domains.spliterator().estimateSize(); // This returns 0!
domains.spliterator().forEachRemaining(domain -> {
// Execution flow never reaches this point!
});
So it seems that even when the collection has been loaded you still have that odd behavior. This seems to be a missing or empty spliterator implementation in the proxy used to manage lazy collections. What do you think?
BTW, this is deployed on Glassfish / EclipseLink
The problem here comes from a combination of somebody else's mistakes in several places. The sum of all those mistakes provokes this buggy behavior.
First mistake: Dubious inheritance. EclipseLink seems to create a proxy to manage lazy collections of type org.eclipse.persistence.indirection.IndirectList. This class extends java.util.Vector although it overrides everything but removeRange. Why on earth, dear Eclipse developers, do you extend a class to override almost everything in the parent, instead of declaring that class to implement a suitable interface (Iterable<E>, Collection<E> or List<E>)?
Second mistake: Hey, I inherit from you but don't give a $#|T about your internals. So IndirectList does its magic of lazy loading things using a delegate. But, oh my! How do I compute size? Do I use (and maintain updated) the parent's elementCount property? No, of course, I just delegate that task to my delegate... so if the parent class needs to do anything related to size, well, bad luck. Anyway I've overrided everything... and they won't add anything new to that class, will they?
Third mistake: Encapsulation breakage. Enters Vector. In Java 1.8 this class is augmented and now provides a spliterator method to support the new stream functionalities. They create a static inner class (VectorSpliterator) that lets clients traverse the vector using the shiny new API. Everything ok until you notice that in order to know when to finish the traversal they use the protected instance variable elementCount instead of using the public API method size(). Because who would extend a non final class and return a size not based on elementCount? Do you see the disaster coming?
So here we are, IndirectList is unawarely inheriting new functionality from Vector (remember that it probably shouldn't inherit from it in first place), and breaking things with this combination of mistakes.
Summing up, it seems that stream traversal of lazy collections won't work even for already loaded collections when using EclipseLink (default JPA provider in Glassfish). Remember that these products come from the same vendor. Hooray!
WORKAROUND: In case you face this problem and still want to leverage the functional programming style provided by stream() you can make a copy of the collection so a proper iterator is built. In my case I was able to keep all similar uses of domains as one-liners modifying the getDomains method. I favor code readability (with functional style) over performance in this case:
public List<Domain> getDomains() {
return Collections.unmodifiableList(new ArrayList<>(domains));
}
NOTE TO THE READER: Sorry for the sarcasm, but I hate to lose my precious development time with these things.
Thanks to #Ferrybig for the initial clue
UPDATE: Bug reported. If this has hit you, you can follow its progress at https://bugs.eclipse.org/bugs/show_bug.cgi?id=487799
I've hit a very similar issue with this code in a unit test:
Optional<ChildTable> ct = st.getChildren().stream().filter(i -> i.getId().equals(20001000l)).findFirst();
ct.get() failed with a NoSuchElementException.
Updating EclipseLink from 2.5.2 to 2.6.2 solved this issue. You didn't mention the EclipseLink version.
I think your bug report is a duplicate of https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075.
See also the unresolved bug with EclipseLink and Java 8 stream API https://bugs.eclipse.org/bugs/show_bug.cgi?id=467470.
I'm using the last version at the moment of Firebase dependency, which is 1.0.2 and I'm having problems into getting my pojos parsed correctly.
The thing is, at any time the schema can changed but I don't want my app to crash with this:
D/AndroidRuntime(14097): Shutting down VM W/dalvikvm(14097):
threadid=1: thread exiting with uncaught exception (group=0x40a451f8)
E/AndroidRuntime(14097): FATAL EXCEPTION: main
E/AndroidRuntime(14097): com.firebase.client.FirebaseException: Failed
to bounce to type E/AndroidRuntime(14097): at
com.firebase.client.DataSnapshot.getValue(DataSnapshot.java:213)
Looking into the dependency tree I get that Firebase is using Jackson mapper 1.9.7, so the annotation #JsonIgnoreProperties(ignoreUnknown = true") is not an option. Moreover, the object mapper is wrapped into this Firebase object so I can't configure the DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES property (DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES for Jackson 1.9 and before).
Is there any way to set this property, either as a class-level annotation or configuring the mapper or any other mechanism whatsoever?
The best solution would be that Firebase 1.0.3 started using Jackson 2.0, but don't know if this is something they care about right now.
Note: I've already thought about excluding the transitive Jackson 1.9.7 dependency and adding Jackson 2.0 so that I can access to this ignoreUnknown feature, but I don't think it is a viable choice since I would be changing the mayor version.
For those who have moved over to Google's official version of Firebase (As of May 29, 2016), you can use #Exclude instead of #JsonIgnore or #JsonProperty. Here is the link to their document.
Example:
public class dataPacket{
public String data;
...
#Exclude
public String getData(){return data;}
}
Update:
As others pointed, annotation #Exclude is right way to use it now. But if you use Kotlin that won't work. For Kotlin use
#Exclude #JvmField
var data: String? = nil
//or
#set:Exclude #get:Exclude
var data: String? = nil
Because annotation can be applied only for generated fields and not to properties.
Old answer:
I'm coming to Firebase from GSON were I used transient keyword. And that works with Firebase too
public transient String data;
As the accepted answer states, Firebase now uses Jackson, so you can annotate the desired methods you wish to ignore with
#JsonIgnore
Edit:
Firebase changed everything. Woot. Now use this instead:
#Exclude
Firebase 1.0.3 was released and now uses Jackson 2.2.2, so annotation #JsonIgnore is the way to go.
Edit:
as of now in 2017, Firebase doesn't use Jackson anymore. the correct annotation is #Exclude.
I'm using Jackson's readValue() method on an object mapper to read from a JSON file and convert it into my java object.
eg.
mapperObject.readValue( node, MyTargetClass.class )
Are there any annotations that I can set on MyTargetClass to enforce required attributes? For example, if I have a JSON object with properties ABC,DEF and GHI, and my Json is the following
{
"ABC" : "somevalue"
"DEF" : "someothervalue"
}
I want it to fail somehow, and only succeed on the readValue if it contained ABC, DEF and GHI.
You can mark a property as required with the #JsonProperty(required = true) annotation, and it will throw a JsonMappingException during deserialization if the property is missing or null.
Edit: I received a downvote for this without comment. I'd love to know why, since it does exactly the right thing.
Jackson does not include validation functionality, and this is by design (i.e. that is considered out-of-scope). But what is usually used is Bean Validation API implementation.
The nice thing about this is decoupling between data format handling, and validation logic.
This is what frameworks like DropWizard use; and it's the direction JAX-RS (like Jersey) are taking things for JAX-RS 2.0.
If you want to make sure a json field is provided, you have to use the #JsonProperty(value = "fieldName", required = true) annotation as a parameter to the constructor. But this is not enough, also the Constructor should have #JsonCreator annotation.
For example, if you have a field named 'endPoint' and you want o make sure it is provided in the JSON file, then the following code will throw an exception if it is not provided.
#JsonCreator
public QuerySettings(#JsonProperty(value = "endPoint", required = true) String endPoint) {
this.endPoint = endPoint;
}
I found this link helpful to understand the Jackson annotations. It also well explains why required=true is not enough and counter-intuitive to its name.
If you are neither satisfied with using #JsonProperty(required = true) as it works only with #JsonCreator nor with the use of bean validation then one more way of tackling it would be to catch this in your setter methods for the relevant variables.
You can simply check if the variable is null before setting it and throw an IllegalArgumentException or NullPointerException (as preferred by few people)
Note: It depends on how your POJO is defined too, so please make sure that it is going the setter method route for this solution to work.