Converting JSON input using Jackson into generic array - java

I have the following JSON file, where one field is the number of elements in an array (not relevant)
and the other is the "res" field of type Person:
{
"num" : 2,
"res" : [
{
"id": 1,
"name": "Person1"
},
{
"id": 2,
"name": "Person2"
}
]
}
Person class:
public class Person {
int id;
String name;
}
How to convert this JSON into Person[] array?
I've tried using the Generic wrapper class and readValue
method:
class Wrapper<T> {
private int num;
private T[] res;
// ... getters and setters
}
...
ObjectMapper mapper = new ObjectMapper();
Wrapper<Person> wrapper = mapper.readValue(reqString, Wrapper.class)
and I get 2 hash maps instead of Person objects. Is there an easier way to get the Person[] array? I was
thinking of somehow just filtering the "res" field and then doing readValue, but I don't have a clear idea of how to do that.

Try a TypeReference instance:
class Wrapper<T> {
private int num;
private T[] res;
// ... getters and setters
}
...
ObjectMapper mapper = new ObjectMapper();
Wrapper<Person> wrapper = mapper.readValue(reqString, new TypeReference<Wrapper<Person>>{})
Generic arrays often cause problem, so I'd suggest using a List<T> instead:
class Wrapper<T> {
private int num;
private List<T> res;
// ... getters and setters
}
You can implement a getter which converts the list to an array upon access. To deserialize, you will still need to use a TypeReference instance.
Or define a non-generic wrapper to avoid the TypeReference:
class PersonWrapper {
private int num;
private Person[] res;
// ... getters and setters
}

Related

reading nested json with jackson - MismatchedInputException: Cannot deserialize value

