Let's assume we have JSON object like this:
{
"employees" : {
"Mike" : 23,
"Bill" : 42,
"Jimmy" : 30
}
}
And the next classes:
public class Employees {
private List<Employee> employees;
}
public class Employee {
private String name;
private int age;
}
Have we any ways to deserialize the json to Employees type object?
List can be used to deserialize array of elements [...] but {...} is object. If you want you can consider using Map instead of List
class Data {
Map<String,String> employees;
}
...
Gson gson = new Gson();
Data data = gson.fromJson(json, Data.class);
for (Map.Entry<String, String> emp: data.employees.entrySet())
System.out.println(emp.getKey()+" -> "+emp.getValue());
Output:
Mike -> 23
Bill -> 42
Jimmy -> 30
If you would like to parse your json using these classes
public class Employees {
private List<Employee> employees;
}
public class Employee {
private String name;
private int age;
}
Your data should look like:
{
"employees" : [
{"name":"Mike", "age": 23},
{"name":"Bill", "age": 42},
{"name":"Jimmy", "age": 30}
]
}
You can use Jackson's ObjectMapper class to deserialize JSON like so: https://stackoverflow.com/a/6349488/2413303
Related
Using this input
{
"personId": "uhqwe-8ewn-3129m",
"infoPerson": {
"name": "john",
"age" : 35
}
},
....
And this POJO
public class Person {
private String name;
private int age;
//getter/setter
}
I need to read it as
private Map<String,Person> pMap;
using the personId as the map key.
Is there a way to do it with jackson annotation?
As far as I know, it is not possible.
You can read a list and then convert it to a map.
Something like this
Map<String, Person> map = list.stream()
.collect(Collectors.toMap(Person::getPersonId, person -> person));
I have a JSON structured like:
{
"id" : "123",
"name" : [ {
"id" : "234",
"stuff" : [ {
"id" : "345",
"name" : "Bob"
}, {
"id" : "456",
"name" : "Sally"
} ]
} ]
}
I want to map to the following data structure:
Class01
#Getter
public class Class01{
private String id;
#JsonDeserialize(using = Class01HashMapDeserialize.class)
private ArrayList<Class02> name;
}
Class02
#Getter
public class Class02{
private String id;
private ArrayList<Class03> stuff;
}
Class03
#Getter
public class Class03{
private String id;
private String name;
}
In my main Method im using an ObjectMapper with objectMapper.readValue(jsonString,new TypeReference<ArrayList<Class02>>(){}) to map this JSON to my Class01. This Class successfully deserealizes the Class02-array into the name array.
When it comes to the second array I don't know how to further deserialize as I am not able to access the json text from the class02 stuff entry.
#Override
public ArrayList<Class02> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
ArrayList<Class02> ret = new ArrayList<Class02>();
ObjectCodec codec = parser.getCodec();
TreeNode classes02 = codec.readTree(parser);
if (classes02.isArray()) {
for (JsonNode class02 : (ArrayNode) classes02) {
if(classe02.get("stuff").isArray()){
ObjectMapper objectMapper = new ObjectMapper();
ArrayList<Class03> classes03 = objectMapper.readValue(class02.get("stuff").asText(), new TypeReference<ArrayList<Class03>>(){});
}
ret.add(new Class02(class02.get("id").asText(), classes03));
}
}
return ret;
}
Why did you put a #JsonDeserialize annotation ? Jackson shall be able to deserialize it just fine without any custom mapping:
#Getter
public class Class01{
private String id;
private ArrayList<Class02> name;
}
Also in a first pass, I would generate the getters/setters/constructor manually for the 3 classes. There may be issues with Lombok & Jackson that you may want to solve later once you made the first version of the code works (Can't make Jackson and Lombok work together)
And your reader shall be more like:
ObjectMapper objectMapper = new ObjectMapper();
String text = ... //Your JSon
Class01 class01 = objectMapper.readValue(text, Class01.class)
First, I know there are many questions on this topic but I couldn't find one that solves my problem.
I need to deserialize with Gson a json that is in this form:
{
"name": "abc",
"entries":
[
[
"first_entry_name",
{"is_ok": true, "type": "first_entry_type"}
],
[
"second_entry_name",
{"is_ok": false, "type": "second_entry_type"}
]
]
}
I've implemented the classes:
class Entries
{
String name;
ArrayList<Entry> entries;
}
class Entry
{
String name;
Details details;
}
class Details
{
Boolean is_ok;
String type;
}
I'm deserializing with:
Entries ent = new Gson().fromJson(json, Entries.class);
And I'm getting this error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY
I understand why I'm getting this error but I can't think of a way to deserialize this json.
How should this json be deserialized?
Code is fine but your JSON file should be like below
{
"name": "abc",
"entries":
[
{
"first_entry_name":{"is_ok": true, "type": "first_entry_type"}
},
{
"second_entry_name":{"is_ok": false, "type": "second_entry_type"}
}
]
}
In the json posted in original question, there was list inside a list (entries) but ideally it should be json element inside a list.
Here is the screenshot of code with output
Hope this helps
i am not really sure if that help , i come from c# world . but i think that will be a generic problem , so try to cast your instance (ent) from object to array or list of that Type ..
i mean like this
this c# syntax but i think there are mostly the same with java
<List>Entries ent = (<List>Entries)new Gson().fromJson(json, Entries.class);
it can be also here Array but List is more dynamic .. hopefully works
You may do something like below.
try adding another wrapper class as I have added sample here as EntryTwo.
class Entries
{
String name;
ArrayList<Entry> entries;
}
class Entry
{
ArrayList<EntryTwo> entryTwo;
}
class EntryTwo
{
String name;
Details details;
}
class Details
{
Boolean is_ok;
String type;
}
Hope this works...
You need to implement custom deserialiser for List<Entry> type:
class EntriesJsonDeserializer implements JsonDeserializer<List<Entry>> {
#Override
public List<Entry> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonArray array = json.getAsJsonArray();
List<Entry> entries = new ArrayList<>(array.size());
for (JsonElement item : array) {
JsonArray itemArray = item.getAsJsonArray();
entries.add(parseEntry(context, itemArray));
}
return entries;
}
private Entry parseEntry(JsonDeserializationContext context, JsonArray array) {
Entry entry = new Entry();
entry.setName(array.get(0).getAsString());
entry.setDetails(context.deserialize(array.get(1), Details.class));
return entry;
}
}
You can register it using JsonAdapter annotation:
class Entries {
String name;
#JsonAdapter(EntriesJsonDeserializer.class)
List<Entry> entries;
}
You can try following code:
public class Entries {
private String name;
private List<List<String>> entries;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setEntries(List<List<String>> entries) {
this.entries = entries;
}
public List<List<String>> getEntries() {
return entries;
}
}
I'm wondering if there is any way to deserialize several JSON fields to just one Java property. E.g. given this JSON:
{
"id" : "1",
"name" : "Bartolo",
"address" : "whatever",
"phone" : "787312212"
}
deserialize it to this class:
public class Person {
public String id;
public String name:
#JsonProperty(names = {"address", "phone"}) //something like this
public String moreInfo;
}
so moreInfo equals to "whatever, 787312212" or something similar.
Is this possible without using custom deserializer?
You could use the #JsonCreator annotation like following:
String json = {"id" : "1", "name" : "Bartolo", "address" : "whatever", "phone" : "787312212" }
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json , Person.class);
and in the constructor of your Person class add this
#JsonCreator
public Person(#JsonProperty("address") String address, #JsonProperty("phone") String phone) {
this.moreInfo = address + "," phone;
}
Another solution, if you don't want to know/handle other fields in the object, but decided to still receive these fields (maybe for logging purposes), then you can put them in a key-value store(Map)
#Getter
private final Map<String, Object> otherFields = new HashMap<>();
#JsonAnySetter
public void set(String name, Object value) {
otherFields.put(name, value);
}
Note that if you have any field with the same name as the Map field(like 'otherFields' in the example above), then you can get MismatchedInputException
I've the following JSON from some upstream api
{
"Id": "",
"Name": "",
"Age": ""
}
And I need to map this above json to a downstream request paylaod (POJO) .
public class Employee
{
#JsonProperty("Id")
private Integer Id;
private User user;
}
public class User {
#JsonProperty("Name")
private String name;
#JsonProperty("Age")
private String age;
}
Right now I'm doing something like
Employee employee = new ObjectMapper().treeToValue(JsonNode node,Employee.class);
But this is giving null in User Object.
The challenge here is , that the json we are getting from upstream can't be changed . So , is there is any way to map the fields into the nested User object , without changing the structure of json received from upstream.
One Solution is : map the fields separately into User object and then set it into the Employee object . But that's not an efficient solution , because for null validations we would need to do validations separately for User and Employee objects. If the nesting is complex then , validation will be hell of replicated code .
Your JSON does not comply with your Employee class.
Because name and age is at the same level as id, but you want to wrapped in a class User.
So either:
Change the json the structure to
{
"id": "",
"user": {
"name": "",
"age": ""
}
}
Or
Unwrap the User class, the Employee class will be:
public class Employee
{
#JsonProperty("Id")
private Integer Id;
#JsonProperty("Name")
private String name;
#JsonProperty("Age")
private String age;
}
Edit
If you can't choose either option 1 or 2, you have only one option left is to create custom deserializer:
Write a deserializer:
public class EmployeeDeserializer extends StdDeserializer<Item> {
public EmployeeDeserializer() {
this(null);
}
public EmployeeDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Employee deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
int id = (Integer) ((IntNode) node.get("Id")).numberValue();
String name = node.get("Name").asText();
String age = node.get("Age")).asText();
User user = new User(name, age);
return new Employee(id, user);
}
}
Then register this deserializer:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Employee.class, new EmployeeDeserializer());
mapper.registerModule(module);
Employee readValue = mapper.readValue(json, Employee.class);
Another way to register deserializer is:
#JsonDeserialize(using = EmployeeDeserializer.class)
public class Employee {
It seems you are not nesting your JSON correctly. Or your Object Structure is wrong.
JSON should be:
{
"Id": "",
"user" : {
"Name": "",
"Age": ""
}
}
The json structure does not match the structure of your classes.
if the json was like;
{
"Id": "an-id,
"user": {
"Name": "Joe",
"Age": "21"
}
}
Then your code to deserialise to an Employee object would work.