JsonMappingException: Direct self-reference leading to cycle (through reference chain: MyClass["underlyingValue"]) - java

I have a POJO class extending net.sf.gilead.pojo.gwt.LightEntity. I am not able to serialize the POJO object to JSON string using com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(obj). I am getting this error
com.fasterxml.jackson.databind.JsonMappingException:
Direct self-reference leading to cycle
(through reference chain: com.example.MyClass["underlyingValue"])
Noticed that the LightEntity class has this method:
public Object getUnderlyingValue() {
return this;
}

How about to try to override this method in your MyClass class and add the #JsonIgnore annotation?
Example:
public class MyClass extends LightEntity {
#JsonIgnore
#Override
public Object getUnderlyingValue() {
return super.getUnderlyingValue();
}
}

Related

Java ArrayList of type Interface

I have two java endpoints in spring boot like this:
#PostMapping(path="/my-import-1")
#ResponseStatus(HttpStatus.OK)
public String myImport1(#Valid #RequestBody ParameterDto1 params) {
return this.serviceImpl.import(params);
}
and
#PostMapping(path="/my-import-2")
#ResponseStatus(HttpStatus.OK)
public String myImport2(#Valid #RequestBody ParameterDto2 params) {
return this.serviceImpl.import(params);
}
Both use the same service for importing, but have some differences in their parameters.
I created the service's import method like this
#Override
public String import(ParameterInterface params) throws Exception {
...
}
and the ParameterInterface like this
public interface ImportMetaData {
public default ArrayList<FileInterface> getFiles() {
return null;
}
public void setFiles(ArrayList<FileInterface> files);
}
Implementing this interface I created two ParameterDto classes (ParameterDto1 and ParameterDto2). The IDE shows everything is correct, also the start of my service works, but as soon as I send a request to one of the endpoints, I get the following error:
Servlet.service() for servlet [dispatcherServlet] in context with path
[] threw exception [Request processing failed; nested exception is
org.springframework.http.converter.HttpMessageConversionException:
Type definition error: [simple type, class
com.beo.services.myImportService.rest.domain.dto.metadata.interfaces.ParameterInerface];
nested exception is
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot
construct instance of
com.beo.services.myImportService.rest.domain.dto.metadata.interfaces.ParameterInerface
(no Creators, like default constructor, exist): abstract types either
need to be mapped to concrete types, have custom deserializer, or
contain additional type information at [Source:
(PushbackInputStream); line: 3, column: 5] (through reference chain:
com.beo.services.myImportService.rest.domain.dto.metadata.ParameterDto["files"]->java.util.ArrayList[0])]
with root cause
Can I any how create such an ArrayList from an interface and get these two endpoints running? Or is there another solution?
The issue is with the ParameterDto1 and ParameterDto2. Jackson library requires a default, no-args constructor or a constructor with parameters annotated with #JsonProperty("field_name"), otherwise it cannot convert your message.
Solution:
Add a no-args constructor to ParameterDto1 and ParameterDto2 or annotate the constructor parameters with #JsonProperty("field_name")
im guessing here because you didnt share the implementation of ParameterDto1 or ParameterDto2 - and for some reason your interface is called ImportMetaData where according to the exception, your explanation and other files it should be ParameterInterface.
the problem is that getFiles/setFiles is considered as a property by jackson , its type is an interface and you are not sending any type information.
in general assuming ParameterDto1 and ParameterDto2are using a concreate implementation of FileInterface you could just change your interface methods getFiles/setFiles so they are using generics parameter and in each implementation set the concreate type for FileInterface you are using , this will allow jackson to understand the concreate type for FileInterface .
incase ParameterDto1 and ParameterDto2 are not using a concreate implementation of FileInterface you should add #JsonTypeInfo or #JsonSubTypes (see https://www.baeldung.com/jackson-annotations section 5 for more info) - note that the client calling the api should also specify the actual type in the json-type field
Suggested implementation
public interface ParameterInterface {
#JsonIgnore
public List<FileInterface> getParameters() default { return null;}
.....
}
public class ParameterDto1 implements ParameterInterface {
private List<FileImpl1> files;
public List<FileImpl1> getFiles(){return files;}
public void setFiles(List<FileImpl1> files){this.files=files;}
....
}
public class ParameterDto2 implements ParameterInterface {
private List<FileImpl2> files;
public List<FileImpl2> getFiles(){return files;}
public void setFiles(List<FileImpl2> files){this.files=files;}
...
}
public class FileImpl1 implements FileInterface{
...
}
public class FileImpl2 implements FileInterface{
...
}

Jackson ObjectMapper deserialize an object which contains an array of objects

