jackson deserialization json to java-objects - java

Here is my Java code which is used for the de-serialization,
i am trying to convert json string into java object. In doing so i have used the following code:
package ex1jackson;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Ex1jackson {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
try {
String userDataJSON = "[{\"id\":\"value11\",\"name\": \"value12\",\"qty\":\"value13\"},"
+ "{\"id\": \"value21\",\"name\":\"value22\",\"qty\": \"value23\"}]";
product userFromJSON = mapper.readValue(userDataJSON, product.class);
System.out.println(userFromJSON);
} catch (JsonGenerationException e) {
System.out.println(e);
} catch (JsonMappingException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
}
}
and my product.java class
package ex1jackson;
public class product
{
private String id;
private String name;
private String qty;
#Override
public String toString() {
return "Product [id=" + id+ ", name= " + name+",qty="+qty+"]";
}
}
i am getting the following error.
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "id" (class ex1jackson.product), not marked as ignorable (0 known properties: ]) at
[Source: java.io.StringReader#16f76a8; line: 1, column: 8] (through reference chain: ex1jackson.product["id"])
BUILD SUCCESSFUL (total time: 0 seconds)
help me to solve this,

It looks like you are trying to read an object from JSON that actually describes an array. Java objects are mapped to JSON objects with curly braces {} but your JSON actually starts with square brackets [] designating an array.
What you actually have is a List<product> To describe generic types, due to Java's type erasure, you must use a TypeReference. Your deserialization could read: myProduct = objectMapper.readValue(productJson, new TypeReference<List<product>>() {});
A couple of other notes: your classes should always be PascalCased. Your main method can just be public static void main(String[] args) throws Exception which saves you all the useless catch blocks.

You have to change the line
product userFromJSON = mapper.readValue(userDataJSON, product.class);
to
product[] userFromJSON = mapper.readValue(userDataJSON, product[].class);
since you are deserializing an array (btw: you should start your class names with upper case letters as mentioned earlier). Additionally you have to create setter methods for your fields or mark them as public in order to make this work.
Edit: You can also go with Steven Schlansker's suggestion and use
List<product> userFromJSON =
mapper.readValue(userDataJSON, new TypeReference<List<product>>() {});
instead if you want to avoid arrays.

JsonNode node = mapper.readValue("[{\"id\":\"value11\",\"name\": \"value12\",\"qty\":\"value13\"},"
System.out.println("id : "+node.findValues("id").get(0).asText());
this also done the trick.

Your product class needs a parameterless constructor. You can make it private, but Jackson needs the constructor.
As a side note: You should use Pascal casing for your class names. That is Product, and not product.

Related

Not able to convert json to pojo class, getting com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException exception

This is my JsonObject
JSONObject input = new JSONObject("{\n" +
" \"ColumnNames\":[\"col1\", \"col2\", \"col3\", \"col4\", \"col5\"]\n" +
"}");
My POJO Class
public class RequestClass {
private List<String> ColumnNames;
public void setColumnNames(List<String> ColumnNames) {
this.ColumnNames = ColumnNames;
}
public List<String> getColumnNames() {
return this.ColumnNames;
}
}
Trying to convert JsonObject to pojo class object with the help of ObjectMapper as shown below -
ObjectMapper mapper = new ObjectMapper();
//mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
RequestClass request = null;
try {
request = mapper.readValue(input.toString(), RequestClass.class);
} catch (Exception e) {
e.printStackTrace();
}
Getting an exception in output
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "ColumnNames" (class RequestClass), not marked as ignorable (one known property: "columnNames"])
at [Source: {"ColumnNames":["col1","col2","col3","col4","col5"]}; line: 1, column: 17] (through reference chain: RequestClass["ColumnNames"])
The name of the private property named ColumnNames is actually irrelevant. The property is found by introspection, looking at the getters and setters. And by convention, if you have methods named getColumnNames and setColumnNames, they define a property named columnNames (lowercase c).
So you have two choices:
change the name of the property in the JSON to columnNames, or
use an annotation to override the default introspective behavior.
The latter is achieved by using the #JsonProperty on the getter and setter, as follows:
#JsonProperty("ColumnNames")
public List<String> getColumnNames() {
return this.ColumnNames;
}
Looking at the exception it looks like in the pojo , you have mentioned ColumnNames and in the json you have mentioned columnNames (a case mismatch) , although you have defined it correctly in the json example above. Please check whether there is a case mismatch in the field names.

Mapping one custom Java field to many JSON fields using Jackson #JsonDeserializer

I have a java class representing a JSON using Jackson. All of the fields, with one exception, can be translated using no annotations at all. 1-to-1, simple translations (although some of them are nested POJOs).
#Data
#AllArgsConstructor
#NoArgsConstructor
public class MyPojo {
private String someString;
private AnotherPojo someOtherPojo;
//The problem child:
private Object value;
}
The field value which is an exception to this rule, can represent any JSON field matching value* where * is a wildcard of indefinite length. That means valueString or valueReference in JSON will be assigned to this field with the assertion that only one may be present.
{
"someString": "asdasdadasdsa",
"someOtherPojo": {
"someOtherProperty": "whatever"
},
"valueCodeableConcept": {
"text": "text value",
"coding": [
{
"code": "my-code"
}
]
}
}
Using a custom deserializer on the top-level class, I can scrape all of the fields from the root node (baseNode in the following example) that start with value and set the value field appropriately. That works great! However, in doing so, I now have to set every other field in this MyPojo class manually in my deserializer, and I have to put a custom copy of this deserializer on each POJO that uses a field like value*.
private Object parseValueX(JsonNode baseNode, DeserializationContext context) throws IOException {
//Find the concrete implementation referred to by the value[x] field
Set<String> concreteNames = new HashSet<>();
baseNode.fieldNames().forEachRemaining(name -> {
if (name.startsWith("value")) {
concreteNames.add(name);
}});
if (concreteNames.isEmpty()) {
return null;
}
if (concreteNames.size() > 1) {
throw JsonMappingException.from(context, "The field value[x] must have no more than one concrete " +
"implementation, ex: valueCode, valueCodeableConcept, valueReference");
}
String concreteName = concreteNames.stream().findFirst().orElseThrow(() -> new RuntimeException(""));
JsonNode jsonSource = baseNode.get(concreteName);
//...deserialize from jsonSource, solved, but not relevant to question...
}
To make this apply to any value* property on any POJO, I tried to move the deserializer to the value attribute in the POJO (whereas it's on the top-level resource now). The first flaw is that the deserializer isn't even invoked unless the JSON property exactly matches value. What I actually need is for the entire parent JSON resource to be passed to that field-specific deserializer, so that I may find the matching field and assign it -- OR -- I need to be able to have the deserializer on MyPojo only assign the one field value and allow the automatic deserialization to take care of the others. How do I do either of these?
For those curious about my motivation, I am implementing the HL7 FHIR Specification, which specifies generic attributes called value[x] (here's one example: https://www.hl7.org/fhir/extensibility.html#Extension) where [x] becomes the type of the resource.
I think a good fit for you problem is #JsonAnySetter. This method annotation tells Jackson to route unknown properties to it. the arg (in your case) is a Map containing the json tree of the unknown property. if I understand your code properly, the name of the value property contains the class name of the target Pojo. so once you have a class name, you can tell Jackson how to "deserialize" the map into an instance of the target class.
Here is an example based on the code from the question
public class MyPojo {
public String someString; // made properties into public for this example...
public AnotherPojo someOtherPojo;
public Object value;
#JsonAnySetter
public void setValue(String name, Object value) {
System.out.println(name + " " + value.getClass());
System.out.println(value);
// basic validation
if (name.startsWith("value") && value instanceof Map) {
String className = "com.company." + name.substring("value".length());
System.out.println(name + " " + value.getClass() + " " + className);
System.out.println(value);
try {
// nice of Jackson to be able to deserialize Map into Pojo :)
ObjectMapper mapper = new ObjectMapper();
this.value = mapper.convertValue(value, Class.forName(className));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.value + " " + this.value.getClass());
}
}
}
public class AnotherPojo {
public String someOtherProperty;
}
public class CodeableConcept {
public String text;
public Code[] coding;
}
public class Code {
public String code;
}

Populate POJO with array (root JSON node) using Jackson

I'm using Jackson and RESTEasy to hook into an external API. The API mainly returns simple objects which I have managed to successfully populate into POJOs.
I'm hitting a problem where I get an array of objects back e.g.
[
{
"variable1": "someValue1",
"variable2": "someValue2",
"variable3": "someValue3"
}
{
"variable1": "someValue4",
"variable2": "someValue5",
"variable3": "someValue6"
}
{
"variable1": "someValue7",
"variable2": "someValue8",
"variable3": "someValue9"
}
]
I have 2 classes: one called VariableObject which looks like this:
public class VariableObject {
private String variable1;
private String variable2;
private String variable3;
}
and VariableResponse which looks like:
public class VariableResponse {
private List<VariableObject> variableObjects;
}
My client uses JAXRS Response class to read the entity into the class i.e
return response.readEntity(VariableResponse.class);
I get a stack trace which reads:
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of VariableResponse out of START_ARRAY token
I understand you can return these as a List of POJOs i.e List quite easily, but this is not what I want to do.
The question really is two parts:
a. Can I possibly populate the VariableResponse POJO using Jackson (some how) preferably without a customer deserialiser? Maybe some annotation exists (this would be ideal)?
b. Is there some way to detect if an Array is being retuned as the root JSON node in the response and then act accordingly?
Help greatly appreciated.
Your JSON is indeed an array of objects.
You can deserialize it with:
response.readEntity(new GenericType<List<VariableObject>>() {});
And then create a new instance of VariableResponse passing resulting List as a constructor parameter like this:
public class VariableResponse {
private final List<VariableObject> variableObjects;
public VariableResponse(List<VariableObject> variableObjects) {
this.variableObject = new ArrayList<>(variableObjects);
}
}
You might forget to add comma after each {..}. After correcting your JSON string, I converted it into ArrayList<VariableObject> using TypeReference and ObjectMapper.
sample code:
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
...
TypeReference<ArrayList<VariableObject>> typeRef = new TypeReference<ArrayList<VariableObject>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
ArrayList<VariableObject> data = mapper.readValue(jsonString, typeRef);
for (VariableObject var: data) {
System.out.println(var.getVariable1()+","+var.getVariable2()+","+var.getVariable3());
}
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
output:
someValue1,someValue2,someValue3
someValue4,someValue5,someValue6
someValue7,someValue8,someValue9
If you prefer your own response type direct.
Try just extending ArrayList?
public VariableResponse extends ArrayList<VariableObject> {
}

Mapping JSON back to POJO with JSON and Jackson libs

I have a JSON string:
{
"fruit": {
"weight":"29.01",
"texture":null
},
"status":"ok"
}
...that I am trying to map back into a POJO:
public class Widget {
private double weight; // same as the weight item above
private String texture; // same as the texture item above
// Getters and setters for both properties
}
The string above (that I am trying to map) is actually contained inside an org.json.JSONObject and can be obtained by calling that object's toString() method.
I would like to use the Jackson JSON object/JSON mapping framework to do this mapping, and so far this is my best attempt:
try {
// Contains the above string
JSONObject jsonObj = getJSONObject();
ObjectMapper mapper = new ObjectMapper();
Widget w = mapper.readValue(jsonObj.toString(), Widget.class);
System.out.println("w.weight = " + w.getWeight());
} catch(Throwable throwable) {
System.out.println(throwable.getMessage());
}
Unfortunately this code throws an exception when the Jackson readValue(...) method gets executed:
Unrecognized field "fruit" (class org.me.myapp.Widget), not marked as ignorable (2 known properties: , "weight", "texture"])
at [Source: java.io.StringReader#26c623af; line: 1, column: 14] (through reference chain: org.me.myapp.Widget["fruit"])
I need the mapper to:
Ignore the outer curly brackets ("{" and "}") altogether
Change the fruit to a Widget
Ignore the status altogether
If the only way to do this is to call the JSONObject's toString() method, then so be it. But I'm wondering if Jackson comes with anything "out of the box" that already works with the Java JSON library?
Either way, writing the Jackson mapper is my main problem. Can anyone spot where I'm going wrong? Thanks in advance.
You need to have a class PojoClass which contains (has-a) Widget instance called fruit.
Try this in your mapper:
String str = "{\"fruit\": {\"weight\":\"29.01\", \"texture\":null}, \"status\":\"ok\"}";
JSONObject jsonObj = JSONObject.fromObject(str);
try
{
// Contains the above string
ObjectMapper mapper = new ObjectMapper();
PojoClass p = mapper.readValue(jsonObj.toString(), new TypeReference<PojoClass>()
{
});
System.out.println("w.weight = " + p.getFruit().getWeight());
}
catch (Throwable throwable)
{
System.out.println(throwable.getMessage());
}
This is your Widget Class.
public class Widget
{ private double weight;
private String texture;
//getter and setters.
}
This is your PojoClass
public class PojoClass
{
private Widget fruit;
private String status;
//getter and setters.
}

jackson in Spring MVC

I have two java classes for parsing jason into java alone. Beyond that, the classes are not used for any thing. Below are the two classes.
import java.util.ArrayList;
public class PaymentsPaid {
public ArrayList<PaidDetailAmounts> amount;
}
and
public class PaidDetailAmounts {
public Long invoiceFeeId;
public Double amountPaid;
}
Here is where the string and the use of and object mapper.
"amount": [{"invoiceFeeId": 12085, "amountPaid": 100},{"invoiceFeeId": 12084, "amountPaid": 100},{"invoiceFeeId": 12086, "amountPaid": 500}]
and the mapper code
ObjectMapper mapper = new ObjectMapper();
try {
PaymentsPaid paymentsPaidModel = mapper.readValue(httpServletRequest.getParameter("amount"), PaymentsPaid.class);
/*
Iterator<PaidDetailAmounts> iterator = paymentsPaidModel.amount.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().invoiceFeeId);
}
*/
} catch (JsonParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
This is my exception:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "
invoiceFeeId" (Class PACKAGE_NAME.PaymentsPaid), not marked as ignorable
I must be doing something worng, because I built a search feature using this approach and it is currently in my application and working well. Please advise. I think it may be a mal formed json string, because it should be an array.
The Problem seams to be that jackson tryes to access the invoiceFeeId field of class PaymentsPaid, but it is a field of class PaidDetailAmounts.
I think there is some surrounding brackets missing in your Json string:
{ // <-- missing?
"amount": [{"invoiceFeeId":...}]
} // <-- missing?
But I am not an JSON expert, so I would try to write an simple test case that create a JSON string from some Java Objects and then parse this strings to back to Java Objects. So that the test can assert that both (sets of) objects are equals.
Then you can use the JSON String created by the test and compare it with your input, I would expect that the (missing) brackets are the difference between them.

Categories