stucked at accessing nested json. similar stuff:
[
{
key-value,
key-value
},
{
key-value,
key-value
},
{
key-value,
key-value
}
]
works nicely but when i try:
{
"alfa":{
"id":"foo",
"product":{
"id":"foo1",
"price":"foo2"
}
},
"beta":{
"id":"foo",
"product":{
"id":"foo1",
"price":"foo2"
}
}
}
i get error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<...
i of course did change structure of classes:
public class Alphabet{
private Purchase purchase;
...
public class Purchase{
private String id;
private Product product;
...
public class Product {
private String id;
private String price;
...
to read it:
ObjectMapper mapper = new ObjectMapper();
InputStream inputStream = new FileInputStream(new File("src/main/json/file.json"));
TypeReference<List<Alphabet>> typeReference = new TypeReference<List<Alphabet>>() {};
List<Alphabet> alphabet= mapper.readValue(inputStream, typeReference);
System.out.println(alphabet);
whats wrong, please?
It seems like the JSON structure you try to read is not a List<Alphabet>, but a Map<String, Purchase>.
Your second json not like list of object. The second json look lik have 2 main objects if so you need class like below.
public class Alphabet{
private Purchase purchase;
private Purchase purchase1;
}
But it not good practice. Use as first josn like list of objects.

JSON to Java object with Random Key

I am trying to convert following JSON to Java object and ending up with UnrecognizedPropertyException.
{
"5214": [{
"name": "sdsds",
"age": "25",
"address": null
},
{
"name": "sdfds",
"age": "26",
"address": null
}]
}
Here "5214" is the random key that I get. I can covert it by modifying JSON little bit. But I want to know whether any possible way to convert the mentioned JSON. I even tried with following snippet taking some reference.
public class SampleTest {
private Map<String, List<EmployeeDetails>> employeeDetails = new HashMap<String, List<EmployeeDetails>>();
public Map<String, List<EmployeeDetails>> getEmployeeDetails() {
return employeeDetails;
}
public void setEmployeeDetails(Map<String, List<EmployeeDetails>> employeeDetails) {
this.employeeDetails = employeeDetails;
}
}
public class EmployeeDetails {
private String name;
private String age;
private String address;
//Getters and Setters
}
Can someone guide me on this?
Use Type Reference (Import Jackson Package for Java)
TypeReference<Map<String, List<EmployeeDetails>>> typeReference = new TypeReference<Map<String, List<EmployeeDetails>>>()
{
};
Map<String, List<EmployeeDetails>> employeeDetails = new ObjectMapper().readValue(jsonString, typeReference);
Check something from that
Maybe:
public class Data {
// String contain the Key, for example: 5214
Map<String, List<EmployeeDetails>> employeeDetails =
new HashMap<String,List<EmployeeDetails>>();
public Data() {
}
#JsonAnyGetter
public Map<String, List<EmployeeDetails>> getEmployeeDetails() {
return employeeDetails;
}
}
I would use custom deserializer with few helper classes. To make the code (matter of opinion I guess) clearer, create the list object:
#SuppressWarnings("serial")
#Getter #Setter
public class EmployeeDetailsList extends ArrayList<EmployeeDetails> {
// this will hold the arbitrary name of list. like 5214
private String name;
}
Then this list seems to be inside an object, say Wrapper:
#Getter
#RequiredArgsConstructor
#JsonDeserialize(using = WrapperDeserializer.class)
public class Wrapper {
private final EmployeeDetailsList employeeDetailsList;
}
So there is annotation #JsonDeserializer that handles deserializing Wrapper. It is not possible to directly deserialize unknown field names to some defined type so we need to use mechanism like this custom deserializer that inspects what is inside Wrapper and determines what to deserialize and how.
And here is how the deserializer works:
public class WrapperDeserializer extends JsonDeserializer<Wrapper> {
private final ObjectMapper om = new ObjectMapper();
#Override
public Wrapper deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
TreeNode node = p.readValueAsTree();
// This is the place for caution. You should somehow know what is the correct node
// Here I happily assume there is just the one and first
String fName = node.fieldNames().next();
EmployeeDetailsList edl = om.readValue(node.get(fName).toString(),
EmployeeDetailsList.class);
edl.setName(fName);
return new Wrapper(edl);
}
}
Please check it carefully it is not perfect in sense finding alwasy the correct node and maybe the instantiation can be done in other ways better. But it shoudl give you a hunch how it could be done.

Is there any way to create an object with attribute from multiple arrays?

I have a JSON file which is a menu. So there is one array with pizza's and inside that array is a array called ingredient which contains the id from the ingredients.
So I want to create objects who has the attributes from the pizza arrays with the value from the ingredient array.
My error is below:
java.lang.IllegalArgumentException (out of START_OBJECT token)
I already created a object which only accesses to the pizza array.
In the code you can see how I tried to convert it.
Code snippet
public static void main(String[] args)
throws JsonGenerationException, JsonMappingException, IOException
{
//File file = new File("path");
ObjectMapper mapper = new ObjectMapper();
try
{
JsonNode gesMenu = mapper.readValue(file, JsonNode.class);
JsonNode jMenu = gesMenu.get("Menu");
JsonNode gesIngredient = jMenu.get("ingredient");
Ingredient[] cIngredient = mapper.convertValue(gesIngredient, Ingredient[].class);
System.out.println(cIngredient[7].getDescription());;
JsonNode gesPizza = jMenu.get("pizza");
System.out.println("\n" + gesPizza);
//These last two lines cause Errors
Pizza2[] pPizza = mapper.convertValue(gesPizza, Pizza2[].class);
System.out.println(pPizza[0]);
}
...
Here is a example of the JSON file:
{
"menu" : {
"pizza" : [
{
"nr" : 1,
"description" : "Pizza Salami",
"ingredient" : [
{
"id" : 0
}
],
"Picture" : "Salami.jpg"
}
]
}
}
According to the structure of JSON string you provide and what you mentioned in comment, there is a simple way to convert whole JSON string to nested POJOs as follows:
ObjectMapper mapper = new ObjectMapper();
MyJsonObject myJsonObj = mapper.readValue(jsonStr, MyJsonObject.class);
System.out.println(myJsonObj.toString());
Console output
MyJsonObject [menu=Menu [pizza=[Pizza [nr=1, description=Pizza Salami, ingredient=[Ingredient [id=0, description=null, priceSmall=null, priceMedium=null, priceBig=null]], picture=Salami.jpg]]]]
Whereas the nested POJOs look like:
class MyJsonObject {
private Menu menu;
//general getters and setters
//toString()
}
class Menu {
private List<Pizza> pizza;
//general getters and setters
//toString()
}
class Pizza {
private int nr;
private String description;
private List<Ingredient> ingredient;
#JsonProperty("Picture")
private String picture;
//general getters and setters
//toString()
}
class Ingredient {
private int id;
private String description;
private String priceSmall;
private String priceMedium;
private String priceBig;
//general getters and setters
//toString()
}
Then you can access both pizza or ingredient JSON arrays easily just like operating objects in Java!

How to deserialize a generic type with jackson?

I tried many solutions, but my case seems special. The #JsonProperty must be depending on class type:
I have JSON for two entities:
"Person": [ { "id": "452009517701", "name": "Perosn1",
"address": "541-DPL-355" } ]
"Car": [ { "id": 5787544, "brand": "Toyota", "number":
12454 } ]
The entities look like :
public class Person{
private String id:
private String name;
private String address:
// Constcutors && Getters && Setters
}
public class Car{
private Long id:
private String brand;
private Long number:
// Constcutors && Getters && Setters
}
The generic class :
public class GenericEntity<T>{
//#JsonProperty
private List<T> myList;
// Constcutors && Getters && Setters
}
Main class :
public static void main(String[] args) {
ObjectMapper mapper=new ObjectMapper();
GenericEntity p=mapper.readValue(personJson,GenericEntity.class);
GenericEntity c=mapper.readValue(carJson,GenericEntity.class);
}
When I debug I find that the lists inside GenericEntity are always null. I do not know how to set jsonProperty dynamically on the top of the list inside the GenericEntity.
Also, i used :
Object readValue = mapper.readValue(jsonPerson, new TypeReference<GenericEntity<Person>>() {});
And :
JavaType javaType = mapper.getTypeFactory().constructParametricType(GenericEntity.class, Person.class);
Object readValue =mapper.readValue(jsonPerson, javaType);
Bu i got this :
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.test.GenericEntity` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('Person')
at [Source: (String)""Person": [ { "id": "452009517701", "name": "Perosn1", "address": "541-DPL-355" } ]"; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
The simplest option would be to use a wrapper type with a separate field per collection type, like that:
class GenericEntity {
#JsonProperty("Car") List<Car> car;
#JsonProperty("Person") List<Person> person;
}
This way you would always have one of those lists filled (according to our conversation in comments). This will work fine as long as you don't have too many types and it doesn't change too frequently :)
The more-advanced way would be to use a custom deserializer, like that:
#JsonDeserialize(using = MyDeserializer.class)
class GenericEntity<T> {
List<T> myList;
GenericEntity(List<T> myList) {
this.myList = myList;
}
}
The deserializer itself would have to create a GenericEntity on its own, but it can delegate all specific-type-deserializing job to other deserializers (so our job would be just to tell it what to deserialize and to what type):
class MyDeserializer extends JsonDeserializer<GenericEntity<?>> {
#Override
public GenericEntity<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = p.getCodec();
JsonNode node = codec.readTree(p);
if (node.hasNonNull("Person")) {
JsonParser nodeParser = node.get("Person").traverse(codec);
nodeParser.nextToken();
Person[] people = ctxt.readValue(nodeParser, Person[].class);
return new GenericEntity<>(asList(people));
} else if (node.hasNonNull("Car")) {
JsonParser nodeParser = node.get("Car").traverse(codec);
nodeParser.nextToken();
Car[] cars = ctxt.readValue(nodeParser, Car[].class);
return new GenericEntity<>(asList(cars));
}
throw new RuntimeException("Couldn't find a type to deserialize!");
}
}

How do I configure the the jackson objectmapper to correctly deserialize to pojo?

I'm having a bit of a problem understanding how i should configure the objectMapper and pojo when deserializing. My Json is created by another application that
supports both xml and json. It returns a list with myobject, but the Json contains the type, like this:
[
{
"myobject": {
"somethingcool": "amazing",
"contactPersonsForMyObject": [
"test.test#gmail.com",
"test#test.se"
],
"myObjectId": "c85e48730501bfae41e67714c6131b7d"
}
},
{
"myobject": {
"somethingcool": "cool",
"contactPersonsForMyObject": [
"test.test2#gmail.com",
"test#test2.se"
],
"myObjectId": "c85e48730501bfae41e67714cqwerty"
}
}
]
My class:
public class MyObject {
private String myObjectId;
private String somethingcool;
private List<String> contactPersonsForMyObject;
public String getMyObjectId() {
return myObjectId;
}
public void setMyObjectId(String myObjectId) {
this.myObjectId = myObjectId;
}
public String getSomethingcool() {
return somethingcool;
}
public void setSomethingcool(String somethingcool) {
this.somethingcool = somethingcool;
}
public List<String> getContactPersonsForMyObject() {
return contactPersonsForMyObject;
}
public void setContactPersonsForMyObject(List<String> contactPersonsForMyObject) {
this.contactPersonsForMyObject = contactPersonsForMyObject;
}
}
But when doing:
List<MyObject> myObjects = mapper.convertValue(rootNode, new TypeReference<List<MyObject>>() {});
I'm getting a exception stating:
java.lang.IllegalArgumentException: Unrecognized field "myobject" (Class com.domain.MyObject), not marked as ignorable
at [Source: N/A; line: -1, column: -1] (through reference chain: com.domain.MyObject["myobject"])
It's like the mapper do not understand the extra "layer".
When serializing to get this structure it is possible to configure the mapper like this: mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
So there should be somehow to do the reverse?
Thank you!
You need to give it concrete classes and not interfaces. So
List<Map<String, MyObject>> myObjects = mapper.readValue(json, new TypeReference<ArrayList<HashMap<String, MyObject>>>() {
});
What you need is to use #JsonTypeInfo annotation on type (class), which will include additional type information. In your case it looks as if you wanted to include a type id as property key.
If so, inclusion method should be "as wrapper object", and you will also need to define what type id of "myobject" binds to -- this can be done by adding #JsonTypeName("myobject") for MyObject class (it needs to be included in subtype of whatever has #JsonTypeInfo, but in this case both would be added for the same class).
Your json has an extra level of nesting: you have a list of Maps of Strings to MyObjects, not a List of MyObjects. You'd need to read it like this:
List<Map<String, MyObject>> myObjects = mapper.readValue(json, new TypeReference<List<Map<String, MyObject>>>() {
});
Or else change whatever is generating this json to ditch the inner Map (IMHO that'd be better).
Change List<String> to ArrayList<String>
and then
MyObject myObject = mapper.readValue(json, MyObject.class);
Add the following constructor to MyObject class
#JsonCreator
public MyObject(#JsonProperty("myObjectId") String myObjectId,
#JsonProperty("somethingcool") String somthingcool,
#JsonProperty("contact") ArrayList<String> contactPersonsForMyObject) {
this.myObjectID = myObjectId;
this.somethingcool = somethingcool;
this.contactPersonsForMyObject = contactPersonsForMyObject;
}
and change the return value for the getter to ArrayList

Categories

Resources