Jackson JsonMappingException: Infinite recursion (StackOverflowError) - json

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": []
}

Related

spring boot json to object mapper with complicated 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.

Serialize Feign Json Response to object

I've the following Json response coming from a Feign client:
{
"maxResults": 1,
"total": 5,
"isLast": false,
"values": [
{
"id": 37,
"self": "https://your-domain.atlassian.net/rest/agile/1.0/sprint/23",
"state": "active",
"name": "sprint 1",
"goal": "sprint 1 goal"
}
]
}
The feign client:
#FeignClient(name = "jira")
public interface JiraFeignClient {
#GetMapping("/rest/agile/1.0/board/{boardId}/sprint?state=active&maxResults=1")
ActiveSprintResponse getActiveSprint(#PathVariable String boardId);
}
I'd like to define the ActiveSprintResponse class in order to have the information related to the "values" property (I'm only interested in those) of the json response but I don't understand how can I easily represent it.
I would have no problems for the properties "maxResults", "total" etc... but how can easily unpack "values"? I can assume I will always have only one element in the value array.
I've tried defining it like that but it clearly does not work:
public class ActiveSprintResponse {
private final String id;
private final String self;
private final String name;
private final String goal;
public ActiveSprintResponse(String id, String self, String name, String goal) {
this.id = id;
this.self = self;
this.name = name;
this.goal = goal;
}
}
You need to define a class that represents the root JSON object. You can define a property for the values of type List then:
public class ActiveSprintResponseList {
private List<ActiveSprintResponse> values;
// (Other fields omitted for simplicity)
public void setValues(List<ActiveSprintResponse> values) {
this.values = values;
}
public List<ActiveSprintResponse> getValues() {
return values;
}
}
you then need to declare that class as return type:
#FeignClient(name = "jira")
public interface JiraFeignClient {
#GetMapping("/rest/agile/1.0/board/{boardId}/sprint?state=active&maxResults=1")
ActiveSprintResponseList getActiveSprint(#PathVariable String boardId);
}
and use it on the calling side:
ActiveSprintResponseList response = client.getActiveSprint(..);
List<ActiveSprintResponse> values = response.getValues();
// work with values

JSON data type not validated in spring boot

I have json like below
"name": {
"title": "Mr",
"firstName": "somename",
"middleName": "middleName",
"lastName": "Micheal",
"maidenName": "maidenName"
}
My POJO class is defined as like below
#JsonInclude(JsonInclude.Include.NON_NULL)
#Data
#Validated
public class Name {
#Length(min = 0,max = 10)
private String title;
#Length(min = 0,max = 50)
private String firstName;
#Length(min = 0,max = 700)
private String middleName;
#Length(min = 0,max = 50)
private String lastName;
#Length(min = 0,max = 50)
private String maidenName;
}
Controller class
#RestController
#RequestMapping("/price/v1")
#Validated
public class MyServerController implements PriceAPI {
#PostMapping(value = "/price",
produces = {"application/json"},
consumes = {"application/json"})
public ResponseEntity<SomeResponse> getmyResponse
(#Valid
#RequestBody Name name) {
return ResponseEntity.ok(myService.getmyResponse(priceData));
}
}
Issue is when I provide integer to my json first name field it is not throwing any error. example
"name": {
"title": "Mr",
"firstName": 123,
"middleName": "middleName",
"lastName": "Micheal",
"maidenName": "maidenName"
}
I tried few options in jackson and explored few Json annotation. But those are not working as expected. Please help me on this.
You will need to include a pattern for the individual field by defining a regular expression with the #Pattern annotation. In this case, for the firstName field, the appropriate #Pattern should be defined as --
#Length(min = 0, max = 50)
#Pattern(regexp = "^[A-Za-z]+$")
private String firstName;
Include a suitable pattern for all the other fields as required.

mapper.readValue not honoring JSON Views

I'm using JSONView to hide things from being shown to the API. Everything works if I want to have non-human readable JSON. The problem is, I'm also trying to prettify the json to make it look more readable. The second-to-last line in the below method does this:
#RequestMapping(path = "/questions")
public #ResponseBody List<Question> questionListRest() throws IOException {
ObjectMapper mapper = new ObjectMapper();
String result = mapper
.writerWithView(Views.Public.class)
.writeValueAsString((List<Question>) questionRepository.findAll());
List<Question> JsonList = mapper.readValue(result, new TypeReference<List<Question>>(){});
return JsonList;
}
However, it initializes 'answer' as null, even though answer should be hidden from the json completely (and it is hidden from the json string before mapper.readValue is called):
[ {
"questionId" : 6,
"questionName" : "Which of the following would you most likely eat?",
"questionType" : "checkbox",
"values" : [ "A chainsaw", "A table", "An Apple" ],
"answers" : null
}, {
"questionId" : 7,
"questionName" : "What countries have you visited",
"questionType" : "checkbox",
"values" : [ "Finland", "Sweden", "Estonia" ],
"answers" : null
}, {
"questionId" : 8,
"questionName" : "Where did you last feel unconfortable",
"questionType" : "checkbox",
"values" : [ "At a bar", "While coding spring", "While eating an unsliced long sub" ],
"answers" : null
} ]
Here is my Question class:
#Entity
public class Question {
#JsonIgnore
private static AnswerRepository answerRepository;
#JsonIgnore
private static CategoryRepository categoryRepository;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long questionId;
private String questionName;
private String questionType; //text, radio, checkbox..
private String[] values;
public Question(String questionName, String questionType, Category category, String[] values) {
super();
this.questionName = questionName;
this.questionType = questionType;
this.category = category;
this.setValues(values);
}
public Question(String questionName, String questionType, Category category) {
super();
this.questionName = questionName;
this.questionType = questionType;
this.category = category;
}
#ManyToOne(cascade = {CascadeType.MERGE})
#JoinColumn(name = "categoryid")
#JsonBackReference
private Category category;
#JsonView(Views.Internal.class)
public List<Answer> getAnswers() {
return answers;
}
public void setAnswers(List<Answer> answers) {
this.answers = answers;
}
#JsonView(Views.Internal.class)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "question")
#JsonManagedReference
private List<Answer> answers;
public Question() {
super();
}
... getters and setters ...
Here is the json before readValue is called on it (as in when result is logged)
[{"questionId":6,"questionName":"Which of the following would you most likely eat?","questionType":"checkbox","values":[ "A chainsaw", "A table", "An Apple" ]},{"questionId":7,"questionName":"What countries have you visited","questionType":"checkbox","values":[ "Finland", "Sweden", "Estonia" ]},{"questionId":8,"questionName":"Where did you last feel unconfortable","questionType":"checkbox","values":[ "At a bar", "While coding spring", "While eating an unsliced long sub" ]}]
I "fixed" this by adding #JsonInclude(JsonInclude.Include.NON_EMPTY) to the fields in said null or empty entities. NON_EMPTY hides all values, empty or null, NON_NULL only null values. I probably asked about the wrong thing in the first place.

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.