I have recently moved to test API's for a new project with Rest Assured. I am not so fluent in Java, so that is why I need to know how to optimise the code.
Let's say I have an API, which output's JSON in this format -
{
"records":[
0: {
"id" : 1232,
"attribute1": "some_value",
"attribute2": "some_value1"
},
1: {
"id" : 1233,
"attribute1": "some_new_value",
"attribute2": "some_new_value1"
}]}
There are around 400 such objects coming inside the records array. I want to get the id of all the 400 records, and store in an array. I am able to do so, but I think the approach can be optimised.
My current code :
private static Response response;
Response r;
JSONParser parser = new JSONParser();
String resp = response.asString();
JSONObject json = (JSONObject) parser.parse(resp);
JSONArray records= ((JSONArray)json.get("records"));
ArrayList<Long> idlist = new ArrayList<Long>();
for(int i=0;i<records.size();i++) {
idlist.add((Long) ((JSONObject)records.get(i)).get("id"));
}
How can I minimize the lines of code to achieve the same thing?
Response response
// Code that assigns the response
List<Long> idList = response.jsonPath().getList("records.id");
// Code that uses the id list.
Related
I am trying to get a single nested value from a ResponseEntity but I am trying to do so without having to create a pojo for every possible item as this is a third party api response.
Example response.getBody() as it appears in Postman:
{
"message": "2 records found",
"records": [
{
"Account": {
"Id": "1",
"Name": "Foo Inc"
},
"CaseNumber": "200",
"Contact": {
"FirstName": "Foo",
"LastName": "Bar"
},
"Status": "In Progress",
"StatusMessage": "We are working on this."
},
{
"Account": {
"Id": "1",
"Name": "Foo Inc"
},
"CaseNumber": "100",
"Contact": {
"FirstName": "Foo",
"LastName": "Bar"
},
"Status": "Closed"
}
]
}
Basically, if I were in JS, I am looking for:
for(let record of res.body.records){
if(record && record.CaseNumber === "200"){
console.log(record.Status)
}
res.body.records[0].Status
Currently, they are are doing this to check if the response is empty:
ResponseEntity<Object> response = restTemplate.exchange(sfdcURL, HttpMethod.POST, entity, Object.class);
LinkedHashMap<Object, Object> resMap = (LinkedHashMap<Object, Object>) response.getBody();
List<Object> recordsList = (List<Object>) resMap.get("records");
if (recordsList.size() <= 0) { return error }
But I need to get the value of of "Status" and I need to do so without creating a pojo.
I appreciate any guidance on how I can do this in Java
UPDATE
So the response.getBody() is returned and when it is displayed in Postman, it looks like the pretty JSON shown above. However, when I do:
System.out.println(response.getBody().toString())
it looks like:
{message=2 Records Found, records=[{Account={Id=1, Name=Foo Inc}, CaseNumber=200, Contact={FirstName=Foo, LastName=Bar}, //etc
To make it worse, one of the fields appears in the console as follows (including linebreaks):
[...], Status=In Progress, LastEmail=From: noreply#blah.com
Sent: 2022-08-08 10:14:54
To: foo#bar.com
Subject: apropos case #200
Hello Foo,
We are working on your case and stuff
Thank you,
us, StatusMessage=We are working on this., OtherFields=blah, [...]
text.replaceAll("=", ":") would help some, but won't add quotations marks nor would it help separate that email block.
How can I so that the responses here like ObjectMapper and JSONObject can work?
You can either convert the string to valid json (not that trivial) and deserialise into a Map<String, Object>, or just pluck the value out of the raw string using regex:
String statusOfCaseNumber200 = response.getBody().toString()
.replaceAll(".*CaseNumber=200\\b.*?\\bStatus=([^,}]*).*", "$1");
This matches the whole string, captures the desired status value then replaces with the status, effectively "extracting" it.
The regex:
.*CaseNumber=200\b everything up to and including CaseNumber=200 (not matching longer numbers like 2001)
.*? as few chars as possible
\\bStatus= "Status=" without any preceding word chars
([^,}]*) non comma/curly brace characters
.* the rest
It's not bulletproof, but it will probably work for your use case so it doesn't need to be bulletproof.
Some test code:
String body = "{message=2 Records Found, records=[{Account={Id=1, Name=Foo Inc}, CaseNumber=200, Contact={FirstName=Foo, LastName=Bar}, Status=In Progress, StatusMessage=We are working on this.}, {Account={Id=1, Name=Foo Inc}, CaseNumber=100, Contact={FirstName=Foo, LastName=Bar}, Status=Closed}]";
String statusOfCaseNumber200 = body.replaceAll(".*CaseNumber=200\\b.*?\\bStatus=([^,}]*).*", "$1");
System.out.println(statusOfCaseNumber200); // "In Progress"
PLEASE DO NOT use Genson as Hiran showed in his example. The library hasn't been updated since 2019 and has many vulnerable dependencies!
Use Jackson or Gson.
Here how you can serialize a string into a Jackson JsonNode:
ObjectMapper mapper = new ObjectMapper();
String json = ...;
JsonNode node = mapper.readTree(json);
If you want to serialize a JSON object string into a Map:
ObjectMapper mapper = new ObjectMapper();
String json = ...;
Map<String, Object> map = mapper.readValue(json, HashMap.class);
You can read more about JsonNode here and a tutorial here.
You can use JSON-Java library and your code will look like this:
JSONObject jsonObject = new JSONObject(JSON_STRING);
String status = jsonObject.getJSONArray("records")
.getJSONObject(0)
.getString("Status");
System.out.println(status);
Or in a loop
JSONArray jsonArray = new JSONObject(jsonString).getJSONArray("records");
for(int i =0; i < jsonArray.length(); i++) {
String status = jsonArray
.getJSONObject(i)
.getString("Status");
System.out.println(status);
}
So the response.getBody() is returned and when it is displayed in Postman, it looks like the pretty JSON shown above. However, when I do:
...
text.replaceAll("=", ":") would help some, but won't add quotations
marks nor would it help separate that email block.
How can I so that the responses here like ObjectMapper and JSONObject
can work?
Firstly, Jackson is the default message converter which Spring Web uses under the hood to serialize and deserialize JSON. You don't need to introduce any dependencies.
Secondly, the process serialization/deserialization is handled by the framework automatically, so that in many cases you don't need to deal with the ObjectMapper yourself.
To emphasize, I'll repeat: in most of the cases in Spring you don't need to handle raw JSON yourself. And in the body of ResponseEntiry<Object> produced by the method RestTemplate.exchange() you have a LinkedHashMap in the guise of Object, it's not a raw JSON (if you want to know why it is a LinkedHashMap, well because that's how Jackson stores information, and it's a subclass of Object like any other class in Java). And sure, when you're invoking toString() on any implementation of the Map you'll get = between a Key and a Value.
So, the problem you've mentioned in the updated question is artificial.
If you want to deal with a Map instead of an object with properly typed properties and here's how you can do that:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<LinkedHashMap<String, Object>> response = restTemplate.exchange(
sfdcURL, HttpMethod.POST, entity, new ParameterizedTypeReference<>() {}
);
Map<String, Object> resMap = response.getBody();
List<Object> recordsList = (List<Object>) resMap.get("records");
if (recordsList.isEmpty()) { ... }
If there are redundant lines in the Values which you want to trim, then as a remedy you can introduce a custom Jackson-module declaring a Deserializer which would handle leading/trailing white-space and new lines, described in this answer. Deserialize in the module would be applied by default, other options would require creating classes representing domain objects which you for some reasons want to avoid.
As Oliver suggested JsonNode seems to be the best approach. But, if I receive the ResponseEntity<Object>, I still cannot figure out a way to convert it to readable Json (and thus convert it to JsonNode), so I am still open to responses for that part.
I was able to get it to work by changing the ResponseEntity<Object> to ResponseEntity<JsonNode> so this is what I will be submitting for now:
ResponseEntity<JsonNode> response = restTemplate.exchange(sfdcURL,
HttpMethod.POST, entity, JsonNode.class);
JsonNode records = response.getBody().get("records");
String status = null;
String statusMessage = null;
for (JsonNode rec : records) {
if(rec.get("CaseNumber").asText().equals(caseNumber)) {
status = rec.get("Status").asText();
if(rec.has("StatusMessage")) {
statusMessage = rec.get("StatusMessage").asText();
}
} else {
statusMessage = "Invalid CaseNumber";
}
}
Because the overall method returns a ResponseEntity<Object> I then converted my strings to a HashMap and returned that:
HashMap<String, String> resMap = new HashMap<String, String>();
resMap.put("Status", status);
resMap.put("StatusMessage", statusMessage);
return new ResponseEntity<>(resMap, HttpStatus.OK);
This is not a perfect solution, but it works for now. Would still be better for exception handling if I could receive a ResponseEntity<Object> and then convert it to a JsonNode though. Thanks everyone for the responses!
Background
I have a list of strings (records) that are dynamically created by a class. Each record may have different keys (e.g. favorite_pizza on first, favorite_candy on second).
// Note: These records are dynamically created and not stored
// in this way. This is simply for display purposes.
List<String> records =
Arrays.asList(
"{\"name\":\"Bob\",\"age\":40,\"favorite_pizza\":\"Cheese\"}",
"{\"name\":\"Jill\",\"age\":22,\"favorite_candy\":\"Swedish Fish\"}");
The list of records is then passed to a separate HTTP request class.
public Response addRecords(List<String> records) {
...
}
Inside the HTTP request service, I want to build a JSON request body:
{
"records": [
{
"name": "Bob",
"age": 40,
"favorite_pizza": "Cheese"
},
{
"name": "Jill",
"age": 22,
"favorite_candy": "Swedish Fish"
}
]
}
I'm using org.json.JSONObject to add the records key and create the request body:
JSONObject body = new JSONObject();
// Add the "records" key
body.put("records", records);
// Create the request body
body.toString();
Issues
When I run my junit test in IntelliJ, the request body contains a backslash before each quote:
org.junit.ComparisonFailure:
Expected :"{"records":["{"name":"Bob","age":40,"favorite_pizza":"Cheese"}","{"name":"Jill","age":22,"favorite_candy":"Swedish Fish"}"]}"
Actual :"{"records":["{\"name\":\"Bob\",\"age\":40,\"favorite_pizza\":\"Cheese\"}","{\"name\":\"Jill\",\"age\":22,\"favorite_candy\":\"Swedish Fish\"}"]}"
And when I make the request it fails because the body is not formatted correctly:
{
"records": [
"{\"name\":\"Bob\",\"age\":40,\"favorite_pizza\":\"Cheese\"}",
"{\"name\":\"Jill\",\"age\":22,\"favorite_candy\":\"Swedish Fish\"}"
]
}
Questions
Why is JSONObject including the backslashes before each quote?
How do I remove the backslashes?
You are creating a list of string, which is not what you want.
You should instead create a list of objects (Maps)
Map<String, Object> m1 = new LinkedHashMap<>();
m1.put("name", "Bob");
m1.put("age", 40);
m1.put("favorite_pizza", "Cheese");
LinkedHashMap<String, Object> m2 = new LinkedHashMap<>();
m2.put("name", "Jill");
m2.put("age", 22);
m2.put("favorite_candy", "Swedish Fish");
List<LinkedHashMap<String, Object>> records = Arrays.asList(m1,m2);
JSONObject body = new JSONObject();
// Add the "records" key
body.put("records", records);
This is a quite common mistake (it seems), to try to serialize strings formatted like json objects expecting is the same thing as passing a the object itself.
UPDATE:
Or if you have a json serialized object list then ...
List<String> recordSource =
Arrays.asList(
"{\"name\":\"Bob\",\"age\":40,\"favorite_pizza\":\"Cheese\"}",
"{\"name\":\"Jill\",\"age\":22,\"favorite_candy\":\"Swedish Fish\"}");
List<JSONObject> records =
recordSource.stream().map(JSONObject::new).collect(Collectors.toList());
JSONObject body = new JSONObject();
// Add the "records" key
body.put("records", records);
System.out.println(body.toString());
If your record strings are already valid json you can either
Iterate over them, converting them one at a time into a JSONObject (see here) and then add the result to a JSONArray which you can manipulate if needed.
Create the array entirely by hand since it's just comma separated record strings inside square brackets.
This is my C# webservice which generates a JSON String. The below code block is what im using for that.
List<Dictionary<String, Object>> lstdict = new List<Dictionary<String, Object>>();
...
...
...
Logic for connecting db and getting records in msqldat (data reader)
goes here.
...
...
while (msqldat.Read())
{
var detls = new Dictionary<string, object>();
for (int i = 0; i < msqldat.FieldCount; i++)
{
detls.Add(msqldat.GetName(i), msqldat.IsDBNull(i) ? null :
msqldat.GetValue(i));
lstdict.Add(detls);
}
}
JavaScriptSerializer jss = new JavaScriptSerializer();
String mret = jss.Serialize(lstdict);
The above webservice is called in a java code from android studio and it returns the below String.
{"GetDataResult":"[
{\"uname\":\"hkIUZIikXVTC5aNaSva8IQ==\",
\"passwd\":\"hkIUZIikXVTC5aNaSva8IQ==\",
\"validupto\":\"\\\/Date(1545330600000)\\\/\",
\"dept\":\"juubHSHgLr\/3JWnrZCh5LeeW5Q7lioWOZ1\/Tg+YRy\/o=\",
\"rid\":1},
{\"uname\":\"hkIUZIikXVTC5aNaSva8IQ==\",
\"passwd\":\"hkIUZIikXVTC5aNaSva8IQ==\",
\"validupto\":\"\\\/Date(1545330600000)\\\/\",
\"dept\":\"juubHSHgLr\/3JWnrZCh5LeeW5Q7lioWOZ1\/Tg+YRy\/o=\",
\"rid\":2}]"}
I am trying to get the values in android application by using this Java code :
JSONObject uiobj = new JSONObject(mret);
JSONArray arrUserinfo = uiobj.getJSONArray("GetDataResult");
arrUserinfo.getJSONObject(0).getString("uname"))
The code fails at the second line. I'm new to JSON. Not sure if the JSON generated from c# code is not right or java code for parsing is not right. Please advise further. Thank you in advance.
The above json is serialized you need to parse json and then extract the object from it.
See this json valid..
{
"GetDataResult": [{
"uname": "hkIUZIikXVTC5aNaSva8IQ==",
"passwd": "hkIUZIikXVTC5aNaSva8IQ==",
"validupto": "/Date(1545330600000)/",
"dept": "juubHSHgLr/3JWnrZCh5LeeW5Q7lioWOZ1/Tg+YRy/o=",
"rid": 1
},
{
"uname": "hkIUZIikXVTC5aNaSva8IQ==",
"passwd": "hkIUZIikXVTC5aNaSva8IQ==",
"validupto": "/Date(1545330600000)/",
"dept": "juubHSHgLr/3JWnrZCh5LeeW5Q7lioWOZ1/Tg+YRy/o=",
"rid": 2
}
]
}
To parse json in java see below post..
https://www.geeksforgeeks.org/parse-json-java/
I have a large file with many JSON objects similiar to the following. I need to parse everything to get the "bought_together" items as an array using the org.json library. I'm having trouble accessing anything nested in "related".
What is the required code to retrieve "bought_together" as a list?
{
"asin": "11158732",
"title": "Girls Ballet Tutu Zebra Hot Pink",
"price": 3.17,
"imUrl": "http://ecx.images-amazon.com/images/I/51fAmVkTbyL._SY300_.jpg",
"related":
{
"also_bought": ["L00JHONN1S", "B002BZX8Z6"],
"also_viewed": ["F002BZX8Z6", "B00JHONN1S", "B008F0SU0Y", "B00D23MC6W", "B00AFDOPDA"],
"bought_together": ["D202BZX8Z6"]
},
"salesRank": {"Toys & Games": 211836},
"brand": "Coxlures",
"categories": [["Sports & Outdoors", "Other Sports", "Dance"]]
}
Here is my attempt (Please note, this is within a MapReduce program so some lines may seem out of context.):
JSONObject object = new JSONObject(sampleText); //sampleText is json that has been split by line
JSONArray boughtTogether = new JSONArray(object.getJSONArray("bought_together"));
using the following code, I hope it's help you.
//this will be your json object that contains and convert your string to jsonobject
//if you have json object already skip this.
JSONObject yourJSON = new JSONObject(targetString);
//getting the "related" jsonObject
JSONObject related = yourJSON.getJSONObject("related");
//getting the "bought_together" as an jsonArray and do what you want with it.
//you can act with jsonarray like an array
JSONArray bought_together = related.getJSONArray("bought_together");
//now if you run blow code
System.out.print(bought_together.getString(0));
//output is : D202BZX8Z6
-------update according to update the question------
you should change your code like this:
JSONObject object = new JSONObject(sampleText); //sampleText is json that has been split by line
JSONObject related = object.getJSONObject("related");
JSONArray boughtTogether = related.getJSONArray("bought_together");
-------update-------
i think you need to this point (it's not technicality all of they difference)
every thing are in {} , they will be JSONObject and the relation
is key and value like :
{"name":"ali"}
this is a jsonobject and the value of key "name" is ali and we call it
like:
myJsonObject.getString("name");
every thing are in [] ,they will be JSONArray and the relation is
index and value like :
["ali"]
this is a JsonArray the value of index 0 is ali and we call it
like:
myJsonArray.getString(0);
so in your case:
your total object is a JSONObject
the value of "related" key is still a JSONObject
the value of "bought_together" key (which is inside the value of {jsonobject} "related" key) is a JSONArray
I creating a Java web application with an action file which generates data from database to a JSON array. Now, I want this JSON array to be passed to jQuery? How is that possible?
FYI. I applied Struts 2, Spring and Hibernate frameworks. I am not using PHP for this app.
Update:
This is my Struts 2 action method:
public static String jsonData = null;
public String generateJson() throws Exception
{
JSONObject json = null;
JSONArray jsonArray = new JSONArray();
this.records = this.recordManager.getAllRecords();
for (RecordEntity record : records)
{
json = new JSONObject();
json.put("id", record.getId());
json.put("firstname", record.getFirstName());
json.put("lastname", record.getLastName());
json.put("due", record.getDue());
json.put("email", record.getEmail());
json.put("website", record.getWebsite());
jsonArray.put(json);
}
System.out.println(jsonArray.toString());
jsonData = jsonArray.toString(); // jsonData will be passed to jQuery
return SUCCESS;
}
And this is my jQuery. I am using BootstrapTable so this is the structure, and I want the value of jsonData to be passed to this jQuery:
$('#record').bootstrapTable({
method : 'get',
data: jQuery.parseJSON(VALUE_OF_jsonData_HERE),
cache : ...
...
...
});
Using Ajax will help you,
refer this to get started, also do read about Ajax first to have a background