How to parse a JSON array of objects using Jackson? - java

I have a Json response as follows:
{
"data": {
"date": "7 Apr 2022",
"employee": [
{
"id": [
"1288563656"
],
"firstname": [
"Mohammed"
],
"lastname": [
"Ali"
]
}
]
}
}
I am trying to create a POJO called Employee and map it to the "employee" attribute in the JSON response.
This is what I did as an attempt:
Employee.java
public class Emoloyee {
private Integer[] id;
private String[] firstname;
private String[] lastname;
public Employee(Integer[] id, String[] firstname, String[] lastname){
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
}
public Employee(){
}
public Integet[] getId(){
return id;
}
public void setId(Integer[] id){
this.id = id;
}
public String[] getFirstname(){
return firstname;
}
public void setFirstname(String[] firstname){
this.firstname = firstname;
}
public String[] getLastname(){
return lastname;
}
public void setLastname(String[] lastname){
this.lastname = lastname;
}
}
Using Jackson:
ObjectMapper mapper = new ObjectMapper();
URL jsonUrl = new URL("[API_URL]");
final ObjectNode node = mapper.readValue(jsonUrl, ObjectNode.class);
Employee[] employees = mapper.treeToValue(node.get("data").get("employee"), Employee[].class);
When I execute the app, I get the following error:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type 'long' from Array value (toke 'JsonToken.START_ARRAY')
As you may noticed, I am not interested of the date attribute, I only need to get the values of the employee and create an Employee object out of it.

The Employee POJO should look like this:
#JsonProperty("id")
private final List<String> ids;
#JsonProperty("firstname")
private final List<String> firstNames;
#JsonProperty("lastname")
private final List<String> lastNames;
#JsonCreator
public Employee(#JsonProperty(value = "id") List<String> ids, #JsonProperty(value = "firstname") List<String> firstNames, #JsonProperty(value = "lastname") List<String> lastNames) {
this.ids = ids;
this.firstNames = firstNames;
this.lastNames = lastNames;
}
//getters code
Then, you have an object Data:
#JsonProperty("date")
private final String date;
#JsonProperty("employee")
private final List<Employee> employees;
#JsonCreator
public Data(#JsonProperty(value = "date") String date, #JsonProperty(value = "employee") List<Employee> employees) {
this.date = date;
this.employees = employees;
}
//getters code
Finally, the whole Answer that you want to parse has this shape:
#JsonProperty("data")
private final Data data;
#JsonCreator
public Answer(#JsonProperty(value = "data") Data data) {
this.data = data;
}
//getter code
Once you have defined these 3 classes, then you will be able to do:
ObjectMapper objectMapper = new ObjectMapper();
Answer answer = objectMapper.readValue(yourStringAnswer, Answer.class);
Note: in your question, you are trying to parse an URL to an ObjectNode. I hardly doubt you would be able to do that.
I guess you want to perform the HTTP request to the URL, then getting the response stream and that's what you want to parse into Answer (not the URL itself).
Also, a few notes on the API response (in case you own it and so you can act on it):
All the lists would be more naturally declared with a plural name (e.g. employee should be employees)
The list of ids are numeric but are returned as strings. Also, why an employee would have a list of ids, and not a single id?
Why would an employee have a list of first names and last names? Shouldn't this be a simple string each (even if composed by more than one name)?
Use camel case (firstName, not firstname)
I don't see the point of putting everything into data, it may simply be the response containing date and employees

Related

Java-Spring Json field with associated classes

