How to add ObjectNodes to ArrayNodes without overwriting old data - java

I was trying to call data from Directus API, and return certain data in JSON Format on my localhost.
I'm working on a project where we need to build an API layer.
My code:
public String findAllPersons(HttpServletResponse response, String showFields) throws IOException{
try {
// Call to the database (this part is normally not a problem
String url = "https://cuxhsjf3.directus.app/items/blog";
PersonRoot personRoot = template.getForObject(url, PersonRoot.class);
// I used the Objectmapper, since I'm going from normal data to Json.
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode personsObject = objectMapper.createObjectNode();
ArrayNode persons = objectMapper.createArrayNode();
// The array is going to loop through all the data objects from the database
for (int i = 0; i < personRoot.data.toArray().length; i++) {
// I put person objects into an array, and I return this array.
personsObject.put("id", personRoot.data.get(i).id);
personsObject.put("firstName", personRoot.data.get(i).firstName);
persons.add(personsObject);
System.out.println(persons);
}
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(persons);
return json;
}
// The 'catch' was omitted as not related to the issue
Expected result:
{
"id": "050469ed-0501-4506-9951-794b41bf7e7f",
"firstName": "Elias"
},
{
"id": "0bfb52f7-3656-4202-8c24-2b63eaeca6a9",
"firstName": "Mathias"
},
{
"id": "3145fb95-afd7-4bc4-a62e-a8622b301db2",
"firstName": "Brent"
},
{
"id": "5b93c9b1-4bd1-4aa5-a5ca-d46e849cc58f",
"firstName": "Jef "
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
}
}
The output I'm getting:
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
}
}

