Java 14 records json serialization - java

Currently experimenting with the Records implements from java 14, everything looks nice but since the accessors are slightly different and jackson is not being able to deserialize and giving the following error:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.x.x.x.xTracking and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
I checked all around the internet, including jackson and gson github to check the jep 359 support but havent found a single comment. Am i missing something really straight forward?
Yes i am aware that the java 14 is still not released and that records is only in preview in this version but would expect some comments at least.

Records support was added to Jackson 2.12.0 (https://github.com/FasterXML/jackson-future-ideas/issues/46). It will be released in the next days.

For someone else experimenting, i went around, not proudly, with the following:
#Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer(){
return builder ->
builder.visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}

With "pure" Json-B you can do it this way:
public class RecordPropertyVisibilityStrategy implements PropertyVisibilityStrategy {
#Override
public boolean isVisible(Field field) {return true;}
#Override
public boolean isVisible(Method method) {return false;}
}
Then
#JsonbVisibility(RecordPropertyVisibilityStrategy.class)
public record MyRecord(Long id, String attr) {}

I did Jackson dealing with this problem in Spring Boot 2.4.1 application annotating the record with
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)

Alternatively, for Spring Boot 2.4.5 and Java 16, it may be enabled via application.properties, like so:
spring.jackson.visibility.field=any
See more on the Spring Boot documentation.

Related

meaning of #P annotation in spring-boot

I have a simple and maybe stupid question so apologies in advance..
I have been reading some spring-boot java code recently I am able to understand almost all for I have been able to find documentation everywhere.. but for example for this I still can't figure out the exact meaning (I am not able to find the annotation #P):
public MyFileEvent getEvent(
#P("user") User user,
#P("file") #Valid #RequestBody EventFile file) throws MyException {
return myService.getFileEvent(file);
}
I understand #Valid and #RequestBody but I don't find the meaning of #P.
According to the javadoc:
"An annotation that can be used along with AnnotationParameterNameDiscoverer to specify parameter names. This is useful for interfaces prior to JDK 8 which cannot contain the parameter names."
There are in fact two versions of the #P annotation:
org.springframework.security.access.method.P since Spring 3.2 (deprecated)
org.springframework.security.core.parameters.P since Spring 5.0

Is there a Jackson annotation to use a wrapper class during deserialization as well as during serialization for Strings

Hi StackOverflow Community,
I am currently trying to deserialize JSON request bodies provided via Spring Boot #RestController.
The request body contains the following array:
{
...
"productIds": [
"123abc",
"234def"
],
...
}
However, I don't want to deserialize the product IDs into a list of Strings, but rather use a simple wrapper class (for various reasons, including but not limited to additional type safety and validation opportunities). Consequently the class looks like this (Lombok annotations were used to keep the code snippet short):
#Value
#AllArgsConstructor
public class TheRequest {
...
List<ProductId> productIds;
...
}
with ProductId being just a simple wrapper as already said (validation annotations are omitted for the sake of brevity):
#Value
#AllArgsConstructor
public class ProductId{
String id;
}
Looking at Stackoverflow I only found ways to achieve this using rather verbose custom deserialization methods.
However, I am a bit astonished, that Jackson does not provide this functionality out of the box. Consequently it would be great if anyone has any idea if
there is a more elegant way to achieve deserialization of a array of Strings into a List of WrapperObjects, ideally only using Jackson annotations?
there is an elegant way to achieve serialization of such a resulting List of ProductId wrapper objects back into String objects, ideally also using only Jackson annotations? I tried Jacksons #Value but that did not provide the required result.
To me still to verbose but it seems to be a working solution with Jacson 2.14+:
public record PayloadId(String id) {
#JsonCreator(mode = Mode.DELEGATING)
public PayloadId{}
#JsonValue
#Override
public String id() {
return id;
}
}
...and here is the records test https://github.com/FasterXML/jackson-databind/blob/2.14/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordCreatorsTest.java

