mapper.readValue not honoring JSON Views - json

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.

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.

JSON don't repeat some attributes and respective values

for all instances serialized, from the second occurrence onwards, of the same model class, the objects only have a part of the attributes and their respective values ​​that they should. This way the JSON file structure is not homogeneous and uniform as expected by the old part of the application (Frontend).
[
{
"id": 1,
"fornecedor": {
"cnpj": "80000876000177",
"nome": "ATACADÃO DIA-A-DIA"
},
"itens": [
{
"id": 2,
"produto": {
"gtin": "7891991010856",
"nome": "CERVEJA BUDWEISER"
},
"quantidade": 3,
"preco": 3.5,
"total": 10.5
}
]
},
{
"id": 3,
"fornecedor": {
"cnpj": "19600347000101",
"nome": "SUPERMERCADO BELLA-VIA"
},
"itens": [
{
"id": 4,
"produto": {
"gtin": "7891991010857",
"nome": "CERVEJA HEINEKEN"
},
"quantidade": 4,
"preco": 5.4,
"total": 21.6
}
]
},
{
"id": 5,
"fornecedor": "19600347000101",
"itens": [
{
"id": 6,
"produto": "7891991010856",
"quantidade": 4,
"preco": 3.2,
"total": 12.8
}
]
},
{
"id": 7,
"fornecedor": "80000876000177",
"itens": [
{
"id": 8,
"produto": "7891991010857",
"quantidade": 5,
"preco": 4.9,
"total": 24.5
}
]
}
]
In the JSON above, the instances with id:1 and id:7 have a field named fornecedor that is structurally different and that shouldn't be because the Frontend expects them to be the same. Other samples are id:2 and id:6 with the field produto and so on.
The JSON above is the serialization of the list of model class Pedido below:
#Data
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Pedido {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(View.External.class)
private Long id;
#ManyToOne
#JoinColumn(name = "fornecedor_cnpj")
#JsonView(View.External.class)
private Fornecedor fornecedor;
#OneToMany(mappedBy = "pedido", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JsonView(View.External.class)
private List<Item> itens;
public List<Item> getItens() {
if(itens == null){
itens = new ArrayList<>();
}
return itens;
}
}
The model class Fornecedor is:
#Data
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "cnpj")
public class Fornecedor {
#Id
#JsonView(View.External.class)
private String cnpj;
#JsonView(View.External.class)
private String nome;
#OneToMany(mappedBy = "fornecedor", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JsonView(View.Internal.class)
private List<Pedido> pedidos;
#OneToMany(mappedBy = "fornecedor", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JsonView(View.Internal.class)
private List<Disponibilidade> disponilidades;
}
And the model class Produto is:
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "gtin")
#NoArgsConstructor
#Data
public class Produto {
#Id
#JsonView(View.External.class)
private String gtin;
#JsonView(View.External.class)
private String nome;
#OneToMany(mappedBy = "produto", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JsonView(View.Internal.class)
private List<Disponibilidade> disponibilidades;
public Produto(String gtin) {
this.gtin = gtin;
}
}
Question: The structure of JSON is the representation of expected behavior? If so, could someone tell me how do I make all instances from the first one to always have all attributes with their respective values ​​(as much as that means repeated attributes and values!).
Thanks!
The JSON you are getting is a consequence of the usage of #JsonIdentityInfo that you or someone else may have added to solve an infinite recursion while serializing your Java objects to JSON (you can read more about this at https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion). This means that the only way to have the complete set of attributes in fornecedor and produto is to get rid of #JsonIdentityInfo on those classes. Keep in mind that this might create the infinite recursion problem mentioned above.
#Data
#Entity
public class Fornecedor {
(...)
}
#Entity
#NoArgsConstructor
#Data
public class Produto {
(...)
}

Saving a list into a json with DateTime as a Header

