spring boot json to object mapper with complicated json - json

I have this list.json that I need to read to mapper object,
{
"name":"first",
"identity":"gold",
"code":{
"csharp":{
"input":"sample of csharp code",
"value":[
{
"main":"true",
"power":"low"
},
{
"main":"false",
"power":"low"
}
],
"description":"description of csharp code",
"manager":"bill gates"
},
"java":{
"input":"sample of java",
"value":[
{
"main":"true",
"power":"low"
},
{
"main":"false",
"power":"high"
},
{
"main":"true",
"power":"low"
}
],
"description":"description of java",
"manager":"steve job"
}
}
},
{
"name":"second",
"identity":"diamond",
"code":{
"python":{
"input":"sample of python code",
"new":"make it more complicated with new parm not value", // do not forget this
"description":"description of python code",
"manager":"john doe"
},
"csharp":{
"input":"sample of csharp code",
"value":[
{
"main":"true",
"power":"low"
},
{
"main":"false",
"power":"low"
}
],
"description":"description of csharp code",
"manager":"bill gates"
},
}
I omit the long list, I only put two base or outter array, but basically its about 200 or more records.
The List.class,
#Data
#AllArgsConstructor
#Entity
public class List {
private String name;
private String identity;
#OneToOne(cascade = CascadeType.ALL)
private Code[] code;
public List() {}
}
Is the Code[] correct and also onetoone or onetomany?
The Code.class,
#Data
#AllArgsConstructor
#Entity
public class Code {
<<I have no idea what to put here>>
}
Do I need to put any string variable for csharp, java, pyhton? They key should be the same as the variable in the class? But how do you do that since it's not constant?
There's a dynamic 2-layer json here in baeldung but how do I do that in the 3-layer?

Here's I got, you have to use JsonNode for the rest of the layers.
I didn't use this annotation for now, don't want to struggle for now, just add getter/setter and constructor using fields, maybe something to do with java 8,
#Data
#AllArgsConstructor
#Entity
#OneToOne(cascade = CascadeType.ALL)
So I remove it. Also how I did it, you have to simulate one by one in the json, meaning I have to add name and identity since those two are similar, if it works, then I add the code as JsonNode.
public class List {
private String name;
private String identity;
JsonNode code;
public List() {}
// put getter/setter
// put constractors as fields
}
Then on your controller,
private String strJson = null;
#PostConstruct
private void loadData() {
ClassPathResource classPathResource = new ClassPathResource("json/list.json");
try {
byte[] binaryData = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
strJson = new String(binaryData, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
ObjectMapper objectMapper = new ObjectMapper();
DataModel datam = null;
try {
datam = objectMapper.readValue(strJson, List.class);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(datam.code()[0].get("csharp").get("value").get("main"); // output = "true"
Thanks to Baeldung for the idea.

Related

Jackson JsonMappingException: Infinite recursion (StackOverflowError)

I have a problem converting the object into its equivalent JSON.
Following is my class structure:
public class Record {
private byte[] header;
private String mti;
private String bitmap;
private int fieldNumber;
private String data;
private String name;
private String recordType;
private List<Record> subRecords;
private Field recordSchema;
private List<PDSRecord> pdsRecords;
}
In my case, a record can have multiple sub-records and then each sub-record can further have multiple sub-records. Therefore, I came up with this schema to store the records.
The problem I'm facing is due to the Circular Reference of List<Record> inside Record class.
Is there anyway Jackson could convert this object? Also, I would need the complete information of all the sub-records.
Thanks in advance
I was able to solve it. For this, I had to generate a unique Id for every object that is created and mark the class with:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
So, the complete class looks like this:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonInclude(JsonInclude.Include.NON_NULL)
#Data // Lombok
public class Record {
private String id;
private byte[] header;
private String mti;
private String bitmap;
private int fieldNumber;
private String data;
private String name;
private String recordType;
#ToString.Exclude // Lombok
private List<Record> subRecords;
private Field recordSchema;
private List<PDSRecord> pdsRecords;
public Record()
{
this.id = UUID.randomUUID().toString();
}
}
Hope it helps.
You can try the below code. I hope this solves your problem.
try{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
String value = mapper.writeValueAsString(r3);
System.out.println(value);
}catch(IOException a){
a.printStackTrace();
}
Output:{
"header": "UEFOS0FK",
"mti": "Data",
"bitmap": "Name",
"fieldNumber": 5,
"data": "data",
"name": "name",
"recordType": "Data",
"subRecords": [
{
"header": "UEFOS0FK",
"mti": "Data",
"bitmap": "Name",
"fieldNumber": 5,
"data": "data",
"name": "name",
"recordType": "Data",
"subRecords": [
{
"header": "UEFOS0FK",
"mti": "Data",
"bitmap": "Name",
"fieldNumber": 5,
"data": "data",
"name": "name",
"recordType": "Data",
"subRecords": null,
"recordSchema": "Record schema",
"pdsRecords": []
}
],
"recordSchema": "Record schema",
"pdsRecords": []
}
],
"recordSchema": "Record schema",
"pdsRecords": []
}

Deserialize this Json Object using Jackson?

I have been trying to deserialize the data received from this API:
{
"result": "success",
"timestamp": 1521038012878,
"data": {
"GB": 14,
"DE": 2,
"US": 2
},
"totalIsPublic": true,
"advanced": false,
"totalDownloads": {
"GB": 14,
"DE": 2,
"US": 2
}
}
Here is the POJO class:
public class BintrayDownloadCounts {
private List<Integer> totalDownloads = new ArrayList<>();
#JsonProperty("totalDownloads")
public List<Integer> getTotalDownloads() {
return totalDownloads;
}
public void setTotalDownloads(List<Integer> totalDownloads) {
this.totalDownloads = totalDownloads;
}
}
When I tried deserializing using :
downloadCounts = mapper.readValue(json, BintrayDownloadCounts.class);
I get this error:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.Integer out of START_OBJECT token
I have seen many questions containing this error but I am unable to figure out a solution for this particular use case. It may be noted that the totalDownloads object is dynamic i.e. its contents are not constant.
The totalDownloads property is not an array, so it cannot be mapped to a List<Integer>. Use Map<String, Integer> instead and ensure that you tell Jackson to ignore the properties that are not mapped to avoid mapping errors:
#JsonIgnoreProperties(ignoreUnknown = true)
public class BintrayDownloadCounts {
#JsonProperty("totalDownloads")
private Map<String, Integer> totalDownloads;
public Map<String, Integer> getTotalDownloads() {
return totalDownloads;
}
public void setTotalDownloads(Map<String, Integer> totalDownloads) {
this.totalDownloads = totalDownloads;
}
}
Then you are good to go:
ObjectMapper mapper = new ObjectMapper();
BintrayDownloadCounts downloadCounts = mapper.readValue(json, BintrayDownloadCounts.class);

JsonArray and Gson

The JSON library I'm using is Gson. I'm having difficulty formulating a functioning data type to represent the following JSON string:
{
"latestoffers": [
{
"id": "4qXleunwNMCKi8M0q0CuMa",
"price": "534.99",
"firstrecorded_at": 1377808800,
"lastrecorded_at": 1382862800,
"seller": "Newegg",
"availability": "In stock. [BBX: Buy Box]",
"currency": "USD"
},
{
"id": "4xTIQAPySG68IS0CGyOuyO",
"price": "582.41",
"firstrecorded_at": 1380725000,
"lastrecorded_at": 1382862800,
"seller": "Beach Audio",
"currency": "USD"
},
{
"id": "5nW67R2V4CwmE8cwaWsawe",
"price": "578.04",
"firstrecorded_at": 1379524200,
"lastrecorded_at": 1379998900,
"seller": "Beach Audio",
"currency": "USD"
}
],
"offers_count": 6,
"name": "newegg.com",
"recentoffers_count": 2,
"sku": "N82E16834216463",
"url": "http://www.newegg.com/Product/Product.aspx?Item=N82E16834216463"
}
My data class (so far) is as follows, it's the method getOfferData() that I don't how to complete. I'm also not certain whether JsonArray is the appropriate JSON element to be using?
static class LatestOffers {
Integer offers_count;
String name;
Integer recentoffers_count;
String sku;
String url;
java.util.List<JsonArray> getOfferData() {
List<JsonArray> list = new ArrayList<JsonArray>();
// how do I get parse the 'id', 'price', 'firstrecorded_at' etc. to add them to the ArrayList?
return list;
}
Integer getOffers_count() {
return offers_count;
}
String getName() {
return name;
}
Integer getRecentoffers_count() {
return recentoffers_count;
}
String getSku() {
return sku;
}
String getUrl() {
return url;
}
}
Any assistance, please? Thank you.
EDIT
Turns out I was unnecessarily complicating things, this works as intended:
static class LatestOffers {
List<Offer> latestoffers;
List<Offer> getOffer() {
return latestoffers;
}
}
static class Offer {
private String id;
private String price;
private long firstrecorded_at;
private long lastrecorded_at;
private String seller;
private String availability;
private String currency;
String getId() {
return id;
}
String getPrice() {
return price;
}
long getFirstrecorded_at() {
return firstrecorded_at;
}
long getLastrecorded_at() {
return lastrecorded_at;
}
String getSeller() {
return seller;
}
String getAvailability() {
return availability;
}
String getCurrency() {
return currency;
}
}
Thank you to all who answered, I'm accepting the answer of user2762451 as (s)he was the first to suggest the use of another class for the Offer data.
I'd advise you to make new POJO for object in array.
class Offer {
private String id;
private String price;
private long firstRecordedAt;
private long lastRecordedAt;
private String seller;
private String availability;
private String currency;
}
And your LatestOffers class can have a List<Offer> offers; and the method getOfferData() should return List<Offer>.
Basically, the following:
static class LatestOfferDetail {
private int offersCount;
private String name;
private int recentOffersCount;
private String sku;
private String url;
private List<Offer> latestOffers = new ArrayList<Offer>();
//other getters and setters
public List<Offer> getLatestOffers() {
return latestOffers;
}
}
Also, you seem to be following multiple naming conventions in the same piece of code and JSON. With JAVA, it's advisable to follow CamelCaseNaming. I've updated answer to reflect those.
Also, your getter method for latestOffers has a name different from convention. It's advisable to name it like get{FieldName}. I've updated answer to reflect that.
Create another POJO contain all parameter use in array like below :
public class MyOffer
{
private String id;
private double price;
private long firstrecorded_at;
private long lastrecorded_at;
private String seller;
private String availability;
private String currency;
//your getter and setter methods here.
}
Include List of above pojo in your class LatestOffers :
List<MyOffer> latestoffers = new ArrayList<MyOffer>();
your class LatestOffers look like this
static class LatestOffers
{
Integer offers_count;
String name;
Integer recentoffers_count;
String sku;
String url;
List<MyOffer> latestoffers = new ArrayList<MyOffer>();
//getter and setter method
}
Main Class for Test:
public static void main(String[] args) {
LatestOffers lso = new LatestOffers();
lso.setName("N82E16834216463");
lso.setOffers_count(6);
lso.setRecentoffers_count(2);
lso.setSku("N82E16834216463");
lso.setUrl("http://www.newegg.com/Product/Product.aspx?Item=N82E16834216463");
MyOffer offer = null;
List<MyOffer> list = new ArrayList<MyOffer>();
for(int i=0;i<2;i++){
offer = new MyOffer();
offer.setAvailability("In stock. [BBX: Buy Box]");
offer.setCurrency("USD");
offer.setFirstrecorded_at(1377808800);
offer.setId("4qXleunwNMCKi8M0q0CuMa");
offer.setLastrecorded_at(1382862800);
offer.setPrice(534.99);
offer.setSeller("Newegg");
list.add(offer);
}
lso.setLatestoffers(list);
Gson gson = new Gson();
String json = gson.toJson(lso);
System.out.println(json);
}
Output :
{
"offers_count": 6,
"name": "N82E16834216463",
"recentoffers_count": 2,
"sku": "N82E16834216463",
"url": "http://www.newegg.com/Product/Product.aspx?Item=N82E16834216463",
"latestoffers": [
{
"id": "4qXleunwNMCKi8M0q0CuMa",
"price": 534.99,
"firstrecorded_at": 1377808800,
"lastrecorded_at": 1382862800,
"seller": "Newegg",
"availability": "In stock. [BBX: Buy Box]",
"currency": "USD"
},
{
"id": "4qXleunwNMCKi8M0q0CuMa",
"price": 534.99,
"firstrecorded_at": 1377808800,
"lastrecorded_at": 1382862800,
"seller": "Newegg",
"availability": "In stock. [BBX: Buy Box]",
"currency": "USD"
}
]
}
no need to define getOfferData() method just create list of MyOffer class and set that list into latestoffers list define in your class LatestOffers. It will serialize your list into JsonArray when you convert your POJO into JSON String.

Jax-RS API POST and GET methods (RESTful Services)

I have a project (homework) about JAX-RS. I'm working with NetBeans, Jersey and Tomcat. In the post method for example:
'{"user":{"username":"accavdar", "gender":"M", "birthDate":"06.11.1982"}}'
when such a request comes I have to parse this input and add new user to my system. The sample response must be:
{
"meta": {
"code": 200
},
"data": {
"message": "You successfully created a user."
}
}
Expectable error can be like that:
{
"meta": {
"code": 101,
"type": "FieldError",
"errors": [
{
"fieldName": "fullname",
"rejectedValue": null
}
]
}
}
Another problem is that: With using Get method the develepor can want to list all user in the system. The response must be like that :
{
"meta": {
"code": 200
},
"data": [
{
"id": 1,
"username": "sample",
"fullname": "sample",
"gender": "M",
"birthDate": "12.02.1990"
},
{
"id": 2,
"username": "sample",
"fullname": "sample",
"gender": "M",
"birthDate": "21.09.1940"
}
]
}
I want to keep users in a text file, there is no limitation about the way of keeping users.(You can keep in database or in memory) But I have no idea about how to handle request input and generate a response like that. I don't want you to do my homework but could anyone give tips about my problem, please?
NOTE: We will work only with JSON "Content-Type: application/json" "Accept: application/json"
EDİT: #Bogdan , Thank you very much for your answer. I searched the web sites you gave. I want to learn that how is that output produced?:
{
"meta": {
"code": 200
},
"data": {
"message": "You successfully created a user."
}
}
or
{
"meta": {
"code": 200
},
"data": {
"id": 1,
"username": "jack",
"fullname": "jack",
"gender": "M",
"birthDate": "12.12.2012"
}
}
I have "meta" and "user" classes.
#XmlRootElement(name="data")
public class User {
#XmlElement
public int id ;
#XmlElement
public String username;
#XmlElement
public String fullname;
#XmlElement
public String gender;
#XmlElement
public String birthDate;
public User(){
}
#XmlRootElement(name="meta")
public class Meta {
#XmlElement
int code=200;
public Meta(){
}
Also I have this jaxbcontextresolver class
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext>{
private JAXBContext context;
private Class[] types = {User.class, Meta.class};
public JAXBContextResolver() throws Exception {
this.context =
new JSONJAXBContext( JSONConfiguration.mapped().nonStrings("id").nonStrings("code").build(), types);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
for (Class type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
but how to create this response constructs, could any help me?
Your application works with users. This is the resource that you application deals with and your client interacts with for creating, updating, deleting and getting (basically CRUD).
But a user is an abstract resource so your server and client interact with each other by using representations of this resource. The representations can be in JSON format (as in your example), XML etc. Your client specifies what type of representation it wants and the server specifies the representation type it returns by means of the Content-Type. The body of the request/response matches the content type.
This is a Java application so in your application code the users are represented as Java objects. You need to transform the request/response body into objects with getters and setters. You can use JAXB to do the XML transformation, and with a library like Jackson you can transform JSON. You can off course do it manually and parse strings into objects and output strings from objects but that would be more work.
A client submits JSON and after transformation you will have your Java objects that you can handle. You can keep them in memory inside an application scope map or write them inside a file or database and change their representation once again.
Your application will bind URLs to specific actions that transform the request representation into objects, perform operations on the objects then return them to be transformed again into the representation the client expects.
The above are just basic explanations. All your questions can be answered if you follow some JAX-RS tutorials (e.g. a quick search returns REST with Java (JAX-RS) using Jersey - Tutorial or REST: CRUD with JAX-RS (Jersey). I'm sure there are lots other). Your question is too open ended so just dig in and return with specific questions on stackoverflow when you hit road blocks.
EDIT : seems you are struggling with this a little so I'll add a basic service to get you started, let's say for the list of users.
You didn't mention so far nothing about your service class. That's the important one, something like:
package com.test;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/api")
public class Test {
#GET
#Path("/users")
#Produces({ MediaType.APPLICATION_JSON })
public UsersResponseWrapper getUsers() {
List<User> users = new ArrayList<User>();
User u1 = new User();
u1.setId(1);
u1.setFullname("Joe Doe");
u1.setGender("M");
u1.setUsername("joe.doe");
u1.setBirthDate("1919-12-12");
User u2 = new User();
u2.setId(1);
u2.setFullname("John Smith");
u2.setGender("M");
u2.setUsername("john.smith");
u2.setBirthDate("1990-01-01");
users.add(u1);
users.add(u2);
UsersResponseWrapper resp = new UsersResponseWrapper();
resp.setMeta(new Meta(200));
resp.setData(users);
return resp;
}
}
Then your user and meta classes:
package com.test;
public class Meta {
private int code;
public Meta(int code) {
this.code = code;
}
public Meta() {
this.code = 200;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
package com.test;
public class User {
private int id;
private String username;
private String fullname;
private String gender;
private String birthDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthDate() {
return birthDate;
}
public void setBirthDate(String birthDate) {
this.birthDate = birthDate;
}
}
A JAXB provider:
package com.test;
import java.util.ArrayList;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private static Class<?>[] types = {UsersResponseWrapper.class, User.class, Meta.class, ArrayList.class};
public JAXBContextResolver() throws Exception {
this.context = JAXBContext.newInstance(types);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
for (Class<?> type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
Something from web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>RestService</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.test</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>RestService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
And a wrapper for your result:
package com.test;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class UsersResponseWrapper {
private Meta meta;
private List<User> data;
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public List<User> getData() {
return data;
}
public void setData(List<User> data) {
this.data = data;
}
}
I think this last class is what put you in difficulty since your result is formed of both meta content and data content. Remember you need to return objects (the default Jackson mapper from Jersey distribution will then take care of it) and it happens you have a complex one. The above should return this (formatting not included):
{
"data": [
{
"birthDate": "1919-12-12",
"fullname": "Joe Doe",
"gender": "M",
"id": "1",
"username": "joe.doe"
},
{
"birthDate": "1990-01-01",
"fullname": "John Smith",
"gender": "M",
"id": "1",
"username": "john.smith"
}
],
"meta": {
"code": "200"
}
}
That's about as much I can add to this as details. It's your homework after all :). You are doing fine, just keep going.

Create JsonObject from #ResponseBody

#RequestMapping(value = "/dropDown", method = RequestMethod.GET)
public #ResponseBody
DropDown getList(Map<String, Object> map, HttpServletRequest request,
HttpServletResponse response) {
DropDown dropDown = new DropDown();
List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();
List<MapTable2> list = contactService.mapProcess();
for (MapTable2 table : list) {
Map<String, Object> dataRow = new HashMap<String, Object>(1);
dataRow.put("text", table.getProcess());
dataRow.put("value", table.getId());
dataRow.put("selected", false);
dataRow.put("description", table.getProcess());
dataRow.put("imageSrc", "image.jpg");
rows.add(dataRow);
}
dropDown.setRows(rows);
return dropDown;
}
I need to create following one
var ddData = [
{
text: "Facebook",
value: 1,
selected: false,
description: "Description with Facebook",
imageSrc: "http://dl.dropbox.com/u/40036711/Images/facebook-icon-32.png"
},
{
text: "Twitter",
value: 2,
selected: false,
description: "Description with Twitter",
imageSrc: "http://dl.dropbox.com/u/40036711/Images/twitter-icon-32.png"
}]
I know the issue with my above java coding , I'm not aware to create json array like above.
please check it and help me to correct it.
MapTable2 has ProcessId & ProcessName
public class MapTable2 {
private int id;
private String process;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProcess() {
return process;
}
public void setProcess(String process) {
this.process = process;
}
}
#theon is right.
Since you are using #Responsebody you can let Spring do the JSON conversion for you. Create a class that matches the objects in the JSON array:
public class SomeObject {
public String getText() { //... }
public int getValue() { //... }
public boolean getSelected { // ... }
public String getDescription { // ... }
public String getImageSrc { // ... }
}
Populate the objects and return it as a list from your controller:
#RequestMapping(value = "/dropDown", method = RequestMethod.GET)
#ResponseBody
public List<SomeObject> getList(Map<String, Object> map, HttpServletRequest request, HttpServletResponse response) {
// Get the objects, return them in a list
}
Add the <mvc:annotation-driven /> or the #EnableWebMvc to your application config unless you have not already done so. Make sure that Jackson is available on your classpath and then Spring will automatically serialize your objects to JSON (if the request has Content-Type: application/json. Alternatively, the produces attribute can be added to the #RequestMapping annotation to always return JSON).
Well Use this library. It is very light weight (16KB) and does exactly what you need.
So in your case, you will be using JSONObject which internally extends HashMap
and do
JSONObject o = new JSONObject();
o.put("text","whatever text");
o.put("value",1);
o.put("selected",false);
//and so on
JSONArray arr = new JSONArray();
arr.add(o);
The above will give you this:
[
{
text: "Facebook",
value: 1,
selected: false,
description: "Description with Facebook",
imageSrc: "http://dl.dropbox.com/u/40036711/Images/facebook-icon-32.png"
}
]
To add more objects, add more JSONObjects in a loop to JSONArray
So as per your given code, just replace dataRow with 'JSONObject' and rows with JSONArray and thats it. In the end, to retrieve the string, do rows.toString().