Jackson serializes using #JsonProperty name in constructor

I have been working on upgrading the Spring Boot version of one of my microservices, and I stumbled upon a strange behaviour. I have class like this:
public class FilteredData {
private final List<ShipmentData> shipments;
public FilteredData(#JsonProperty("listShipments") List<ShipmentData> shipments) {
this.shipments = shipments;
}
public List<ShipmentData> getShipments() {
return shipments;
}
}
The behaviour that I had before doing the upgrade was that, when deserialising, the name listShipments was used in the JSON object to map to the shipments property of the Java class. However, when serialising, it would write the shipments property with the name shipments, not listShipments.
The problem is that now it is using the name listShipments when both deserialising and serialising. I am not sure at what point this issue started happening, as my initial Spring Boot version was 1.5.7 and I am slowly upgrading all the way to 2.3.4. But I believe it started happening after version 2.0.0.
I don't know if this is being caused by some internal change in Spring Boot's Jackson autoconfiguration, or a change in the actual Jackson library, but I am having a hard time tracking what caused this and how to fix it.
EDIT: I noticed from the latest Spring Boot 1 version (1.5.22) to Spring Boot 2.0.0, the Jackson minor version was bumped (from 2.8 to 2.9). Could this have caused the issue?
I was able to reproduce this exact behaviour while switching from Spring Boot 1.5.7.RELEASE to 2.0.0.RELEASE. First, I added the parent and the spring-boot-starter-web dependency. Then, I created a simple #RestController, used the POJO you provided and replaced ShipmentData with String. When I manually create a Jackson ObjectMapper, I could not reproduce the issue across the two releases.
The most interesting part: When you rename the parameter name of the constructor to something other than shipments, it seems to work fine. This could possibly be a bug in Jackson or somewhere in the Spring Framework which manifests only when the getter matches the parameter name in the constructor.
I suggest to use #JsonAlias to define the alternative name that should be accepted during deserialization:
#JsonCreator
public FilteredData(
#JsonProperty("shipments")
#JsonAlias("listShipments")
List<ShipmentData> shipments) {
this.shipments = shipments;
}
With this, you can deserialize with both listShipments and shipments while for serialization only shipments is used (defined by getter).
For reference, a working implementation with 2.3.3.RELEASE:
#RestController
public class FilteredDataController {
#PostMapping("/data")
public FilteredData postFilteredData(#RequestBody FilteredData data) {
return data;
}
}
public class FilteredData {
private final List<String> shipments;
#JsonCreator
public FilteredData(
#JsonProperty("shipments") #JsonAlias("listShipments")
List<String> shipments) {
this.shipments = shipments;
}
public List<String> getShipments() {
return shipments;
}
}
Request:
curl -d '{"listShipments":["a","b","c"]}' -H 'Content-Type: application/json' http://localhost:8080/data
Result:
{"shipments":["a","b","c"]}
I managed to find the issue! It was caused by a dependency being used by spring-boot-starter-web called jackson-module-parameter-names. This library provides a Jackson module named ParameterNamesModule, which, as per Spring Boot's Jackson Autoconfiguration, any Jackson module found in the classpath is automatically imported.
The description for this library in https://github.com/FasterXML/jackson-modules-java8 says:
Parameter names: support for detecting constructor and factory method ("creator") parameters without having to use #JsonProperty annotation. Provides com.fasterxml.jackson.module.paramnames.ParameterNamesModule
I am not 100% sure still why this created the problem that it did, but removing it through Maven solved the issue:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</exclusion>
</exclusions>
</dependency>
There were breaking changes between Spring Boot 1 and Spring Boot 2. Have you tried anything to fix it already? E.g. attempted to move the JsonProperty annotation to field rather than constructor? or using #JsonCreator
online resource: https://www.baeldung.com/jackson-deserialize-immutable-objects

How to ignore new fields for an object model with Firebase 1.0.2

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.

Strange Jackson exception being thrown when serializing Hibernate object

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.

Categories