i don't know if I'm explaining it right but what i want is to be able to save 3 lists of strings but have them separated based on time. The idea is to store some strings for each day and save them into a json so i can search for the date and display them into a calendar.
My try was like so
[Serializable]
public class TimeStamp
{
public string CreatedDate;
public List<Entries> day = new List<Entries>();
}
[Serializable]
public class Entries
{
public List<string> names = new List<string>();
public List<string> location = new List<string>();
public List<string> info = new List<string>();
}
which then i saved like so
string json = JsonUtility.ToJson(timeStamp);
try
{
timeStamp.CreatedDate = DateTime.UtcNow.ToLocalTime().ToString("dd-MM-yyyy HH:mm");
File.WriteAllText(Application.persistentDataPath + "/Journal.json", json);
Debug.Log("Data Saved");
}
catch (Exception e)
{
Debug.Log("Error Saving Data:" + e);
throw;
}
Which does save everything i need but its not separated by the date, trying to explain it the way i think of it is that i want the date to be the "header" of the Json so in one Json file i would have all entries for each day and also i made them into a list so i can add or remove strings.
So later on when i want to add something i would search for the date and access the names list for instance and .Add() my string to that list
In a rather crude way this is how i would like my json to look like:
entries
[
"date"[
list1 = []
list2 = []
list3 = []
]
]
The way i feel like it should be is something like this
[Serializable]
public class Roots
{
public TimeStamp timeStamp;
}
[Serializable]
public class TimeStamp
{
public List<Entries> entries;
}
[Serializable]
public class Entries
{
public List<string> names = new List<string>();
public List<string> location = new List<string>();
public List<string> info = new List<string>();
}
which gives a json like this
{
"Entries": {
"Timestamp": [
{
"names": "",
"location": "",
"info": ""
}
]
}
}
but how can i make that Timestamp be a string or a date?
I guess you actually rather want to have a collection class like
[Serializable]
public class Root
{
public List<TimeStamp> timestamps = new List<TimeStamp>();
}
and rather add your individual timestamps to that list e.g.
// Probably you would load this from JSON at start of your app
private Root root = new Root();
...
root.timeStamps.Add(new TimeStamp(...));
And then
string json = JsonnUtility.ToJson(root);
...
So just the way you already have with
[Serializable]
public class TimeStamp
{
public string CreatedDate;
public List<Entries> day = new List<Entries>();
}
[Serializable]
public class Entries
{
public List<string> names = new List<string>();
public List<string> location = new List<string>();
public List<string> info = new List<string>();
}
This should give you a JSON looking like
{
"timestamps" :
[
{
"CreatedDate" : "12-04-2021 12:34",
"day" :
[
{
"names" : ["Guy1", "Dude2", "Jack3"],
"location" : ["Room1", "Place2", "Location3"],
"info" : ["This", "is", "some", "info"]
},
{
"names" : [...],
"location" : [...],
"info" : [...]
},
...
]
},
{
"CreatedDate" : "13-07-2021 14:42",
"day" :
[
{
"names" : [...],
"location" : [...],
"info" : [...]
},
...
]
},
...
]
}
If your goal was rather to actually have something like
{
"timestamps" :
[
{
"12-04-2021 12:34":
{
[
{
"names" : ["Guy1", "Dude2", "Jack3"],
"location" : ["Room1", "Place2", "Location3"],
"info" : ["This", "is", "some", "info"]
},
...
]
}
},
{
"13-07-2021 14:42" :
{
[
{
"names" : [...],
"location" : [...],
"info" : [...]
},
...
]
}
},
...
]
}
then you will need to rather use soemthing like
[Serializable]
public class Root
{
public Dictionary<string, List<Entries>> timestamps = new Dictionary<string, List<Entries>>();
}
[Serializable]
public class Entries
{
public List<string> names = new List<string>();
public List<string> location = new List<string>();
public List<string> info = new List<string>();
}
but this is not supported by the built-in JsonUtility and you would rather need to use e.g. Newtonsoft JSON.NET (which is available as a Unity Package) and see in particular Serializing a Dictionary

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

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.