Jackson infinite recursion when converting to json - java

When trying to convert a custom object Tag which has the below structure to json, it gives me the following error: java.lang.IllegalArgumentException: Infinite recursion because of getValueArray() function
public class Tag {
private String vr;
#JsonProperty("Value")
private JsonNode Value;
//getters and setters
public org.primefaces.model.TreeNode getValueArray() throws IOException
{
System.out.println("enter check");
TreeNode root = new DefaultTreeNode();
constructTree(Value, 1, root);
return root;
}
}
I have a getter for Value that returns String instead of JsonNode (for specific reason), and I have this getValueArray that causes the problem, Jackson calls this function when converting to json instead of using getValue (I know that as enter check are printed to console), actually when I remove this function it works, for some reason it calls this function and uses the returned TreeNode in it's conversion, I thought the problem might be that it gets confused with Jackson treeNode, so I returned org.primefaces.model.TreeNode to let it recognizes that this is primefaces TreeNode not Jackson TreeNode, I don't even know why it calls this function, and how to fix this.
the error said
java.lang.IllegalArgumentException: Infinite recursion (StackOverflowError) (through reference chain: org.primefaces.model.DefaultTreeNode["parent"]->org.primefaces.model.DefaultTreeNode["children"]->org.primefaces.model.TreeNodeChildren[0]->org.primefaces.model.DefaultTreeNode["parent"]................. and so on

I found a work around that works fine, as the problem is that Jackson uses this function getValueArray() as a getter in the serialization, because of it's name get something, I used
#JsonGetter("valueArray")
public JsonNode serializeValue()
{
return Value;
}
#JsonGetter("valueArray") lets me force Jackson to use the above function as getter for the non exist property valueArray which return the Value, now jackson will no longer uses getValueArray() function as getter.

Related

How can I deserialize a list of enums using Jackson JSON?

I'm working on a configuration system. I'd like to be able to load config values from a JSON file and have them "automagically" convert to the Java type I need. I'm using Jackson for the JSON parsing. For primitive types like floats and strings, it's no big deal, but I'm running into a snag with enums.
Let's say I have the following enum:
public enum SystemMode
{
#JsonProperty("Mode1")
MODE1("Mode1"),
#JsonProperty("Mode2")
MODE2("Mode2"),
#JsonProperty("Mode3")
MODE3("Mode3");
private final String name;
private SystemMode(String name)
{
this.name = name;
}
#Override
#JsonValue
public String toString()
{
return this.name;
}
}
Now, let's say I want to represent a list of values of this enum for a given config variable using the following JSON representation:
{
"Project" : "TEST",
"System" : {
"ValidModes" : ["Mode1", "Mode2"]
}
}
And I'd like to be able to do something like the following:
ArrayList<SystemMode> validModes = (ArrayList<SystemMode>) configurator.getConfigValue("/System/ValidModes");
For reference, my configurator class's getConfigValue method is essentially a thin wrapper over the Jackson JSON parsing:
public Object getConfigValue(String JSON_String)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(JSON_String);
return objectMapper.convertValue(node, Object.class);
}
(The real method has some exception checking that has been omitted for clarity).
Now, when I call the above, Jackson correctly deduces that I want an ArrayList and fills it. However, instead of getting an ArrayList of SystemMode enums, I get an ArrayList of Strings and immediately throw an exception when I attempt to use the list. I have tried several different ways of representing the data to no avail. It seems no matter what I try, Jackson wants to return a list of strings instead of a list of enums.
So my question is this:
How can I make Jackson (version 2.9.4) JSON properly deserialize a list of enum values in a way that is compatible with my single "Object getConfigValue()" method?
The following will provide the correct binding for your enum.
public List<SystemMode> getConfigValue(String path)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(path);
return objectMapper.convertValue(node, new TypeReference<List<SystemMode>>(){});
}
The second option is to convert the list of String yourself, for example:
List<SystemMode> result = jsonResult.stream().map(SystemMode::valueOf).collect(Collectors.toList());
Third option:
public <T>List<T> getConfigValue(String path, Class<T> type)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(path);
CollectionType toType =
objectMapper.getTypeFactory().constructCollectionType(List.class, type);
return objectMapper.convertValue(node, toType);
}

