Serlaize case class with the variable inside it in scala with jackson - java

i am tiring to serialize a case class using jackson fasterxml, i can see the constructor parameters after deserialize (taskRequest and taskNameIn) but not the variables inside the class (jobsRequests is null for example):
//#JsonIgnoreProperties(ignoreUnknown = true) // tried to remove it with no luck
#JsonAutoDetect
case class Job(taskRequest: List[TaskRequest] = Nil,taskNameIn:String) {
{
this.jobsRequests = taskRequest
this.taskName= taskNameIn
}
#JsonProperty
#volatile private var jobsRequests: List[TaskRequest] = Nil
#JsonProperty
var task_name: String = ""
}
Any suggestions ?

Jackson uses Getter from the Java Beans standard to construct the json. Try adding #BeanProperty to your properties and constructor parameters to compile your class with Getter/Setters.
Example
Or you could use the Jackson Scala-Module. You can take a look at their tests to see how to use this module for serialization.

So there where some problems with the serialization, some where easy but i learn something that may help other people with this problem and the understanding of case class in general.
First, i used javap(.exe) to see the java code from the class files, to Job.scala with contained case class named Job, there are two class files: Job$.class and Job.class.
Job$.class:
public final class logic.Queue.Task.Job$ extends scala.runtime.AbstractFunction4<java.lang.Object, java.lang.String, scala.collection.immutable.List<logic.Job.TaskRequest>, org.Server.Job.TaskContainer, logic.Queue.Task.Job> implements scala.Serializable {
public static final logic.Queue.Task.Job$ MODULE$;
public static {};
public final java.lang.String toString();
.
.
.
}
Job.class:
public class logic.Queue.Task.Job implements scala.Product,scala.Serializable {
public static org.Server.Job.TaskContainer apply$default$4();
public static scala.collection.immutable.List<logic.Job.TaskRequest> apply$default$3();
.
.
.
}
Meaning that the scala case class is an anonymous inner class and when you try to serialize it (and you can since both implements scala.Serializable), i solved it with adding to the signature to:
#JsonAutoDetect
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
#JsonCreator
case class Job(stat: Int = CmsTypeMsg.Pending, jobName: String = "", taskRequestIn: List[TaskRequest] = Nil, taskIn: TaskContainer = new TaskContainer())
For more help on this issue:
http://www.jpalomaki.fi/?p=527
Json deserialization into another class hierarchy using Jackson

Related

Jackson serialize/deserialize private builder

I'm trying to serialize/deserialize the DynamoDB Record class using Jackson.
I have no control over this class, so I tried to use Jackson mixins.
I did the following:
#JsonDeserialize( builder = RecordBuilderMixIn.class )
private abstract static class RecordMixIn
{
}
#JsonPOJOBuilder( withPrefix = "" )
private abstract class RecordBuilderMixIn
{
}
var mapper = new ObjectMapper()
.addMixIn( Record.Builder.class, RecordBuilderMixIn.class )
.addMixIn( Record.class, RecordMixIn.class );
But I get the following error when I do mapper.readValue(json, Record.class);:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Builder class `com.surecloud.jackson.RecordObjectMapper$RecordBuilderMixIn` does not have build method (name: 'build')
Any ideas on how to solve this?
Thanks
Add a constructor in RecordBuilderMixIn it is needed for the reflection that the object mapper uses to populate the object.
#JsonPOJOBuilder( withPrefix = "" )
private abstract class RecordBuilderMixIn {
public RecordBuilderMixIn() {
// constructor required by java reflection
}
}
Also stop adding new line for the curybrackets after the method declaration it will just cause problems since no modern editors use this style.

Mapping DTO to inherited class