I serialize this kind of object:
public class MyObject implements Serializable {
private String type;
...
private String[] target;
//getters and setters
}
But when I try to deserialize MyObject I get an error because of the target array.
java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: java.util.ArrayList[0]->MyObject["target"])
...
How can I deserialize an array ?
I finally found the problem. I did not see I had 2 setters in the class. Jackson was probably using the wrong one.
I just had to put the annotation #JsonSetter("target") above the setter which accept an array to tell Jackson to use the good one.
public void setTarget(String target) {
this.target = new String[]{target};
}
#JsonSetter("target")
public void setTarget(String[] target) {
this.target = target;
}

Jackson - #JsonCreator return generics

I want to integrate vavr validation library in my command dto's in a way that when command dto is deserialized from request, return type of the static factory will be Try but jackson is throwing following error :
Type definition error: [simple type, class com.foo.command.FooCommand]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.foo.command.FooCommand (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Here is FooCommand
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public final class FooCommand {
private String foo;
private String bar;
#JsonCreator
public static Try<FooCommand> of(
#JsonProperty("foo") String foo,
#JsonProperty("bar") String bar
) {
return Try.of(() -> {
//Validate values
});
}
}
I am using spring 5 and it's annotated to deserialize request body automatically into controller parameter.
Is something like this possible ? Thanks in advance.
I had a similar problem that I fixed by using Converters: Using Jackson, how can I deserialize values using static factory methods that return wrappers with a generic type?
I haven't yet found how to apply the converters automatically, so you have to annotate every occurrence of the wrapped type in your requests.
public class Request {
#JsonDeserialize(converter = FooCommandConverter.class)
Try<FooCommand> command;
}
You can write a Converter like so:
public class FooCommandConverter
extends StdConverter<FooCommandConverter.DTO, Try<FooCommand>> {
#Override
public Try<FooCommand> convert(FooCommandConverter.DTO dto) {
return FooCommand.of(
dto.foo,
dto.bar
);
}
public static class DTO {
public String foo;
public String bar;
}
}

Serialize proxy object created by cglib

As title, I use Jersey to return an object as JSON, but the object is created by cglib proxy:
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("test")
public Response test() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(A.class);
enhancer.setCallback(new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return "my name";
}
});
return Response.ok(enhancer.create()).build();
}
#Data
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class A {
private String name;
}
It cannot work because enhancer.create() return a proxy object of class A, not a real object of class A.
org.codehaus.jackson.map.JsonMappingException: No serializer found for
class MyREST$1 and no properties discovered to create BeanSerializer
(to avoid exception, disable
SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference
chain: MyREST$A$$EnhancerByCGLIB$$fdcf8406["callbacks"])
The problem you encounter is that cglib creates a subclass of your class but does not copy the annotations. At the same time, annotations are not inherited if that is not explicitly defined.
Cglib does not support annotations. To overcome this, you can choose to use another code generation library that supports annotations. I wrote such a library, it is called Byte Buddy.

Error in JSON list deserialize

I'm developing a GWT web application with a RESTful web service. The web service's results are deserializing to POJO with Jackson 1.8. It's work fine with simple fields. However, it fails when it try to deserialize a list of POJO. This is the POJO with list to deserialize:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class DatosIndicadoresSVclaveDTO implements Serializable {
...
#XmlAttribute
#JsonDeserialize(contentAs = IdeologicoVOXDTO.class)
public List<IdeologicoVOXDTO> ideologicoVox;
...
//getter/setters
}
And this is the POJO that contains the list
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class IdeologicoVOXDTO implements Serializable {
#XmlAttribute
private Integer numeroPalabra;
#XmlAttribute
private String palabra;
#XmlAttribute
private Integer categoria;
...
//getter/setters
}
The JSON have this structure:
{datosIndicadoresSVclave: {
...
"ideologicoVox":[
{
"categoria":"1",
"numeroPalabra":"1",
"palabra":"abandonado",
...
},
{
"categoria":"2",
"numeroPalabra":"3",
"palabra":"hambre",
...
}
],
...
}
}
When it's running, the web service's results works fine, but the deserialize print this error:
SEVERE: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: java.io.StringReader#10b61ad; line: 1, column: 580] (through reference chain: org.ull.etsii.client.dto.DatosIndicadoresSVclaveDTO["ideologicoVox"])
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:212)
Any idea?? Thanks!!
I had faced some similar kind of problem and tried the following way, and it worked for me.
Create a new class which contains property
~ public List ideologicoVox ~
And use this class reference as property to the main class i.e ~ DatosIndicadoresSVclaveDTO
I've solved!!
The problem is the size list is variable, and it fails if it has one element. The Jackson's version is 1.7, that it can't accept arrays single value. My solution is GSON with a custom register type, and I've used the Joshi's adviced. It works great!! Thanks!!

Categories