How to valid incoming JSON and check for missing properties? [Jackson,Jersey]

I'm using Jackson 2.7.0. and latest Jersey for JSON on REST API that handles DB communication with Hibernate 5+.
I don't know how to verify incoming JSON if there are any missing properties in it. It is impossible to perform checking on primitive type if they are null. But also i would like to use primitive types because of performance hit.
What is best practice to handle such problem?
When I receive JSON like below, everything is ok:
{"vehicle":{"id":1},"distance":1000,"quantity":2000}
But when i receive JSON like:
{"vehicle":{"id":1},"quantity":2000}
then distance is set to default value 0.
My entity look like
public class Consumption{
private int id;
private double quantity;
private double distance;
#JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Consumption(
#JsonProperty(value = "quantity", required = true)double quantity,
#JsonProperty(value = "distance", required = true)double distance,
#JsonProperty(value = "vehicle", required = false)Vehicle vehicle) {...
And REST method:
#POST
#Path("/setConsumption/")
public Response setConsumption(#Valid Consumption consum){...
Tried solution #1
I have already tried, to set all values to default value of -1 and then check if it is less then 0, but problem remains with boolean values, where i can not set it to the "neutral" value.
How do you usually handle such problem with missing property in JSON?
Tried solution #2
As you can see i have used #JsonProperty(value = "quantity", required = true) in constructor. This is new feature for Jackson 2.7.0.+, that handles this type of problem. But i get this exception:
Missing required creator property 'distance' (index 1) at
[Source:org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#1b72cb6; line: 1, column: 181]
This exception is returned to user BEFORE JSON reaches my code. So i can not catch it as exception. I have custom responses with httpCode and errorMessage to inform user about them. Problem is that i don't know how to catch this exception and return it to user in my custom form with my text.
Searching for solution
I have either to catch exception of new Jackson feature or try to validate JSON. But i would not like to use class Integer instead of primitive data type int, because of performance.
If it is possible not to write custom JSON deserializer for all 60+ classes i have in project.
If any other solution then Jackson or Jersey supports this types of handling of missing properties, feel free to comment.
TDLR - how to check if variable of primitive data type (int, double, boolean,...) was in incoming JSON or not, without manually writting deserializers for each of 60+ classes in project. Java does not allow to check those types with null.
Suggestion #1
Trying to register an ExceptionMapper for JsonMappingException and see if it overrides the one already registered by Jersey.
My code (still getting default "Missing property ..." exception - problem not solved):
#Provider
public class JacksonNoPropertyMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException e) {
return Response.status(999).entity("OVERRIDE TEST").type(MediaType.APPLICATION_JSON).build();
}
}
The easiest way for you to approach this would be to use the Object counter parts on fields that are optional, it shouldn't affect you too much performance wise, and would allow you to null check the fields.
public class Consumption {
private int id;
private double quantity;
private Double distance;
public boolean isDistanceSet() {
return distance != null;
}

Serializing nested documents mongoDB to Jackson

so I am using Jackson (together with Mongojack) to create POJO's of MongoDB Documents. My database looks something like this:
Document{ id:1, Document2{ value1:1 value2:2}}
I have created a POJO for Document, and for Document 2, and this generally works fine. The problem is that some places in my Database, Document2 has a value of NaN instead of being a nested document with several values, which leads to this error
ERROR [2015-07-21 16:01:26,809] io.dropwizard.jersey.errors.LoggingExceptionMapper: Error handling a request: 3b38d500d7657352
! com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class com.document1.document2] from Floating-point number (NaN); no one-double/Double-arg constructor/factory method
! at [Source: de.undercouch.bson4jackson.io.LittleEndianInputStream#534093c6; pos: 1975] (through reference chain: com.document1["document2"])
! at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148) ~[jackson-databind-2.5.1.jar:2.5.1]
Not really sure what the best way to go about fixing this is. I guess what I'd want is for Jackson to recognize if Document2 is just :nan instead of a nested document, and just serialize it with no values or something?
It's really best if you can keep the data you're working reasonably consistent. That said, there may be a way to hack around this:
public class Document2 {
public Document2() { }
public Document2(double invalid)
{
System.out.print("invalid");
}
public void setValue1(int value1)
{
System.out.println("value1 " + value1);
}
public void setValue2(int value2)
{
System.out.println("value2 " + value2);
}
}
In the "happy" case, Jackson will use the default constructor and set the properties as given. In the NaN case, it will find the single-argument double constructor, and leave the other properties alone.

Getter (accessor) based serialization (json or xml)

I need to serialize a couple of objects in my Android app and send them to web service.
The model classes for objects have various int fields which need to be converted into meaningful string representations from various arrays before sending to web service.
So, I am assuming that easiest way will be to use gson or xstream (JSON or XML - anything is fine) but with following method:
- I'll mark all existing int fields as transient and exclude them from serialization
- I'll create new get method per field. The get method will read value of corresponding integer and return its string representation.
But in either of 2 libraries - gson or xstream, I am unable to find way to serialize based on getters instead of fields. Please suggest.
And yes, I DO NOT need to deserialize the data back.
I think you need a wrapper class.
Consider this:
public class Blammy
{
private int gender;
... imagine the rest of the class.
}
public class BlammyWrapper
{
private String gender;
public BlammyWrapper(final Blammy blammy)
{
if (blammy.gender == 1)
{
gender = "Its a Boy";
}
else if (blammy.gender == 2)
{
gender = "girl";
}
else // always check your boundary conditions.
{
throw new InvalidArgumentException("Naughty blammy; unrecognized gender value");
}
public String gender()
{
return gender;
}
}
Ok, finally, I followed this approach:
1. Removed all resource arrays from my app code
2. Added enums with toString for each current array
3. Changed all int properties to be of corresponding enum type
4. Used XStream
5. Added a custom convertor for XStream for generic enum types where if it finds any property of type enum, then it will marshal it using toString method.
Thanks for all support btw. All your answers and comments atleast made me clear that my current code architecture needed drastic improvement.

TRUE or FALSE into boolean using Jackson JSON parsing

I am using Jackson annotation for parsing JSON response into POJO object.I was using boolean variable in POJO for mapping values "true" and "false" coming from JSON. But suddenly we are getting value as "TRUE" and "FALSE" into JSON and parsing failing for these values.
Can anyone suggest way to map it to boolean as this variable is used so many places where i don't want to change logic to String to Boolean .
It isn't really an issue, this is basically the way BeanUtils works.
For boolean vars, Jackson removes is from the setter name to derive what it expects the variable name to be when marshalling to JSON and adds set to that same derived name to unmarshal back to a POJO.
So boolean isFooTrue; ends up as fooTrue when marshalled to JSON, and when unmarshalling it would attempt to call setIsFooTrue();, which isn't the correct.
If you're using an IDE and you generated your getter/setters, you'll probably notice that the generated code for boolean isFoo; basically ignores the is as if the var name was just foo:
private boolean isFoo;
public boolean isFoo() {
return isFoo;
}
public void setFoo(boolean isFoo) {
this.isFoo= isFoo;
}
Two options are to remove the is from the var name, or add the is to the setter name.
I am not sure this is what you want.
But it works.
Boolean param = Boolean.parseBoolean((String)yourValue);
The tested code is
public class program10 {
public static void main(String args[]) {
String yourValue = "TRUE"; // This is what you get from json.
Boolean param = Boolean.parseBoolean((String)yourValue);
if(param == true)
System.out.println("Value is true");
else
System.out.println("Value is false");
System.out.println(param);
}
}
I also faced a similiar issue using Jackson Parser 1.8.5.
Java POJO to JSON worked but same JSON back to Java POJO did not.
In Java POJO, if a boolean variable is declared as
private Boolean isMyVar;
then the Jackson produces equivalent JSON as
{..,
"myVar" : false,
..
}
(I know the boolean variable naming is wrong here, but the JAR is third party and say you cannot change it!)
I think this is an issue with the way Jackson parser is designed to handle boolean values.
I changed the JSON from "myVar" : false to "isMyVar" : false and it worked ok to create back the Java POJO from the JSON.
Anybody knows if this is still a bug or has it been resolved?

Categories