I want to make a json field in my spring entity like this
#Type(type = "json")
#Column(name = "LIFE_CYCLE_RULES")
private List<S3LifeCycleRule> lifeCycleRules;
but the class S3LifeCycleRule has associated classes
private S3LifeCycleExpiration expiration;
private String id;
private String prefix;
private S3LifeCycleFilter filter;
private Boolean status;
private List<S3LifeCycleTransition> transitions;
S3LifeCycleExpiration.java:
#JsonSerialize
public record S3LifeCycleExpiration(
Instant Date,
int Days,
Boolean ExpiredObjectDeleteMarker
) {
}
S3LifeCycleTransition.java:
#JsonSerialize
public record S3LifeCycleTransition(
Instant date,
int days,
S3ObjectStorageClass storageClass
) {
}
(S3ObjectStorageClass is an enumerator)
S3LifeCycleFilter.java:
#JsonSerialize
public record S3LifeCycleFilter (
String prefix,
S3LifeCycleTag tag,
S3LifeCycleAnd and
){
}
S3LifeCycleTag.java:
#JsonSerialize
public record S3LifeCycleTag(
String key,
String value
) {}
S3LifeCycleAnd.java:
#JsonSerialize
public record S3LifeCycleAnd(
String prefix,
List<S3LifeCycleTag> tags
) {
}
Before I added the JsonSerialize annotation the application did not work entirely, but after I added it the fields: expiration, filter and transitions stayed empty:
the output Json:
[
{
"id": "Life_Cycle_Rule_For_5_Days",
"filter": {},
"prefix": null,
"status": true,
"expiration": {},
"transitions": []
}
]
Thanks.
Seems like the problem was with the records, it worked after I transformed them to normal classes and added default constructors.
I changed the files S3LifeCycleAnd, S3LifeCycleExpiration, S3LifeCycleFileter, S3LifeCycleRule and S3LifeCycleTag to normal classes and added their constructors, getters and setters normally
S3LifeCycleAnd.java:
#JsonSerialize
public class S3LifeCycleAnd {
private String prefix;
private List<S3LifeCycleTag> tags;
public S3LifeCycleAnd(){}
public S3LifeCycleAnd(String prefix, List<S3LifeCycleTag> tags){
this.tags = tags;
this.prefix = prefix;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public List<S3LifeCycleTag> getTags() {
return tags;
}
public void setTags(List<S3LifeCycleTag> tags) {
this.tags = tags;
}
}
the same for the others.
Thanks.
You can try adding getters to your classes, it would be easy with Lombok #Getter annotation.
If it don't work, then you need to create custom serializers for all classes and change their annotations to #JsonSerialize(using = SomeCustomSerializer.class)
Serializer demo (check paragraph 2.7).
Jackson – Custom Serializer

JSON String to Java object with JSON string key rename [duplicate]

This question already has answers here:
Different names of JSON property during serialization and deserialization
(14 answers)
Closed 1 year ago.
I have the following requirement for JSON string conversion to Java Object.
class Person {
private String firstName;
private String lastName;
}
ObjectMapper MAPPER = new ObjectMapper();
String jsonString = "{\"FST_NME\":\"stack\",\"LST_NME\":\"OVERFLOW\"}";
Person person = MAPPER.readValue(jsonString, Person.class);
The above conversion returns null as the Person class attribute name doesn't match.
With #JsonProperty it converts correctly, but the final JSON result key is the same key as in jsonString.
{
"FST_NME" : "stack",
"LST_NME" : "overflow"
}
but I am looking for something like below.
{
"firstName" : "stack",
"lastName" : "overflow"
}
I tried renaming the key in jsonString and it works as expected.
But can we achieve the above result using any annotations or any other approach?
Thanks.
You just need to add #JsonProperty in both setter and getters.
In your case,
You are reading JSON string key FST_NME, so you need to add #JsonProperty('FST_NME') in the setter method for firstName and as you want to get the final JSON string with key firstName so you need to add #JsonProperty('firstName') in the getter method of firstName.
And same for lastName.
Following is the working code.
package com.ubaid.stackoverflow;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
#Slf4j
public class Saravanan {
#SneakyThrows
public static void main(String[] args) {
ObjectMapper MAPPER = new ObjectMapper();
String jsonString = "{\"FST_NME\":\"stack\",\"LST_NME\":\"OVERFLOW\"}";
Person person = MAPPER.readValue(jsonString, Person.class);
String finalJson = MAPPER.writeValueAsString(person);
log.debug("Final JSON: {}", finalJson);
}
}
class Person {
private String firstName;
private String lastName;
#JsonProperty("firstName")
public String getFirstName() {
return firstName;
}
#JsonProperty("FST_NME")
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#JsonProperty("lastName")
public String getLastName() {
return lastName;
}
#JsonProperty("LST_NME")
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
The output of above code is:
Final JSON: {"firstName":"stack","lastName":"OVERFLOW"}
Add below annotation on gets methods.
#JsonGetter("FST_NME")
public String getFirstName(){
return first Name;
}
Read data to a DTO class (Person DTO) with #JsonProperty
Class Person as you wish
Convert DTO to Person
class PersonDTO {
#JsonProperty(value = "FST_NME")
private String firstName;
#JsonProperty(value = "LST_NME")
private String lastName;
}
class Person {
private String firstName;
private String lastName;
}
ObjectMapper MAPPER = new ObjectMapper();
String jsonString = "{\"FST_NME\":\"stack\",\"LST_NME\":\"OVERFLOW\"}";
PersonDTO persondto = MAPPER.readValue(jsonString, PersonDTO.class);
Person person = new Person();
person.setFirstName(persondto.getFirstName());
person.setLastName(persondto.getLastName());

Map multiiple Java pojo fields to one json field with Jackson annotations

I'm trying to map two fields from Java POJO to one json field;
public class Person {
private String firstName;
//this two fields should be in separate json property (object)
private String street;
private String streetNo;
...
//getters and setters
}
And I want to get response something like this:
{
firstName: "Peter",
address: {
street: "Square nine",
streetNumber: "12"
}
}
You should implement then another POJO Address and add address field to your Person POJO
public class Person {
private String firstName;
private Address address = new Address();
...
//getters and setters
}
// another POJO
public class Address {
private String street;
private String streetNo;
//getters and setters
}

Spring REST: Get JSON data from Request body in addition to entity

I'm working with java project using spring REST.
My problem that i could not extract data from request body (which is json) after receive it as enitiy.
for example:
JSON Request Body
{
"firstname": "Rayan",
"lastname": "Cold",
"company_id": 23
}
My Controller maaped method is:
#PostMapping("/employee")
public Employee createEmployee(#RequestBody Employee employee) {
// Here i need to extract the company id from request body
// Long companyId = *something* // how i can extract from request ?
return companiesRepository.findById(companyId).map(company -> {
employee.setCompany(company);
return employeeRepository.save(employee);
}).orElseThrow(() -> new ResourceNotFoundException("Company not found"));
}
I know i can pass company ID as path variable. But i do want it in request body not in URI.
Thanks
company_id can not be mapped if your Employee class contains companyId.
I guess your company class like:
public class Employee {
private String firstname;
private String lastname;
private Long companyId;
//skip getter setter
}
change it to :
public class Employee {
private String firstname;
private String lastname;
#Transient
#JsonProperty("company_id")
private Long companyId;
//skip getter setter
}
You can simply take the company id from the object received:
employee.getCompany_id();
Please ensure, your Employee class should be something like below:
public class Employee
{
private String company_id;
private String lastname;
private String firstname;
// getter and setters of all these member fields
}
Name of the variables should be same as the one in JSON or use the
appropriate annotations.

How to convert ArrayList of Objects into separate JSON objects

I am writing a JSP webapp and right now I'm kind of stuck. I have been using an ArrayList of Java Objects, each with a list of properties. They are Order objects with properties like firstName, lastName, etc. At this point in my development I need to store the individual orders as JSON arrays in javascript variables. I am able using a servlet to convert the arraylist to JSON but the JSON has repeat properties for each of the Order objects, like this:
[{firstName : Mike, lastName : Daniels, ..... firstName : John, lastName : Doe ... etc ...}]
Clearly, the JSON is made up of all of my Order objects, strung together as one JSON array. What I am wondering is how I need to split up the string into separate variables depending on the number of Orders in the app at the time.
Here's how the object is set if it helps. Thanks!
Object orders = request.getSession().getAttribute("orders");
//this is a string of order properies/values
String json = new Gson().toJson(orders);
HttpSession session = request.getSession();
session.setAttribute("jsonOrders", json);
Try iterating through the array list, creating a JSON object for each of the objects instead of converting the array list as a whole.
Let's suppose your orders entity is something like this:
public class Order {
private String firstName;
private String lastName;
// getters & setters
}
and you get your orders data in request like:
[{
"firstName": "Mike",
"lastName": "Daniels"
}, {
"firstName": "John",
"lastName": "Doe"
}]
You should be able to convert it to List<Order> using
List<Order> orders = gson.fromJson(response, Order.class);
You can then iterate over the list like:
for (Order order: orders) {
// Whatever you want to do
}
Or, if you are using java 8:
orders.forEach (order -> System.out::println);
Here is the solution using Gson library,
//Order.java
public class Order {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
//Orders.java
public class Orders {
Order[] order;
public Order[] getOrder() {
return order;
}
public void setOrder(Order[] order) {
this.order = order;
}
}
import com.google.gson.Gson;
//Solution.java
public class Solution {
public static void main(String[] args) {
String json = "{\"order\":[{\"firstName\":\"Mike\",\"lastName\":\"Daniels\"},{\"firstName\":\"John\",\"lastName\":\"Doe\"}]}";
Orders orders = new Gson().fromJson(json, Orders.class);
Order[] orderArr = orders.getOrder();
for(int i=0; i<orderArr.length;i++){
System.out.println("First Name : "+ orderArr[i].getFirstName());
System.out.println("Last Name "+orderArr[i].getLastName());
}
}
}
**Output**
First Name : Mike
Last Name Daniels
First Name : John
Last Name Doe
Answer by ssc looks right. Best way to do is iterate through the list.

Categories

Resources