As #markspace has pointed out in the comments, you're repeatedly adding the reference to the same ObjectNode into the same JsonArray. When you're modifying this single object, all the elements in the get updated, and what you're getting is data of the very last person replicated multiple time.
You need to move creation of the ObjectNode into the loop, so that a new node would be instantiated for each person:
for (int i = 0; i < personRoot.data.toArray().length; i++) {
ObjectNode personsObject = objectMapper.createObjectNode(); // move object-creation inside the loop
personsObject.put(...);
personsObject.put(...);
persons.add(personsObject);
}
Sidenotes
Encapsulation in your custom class PersonRoot is broken. In Java, we use access modifier like protected and private to hide and protect the data within a class instance. And class is supposed to provide behavior which allow interacting with the state of its instances instead of exposing the instance variables directly (getters are the simplest example of such behavior). This might look alien to you, if previously you were working with languages like JavaScript, but Encapsulation is one of the crucial parts of Object-oriented programming, and it's a common practice in Java to design classes in such a way so that their data is properly encapsulated.
You might also want to learn about one of the GRASP design-patterns, namely The information expert pattern. Which states that an object containing the data should be provided with behavior that facilitates interaction with the data (it's not the responsibility of the code that make use of this object).
I.e. instead of doing this: personRoot.data.toArray().length you can introduce length() method in the PersonRoot class. And instead of writing personRoot.data.get(i) there could be a method like PersonRoot.getPerson(int index).
You can also make PersonRoot implement Iterable interface, so that it could be used as a source of data within an enhanced for-loop.

Related

Access json nested element directly in java

I have json string i want to direclty access element like childMap4.childMap3.ch3Name
and my json string is i am not able find proper solutions.
{
"is working": true,
"childMap4": {
"childMap3": {
"childMap2": {
"childMap1": {
"ch1Name": "childMap1"
},
"ch2Name": "childMap2"
},
"ch3Name": "childMap3 testing"
},
"ch4Name": "childMap4"
},
"prName": "parentMap",
"salary": 800.23,
"prName2": "parentMap field 2",
"age": 24
}
If you just want to extract the value, you should take a look at JsonPath.
Just use the method JsonPath.read([json_object], [expression]) with expression $.childMap4.childMap3.ch3Name.
Another approach would be to create proper mapping class and deserialize JSON string with a library like Jackson, but if you want just single field value taht would be an overkill.
JsonNode jsonNode = mapper.convertValue(Obj, JsonNode.class);
jsonNode.path("childMap4").path("childMap3").path("ch3Name").toString();

Check whether json attribute is a list or an object in java

I want to convert JSON returned from a service into a java object class. However, I get two types in the same JSON attribute and with that I get the following error Expected begin array but it was begin object. How to test before parsing JSON for my java class? Example of a JSON below:
{
"name": "ROMEU",
"age": "24",
"phone": "xx xxxx xxxx",
"family": [
{
"kinship": "brother",
"age": "20"
},
{
"kinship": "sister",
"age": "25"
}
]
}
{
"name": "ROMEU",
"age": "24",
"phone": "xx xxxx xxxx",
"family": {
"kinship": "mother",
"age": "20"
}
}
Assuming you are using Jackson, you can use ACCEPT_SINGLE_VALUE_AS_ARRAY
final ObjectMapper mapper = new ObjectMapper()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
If using GSON, something like
JSONObject response=new JSONObject(res);
if(res.get("family") instanceOf JSONObject) {
// code for JSONObject
} else if(res.get("family") instanceOf JSONArray) {
// code for JSONOArray
}
The correct way to fix this is to change the service that returns such a JSON. It makes no sense to either return a List or a single object for the attribute family. This is complexifying what should be simple.
Why not returning always a List, that may happen to include a single object?
If this is not possible, then you will need all sorts of Jackson deserialization features to make this work. And why? Simply because the service that you are consuming is badly designed.

Check for extra fields

I'm using Jersey with javax.ws.rs.*.
I have the following function:
#PUT
public R modify(#PathParam("id") int id, R in) throws Exception {
...
}
Where R (Resource) is a JSON object containing the fields to be updated. Example:
{
"name": "Building",
"address": "Street number",
"address2": "Lower floor"
}
The update process is working, but as an extra measure for our website developers which are using this API I want the method to throw an exception when a field is sent which isn't allowed. Example:
{
"name": "Qcast HQ",
"illegalField" : "someValue"
}
Because the input gets treated as a Resource object I am not sure whether the method can check for illegal fields. Maybe there is an annotation or something I can use to check for this?

Iterate though array of object in java

I am trying to loop over the array of objects in java. I'm posting this value from client side to server side which is java.
"userList": [{
"id": "id1",
"name": "name1"
},
{
"id": "id2",
"name": "name2"
}]
Now I want to get the value of each id and name. I tried the code below:
for (Object temp : userList)
System.out.print(temp);
System.out.print(temp.getId());
}
But the output I get is:[object Object]
I'm sorry for this stupid question. But how will I get the value of id and name?
You're getting [object Object] because you didn't turn your JavaScript object into JSON on the client side before sending it to your server--you need to use something like JSON.stringify(object) in the browser.
Next, you will need to unpack your JSON into some sort of Java structure. The preferable way to do this is to let an existing tool such as Jackson or Gson map it onto a Java object that looks like:
class User {
String id;
String name;
}
How to do this will depend on your framework, but Spring MVC (for example) supports it mostly automatically.
Implement the toString method for your class according to how you want the printed output to look.
For example...
public class User {
private String id;
private String name;
// Constructors, field accessors/mutators, etc...
#Override
public String toString() {
return String.format("User {id: %s, name: %s}", this.id, this.name);
}
}
Your question does not have complete information. You certainly are skipping steps.
Before you start using the object in java you need to cast the object.
ArrayList<User> convertedUserList = (ArrayList<User>)userList;
for (User temp : convertedUserList)
System.out.print(temp);
System.out.print(temp.getId());
}

Is it possible to change order of attributes when toJson()?

Let's take this example. I have a pojo class as below.
public class MyRecord{
private String name;
private String id;
//constructors and getters,setters
}
when I get the toJson(new MyRecord("MyName","myId") output for above I can get.
{
"name": "MyName",
"id": "123"
}
And I have inherited one as follows to add the dateTime.
public class MyRecordWithDateTime extends MyRecord{
private String DateTime;
//constructors and getters,setters
}
so when I called toJson(new MyRecordWithDateTime("2016-01-01", "MyName", "myId"))
The output is this
{
"name": "MyName",
"id": "123",
"dateTime": "2016-01-01"
}
but I actually need that as follows. (dateTime should come first.)
{
"dateTime": "2016-01-01",
"name": "MyName",
"id": "123"
}
Is there anyway to do that with keeping inheritance?
Maybe kinda late but just in case there is this annotation #JsonPropertyOrder
Field/member/attributes in a JSON collection do not have an order, and as far as this JSON data structure is concerned, the "order" doesn't matter.
The only reason I can imagine you are concerned with the order is for printing/presentation purposes. In that case I suggest you manually construct the JSON string yourself.
Before you put it in your JSON file, you can try to make an ordered List ( LinkedList, ArrayList or something like), then sort it as you want and after that put it in JSON.
But obviously, a better idea than mine exists!!
But the fact is: JSON doesn't need to be sorted! You just use the getter and it will find your value associated to the key! Even if it's in the last position.

Categories