My domain:
public class Moral {
private String moralId;
private String socialReason;
private Framework framework;
}
public class Framework {
private String externalId;
private Set<String> identifiers;
}
public class Lab extends Framework {
private String system;
private String availability;
}
My DTO:
public class CreateLabRequest {
private String socialReason;
private Set<String> identifiers;
private String system;
private String availability;
}
My Mapper for this looks like:
#Mapping(source = "system", target = "framework.system")
#Mapping(source = "availability", target = "framework.availability")
#Mapping(source = "identifiers", target = "framework.identifiers")
Moral createLabRequestToMoral (CreateLabRequest createLabRequest);
However, I get the following error:
Unknown property "system" in type Framework for target name
"framework.system". Did you mean "framework.externalId"? Unknown
property "availability" in type Framework for target name
"framework.availability". Did you mean "framework.externalId"?
Simply, It is not Possible !
Maybe you wanted to make Framework inherits from Map ?!
Otherwise, the problem is due that you want to access some field in a class that doesn't have it !
public class Framework {
private String externalId;
private Set<String> identifiers;
}
public class Lab extends Framework {
private String system;
private String availability;
}
As it says, extends means that your Lab class inherits from Framework, that means that Lab inherits all fields that Framework has, and not the opposite.
So with that being said :
"framework.system" // cannot be accessed
Since there is no field named "system" in the framework class
However :
"lab.externalId" // CAN be accessed
Since Lab class inherits it from its parent class "Framework" eventhough there is no such field named "system" in the Lab class
More explanations about JAVA inheritance can be found here : https://www.geeksforgeeks.org/inheritance-in-java/
This is possible as follows:
#Mapping( source = ".", target = "framework" )
Moral createLabRequestToMoral( CreateLabRequest createLabRequest );
Lab createLabFramework( CreateLabRequest createLabRequest )
Since Lab extends Framework mapstruct will use createLabFramework( CreateLabRequest createLabRequest ) since it is an user defined method.
Also since all the fields are called the same it is not needed to add those #Mapping annotations.
Edit: expanding a bit about the error.
Unknown property "system" in type Framework for target name "framework.system". Did you mean "framework.externalId"? Unknown property "availability" in type Framework for target name "framework.availability". Did you mean "framework.externalId"?
This is basically caused by MapStruct not knowing that there is a Lab class available that extends Framework. MapStruct can also not know that you want to use the Lab class instead of the Framework class.
As shown above, one of the methods is manually defining an additional mapping method to notify MapStruct about this situation.

Jackson: conflicting #JsonTypeInfo and #JsonSerialize(as=Klass.class)

The problem
I need to polymorphically JSON-(de-)serialize an #Autowired Spring bean (I'm using Spring Boot 2.0.4) using only original properties.
Since the bean is "enhanced", it is a subclass of my "original" bean, with class name ending with something like $$EnhancerBySpringCGLIB$$12345.
Tried so far
To avoid Jackson trying to serialize the "enhanced" part, I've declared my bean as a supertype of itself with
#JsonSerialize(as=MyClass.class)
It worked as intended.
But, when I try to do polymorphic serialization with
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.WRAPPER_OBJECT)
placed on the interface that the said class implements, the key of the wrapper object is the name of the enhanced class! The rest of JSON string is OK, that is, only properties of the "original" class are included. Needless to say, I can't de-serialize it now, since the mentioned subclass is not around any more.
Using JsonTypeInfo.Id.NAME defeats the whole idea of polymorphic deserialisation, IMHO. I can figure out the target class by querying ApplicationContext, if nothing else works.
EDIT:
Here is a Foo Bar example:
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.WRAPPER_OBJECT)
public class Foo {
private String foo = "Foo";
#JsonSerialize(as = Bar.class)
public static class Bar extends Foo {
private String bar = "Bar";
}
public static class UnwantedMutant extends Bar {
private String aThing = "Not welcome";
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
UnwantedMutant mutant = new UnwantedMutant();
System.out.println(mapper.writeValueAsString(mutant));
}
}
This prints
{"mypackage.Foo$UnwantedMutant":{"foo":"Foo","bar":"Bar"}}
while
{"mypackage.Foo$Bar":{"foo":"Foo","bar":"Bar"}}
is expected/desired.
So, the question:
is there any solution to this problem with "pure" Jackson means, or I just have to live with it?
Did you try with:
#JsonRootName(value = "NameOfYourClass") ?
Sorry if I didn't understand your question.

Deserializing Polymorphic Types with #JsonUnwrapped using Jackson

What I Want to Do
I want to use Jackson to deserialize a polymorphic type, using the standard #JsonTypeInfo annotation as follows:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = As.EXISTING_PROPERTY,
property = "identifier")
#JsonSubTypes({#Type(value = A.class, name = "A"),
#Type(value = B.class, name = "B")})
abstract Class Base {}
Class A implements Base {
public String identifier = "A";
}
Class B implements Base {
public String identifier = "B";
}
Class Decorated {
public String decoration = "DECORATION";
#JsonUnwrapped
public Base base;
}
/*
Serialized instance of Decorated WITHOUT #JsonUnwrapped:
{
"decoration" : "DECORATION",
"base" : {
"identifier" : "A"
}
}
Serialized instance of Decorated WITH #JsonUnwrapped:
{
"decoration" : "DECORATION",
"identifier" : "A"
}
*/
Related post: Deserialize JSON with Jackson into Polymorphic Types - A Complete Example is giving me a compile error
This can normally be deserialized by Jackson as follows:
public Object deserialize(String body, Class clazz) {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(body, clazz);
}
(And this would work if the #JsonUnwrapped annotation were removed)
The Problem
Polymorphic types do not play well with Jackson's #JsonUnwrapped annotation, as discussed in this Jira ticket from 2012:
http://markmail.org/message/pogcetxja6goycws#query:+page:1+mid:pogcetxja6goycws+state:results
Handle polymorphic types with #JsonUnwrapped
Agreed - while fixing things is obviously preferable, improving error messages would be useful if that can't be done.
Unwrapping is one of features where implementations gets complicated enough that any bugs cropping up (on deserialization esp) tend to be antibiotic-resistant...
Hardly encouraging.
Three years later:
http://markmail.org/message/cyeyc2ousjp72lh3
Handle polymorphic types with #JsonUnwrapped
Resolution: Won't Fix
Damn.
So, is there any way to coax Jackson into giving me this behaviour without modifying deserialize() or removing the #JsonUnwrapped annotation?
My SinglePolyUnwrappedDeserializer from this Gist can handle a single polymorphic #JsonUnwrapped property. It's in Kotlin, but can easily be ported to Java if needed. Example:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
#JsonSubTypes(
JsonSubTypes.Type(value = A::class, name = "a"),
JsonSubTypes.Type(value = B::class, name = "b")
)
abstract class Base
data class A(val x: Int) : Base()
data class B(val y: Boolean) : Base()
#JsonDeserialize(using = SinglePolyUnwrappedDeserializer::class)
data class C(val a: String, #JsonUnwrapped val b: Base)
AFAIK, all combinations of other annotations are supported. The only limitation is that there is exactly one #JsonUnwrapped property.
If you also need a generic serializer for polymorphic #JsonUnwrapped, you can write it yourself very easily without any reflection or introspection: just merge the ObjectNode of the inner object onto the ObjectNode of the containing object.

JsonMappingException: No suitable constructor found for type [simple type, class ]: can not instantiate from JSON object

I am getting the following error when trying to get a JSON request and process it:
org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class com.myweb.ApplesDO]: can not instantiate from JSON object (need to add/enable type information?)
Here is the JSON I am trying to send:
{
"applesDO" : [
{
"apple" : "Green Apple"
},
{
"apple" : "Red Apple"
}
]
}
In Controller, I have the following method signature:
#RequestMapping("showApples.do")
public String getApples(#RequestBody final AllApplesDO applesRequest){
// Method Code
}
AllApplesDO is a wrapper of ApplesDO :
public class AllApplesDO {
private List<ApplesDO> applesDO;
public List<ApplesDO> getApplesDO() {
return applesDO;
}
public void setApplesDO(List<ApplesDO> applesDO) {
this.applesDO = applesDO;
}
}
ApplesDO:
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String appl) {
this.apple = apple;
}
public ApplesDO(CustomType custom){
//constructor Code
}
}
I think that Jackson is unable to convert JSON into Java objects for subclasses. Please help with the configuration parameters for Jackson to convert JSON into Java Objects. I am using Spring Framework.
EDIT: Included the major bug that is causing this problem in the above sample class - Please look accepted answer for solution.
So, finally I realized what the problem is. It is not a Jackson configuration issue as I doubted.
Actually the problem was in ApplesDO Class:
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String apple) {
this.apple = apple;
}
public ApplesDO(CustomType custom) {
//constructor Code
}
}
There was a custom constructor defined for the class making it the default constructor. Introducing a dummy constructor has made the error to go away:
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String apple) {
this.apple = apple;
}
public ApplesDO(CustomType custom) {
//constructor Code
}
//Introducing the dummy constructor
public ApplesDO() {
}
}
This happens for these reasons:
your inner class should be defined as static
private static class Condition { //jackson specific
}
It might be that you got no default constructor in your class (UPDATE: This seems not to be the case)
private static class Condition {
private Long id;
public Condition() {
}
// Setters and Getters
}
It could be your Setters are not defined properly or are not visible (e.g. private setter)
I would like to add another solution to this that does not require a dummy constructor. Since dummy constructors are a bit messy and subsequently confusing. We can provide a safe constructor and by annotating the constructor arguments we allow jackson to determine the mapping between constructor parameter and field.
so the following will also work. Note the string inside the annotation must match the field name.
import com.fasterxml.jackson.annotation.JsonProperty;
public class ApplesDO {
private String apple;
public String getApple() {
return apple;
}
public void setApple(String apple) {
this.apple = apple;
}
public ApplesDO(CustomType custom){
//constructor Code
}
public ApplesDO(#JsonProperty("apple")String apple) {
}
}
When I ran into this problem, it was a result of trying to use an inner class to serve as the DO. Construction of the inner class (silently) required an instance of the enclosing class -- which wasn't available to Jackson.
In this case, moving the inner class to its own .java file fixed the problem.
Generally, this error comes because we don’t make default constructor.
But in my case:
The issue was coming only due to I have made used object class inside parent class.
This has wasted my whole day.
Thumb Rule: Add a default constructor for each class you used as a mapping class. You missed this and issue arise!
Simply add default constructor and it should work.
Can you please test this structure. If I remember correct you can use it this way:
{
"applesRequest": {
"applesDO": [
{
"apple": "Green Apple"
},
{
"apple": "Red Apple"
}
]
}
}
Second, please add default constructor to each class it also might help.
You have to create dummy empty constructor in our model class.So while mapping json, it set by setter method.
Regarding the last publication I had the same problem where using Lombok 1.18.* generated the problem.
My solution was to add #NoArgsConstructor (constructor without parameters), since #Data includes by default #RequiredArgsConstructor (Constructor with parameters).
lombok Documentation
https://projectlombok.org/features/all
That would solve the problem:
package example.counter;
import javax.validation.constraints.NotNull;
import lombok.Data;
#Data
#NoArgsConstructor
public class CounterRequest {
#NotNull
private final Integer int1;
#NotNull
private final Integer int2;
}
If you start annotating constructor, you must annotate all fields.
Notice, my Staff.name field is mapped to "ANOTHER_NAME" in JSON string.
String jsonInString="{\"ANOTHER_NAME\":\"John\",\"age\":\"17\"}";
ObjectMapper mapper = new ObjectMapper();
Staff obj = mapper.readValue(jsonInString, Staff.class);
// print to screen
public static class Staff {
public String name;
public Integer age;
public Staff() {
}
//#JsonCreator - don't need this
public Staff(#JsonProperty("ANOTHER_NAME") String n,#JsonProperty("age") Integer a) {
name=n;age=a;
}
}
You must realize what options Jackson has available for deserialization. In Java, method argument names are not present in the compiled code. That's why Jackson can't generally use constructors to create a well-defined object with everything already set.
So, if there is an empty constructor and there are also setters, it uses the empty constructor and setters. If there are no setters, some dark magic (reflections) is used to do it.
If you want to use a constructor with Jackson, you must use the annotations as mentioned by #PiersyP in his answer. You can also use a builder pattern. If you encounter some exceptions, good luck. Error handling in Jackson sucks big time, it's hard to understand that gibberish in error messages.
Add default constructors to all the entity classes
Failing custom jackson Serializers/Deserializers could also be the problem. Though it's not your case, it's worth mentioning.
I faced the same exception and that was the case.
For me, this used to work, but upgrading libraries caused this issue to appear. Problem was having a class like this:
package example.counter;
import javax.validation.constraints.NotNull;
import lombok.Data;
#Data
public class CounterRequest {
#NotNull
private final Integer int1;
#NotNull
private final Integer int2;
}
Using lombok:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
Falling back to
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
Fixed the issue. Not sure why, but wanted to document it for future.

Categories

Resources