how to manipulate HTTP json response data in Java - java

HttpGet getRequest=new HttpGet("/rest/auth/1/session/");
getRequest.setHeaders(headers);
httpResponse = httpclient.execute(target,getRequest);
entity = httpResponse.getEntity();
System.out.println(EntityUtils.toString(entity));
Output as follows in json format
----------------------------------------
{"session":{"name":"JSESSIONID","value":"5F736EF0A08ACFD7020E482B89910589"},"loginInfo":{"loginCount":50,"previousLoginTime":"2014-11-29T14:54:10.424+0530"}}
----------------------------------------
What I want to know is how to you can manipulate this data using Java without writing it to a file?
I want to print name, value in my code
Jackson library is preferred but any would do.
thanks in advance

You may use this JSON library to parse your json string into JSONObject and read value from that object as show below :
JSONObject json = new JSONObject(EntityUtils.toString(entity));
JSONObject sessionObj = json.getJSONObject("session");
System.out.println(sessionObj.getString("name"));
You need to read upto that object from where you want to read value. Here you want the value of name parameter which is inside that session object, so you first get the value of session as JSONObject using getJSONObject(KeyString) and read name value from that object using function getString(KeyString) as show above.
May this will help you.

Here's two ways to do it without a library.
NEW (better) Answer:
findInLine might work even better. (scannerName.findInLine(pattern);)
Maybe something like:
s.findInLine("{"session":{"name":"(\\w+)","value":"(\\w+)"},"loginInfo":{"loginCount":(\\d+),"previousLoginTime":"(\\w+)"}}");
w matches word characters (letters, digits, and underscore), d matches digits, and the + makes it match more than once (so it doesnt stop after just one character).
Read about patterns here https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
OLD Answer:
I'm pretty sure you could use a scanner with a custom delimiter here.
Scanner s = new Scanner(input).useDelimiter("\"");
Should return something like:
{
session
:{
name
:
JSESSIONID
,
value
:
5F736EF0A08ACFD7020E482B89910589
And so on. Then just sort through that list/use a smarter delimiter/remove the unnecessary bits.
Getting rid of every other item is a pretty decent start.
https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html has info on this.

I higly recomend http-request built on apache http api.
private static final HttpRequest<Map<String, Map<String, String>>> HTTP_REQUEST = HttpRequestBuilder.createGet(yourUri, new TypeReference<Map<String, Map<String, String>>>{})
.addDefaultHeaders(headers)
.build();
public void send(){
ResponseHandler<Map<String, Map<String, String>>> responseHandler = HTTP_REQUEST.execute();
Map<String, Map<String, String>> data = responseHandler.get();
}
If you want use jackson you can:
entity = httpResponse.getEntity();
ObjectMapper mapper = new ObjectMapper();
Map<String, Map<String, String>> data = mapper.readValue(entity.getContent(), new TypeReference<Map<String, Map<String, String>>>{});

Related

Java get nested value from ResponseEntity without creating a pojo

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!

JSONObject shows escape characters

I have a manual json object that i created and I use escape characters for one of the fields. I see escape strings whenever i print my jsonobject, is there a way to remove them? I'm just worried that the client will get the json object with the escaped strings when i send this over through the server.
String car_parameters = "{\"property_name\":\"car_id\",\"traceKey\":\"account_id,accountName,car_id\"}";
System.out.println("car params"+car_parameters);
JSONObject jsonObject = new JSONObject();
jsonObject.put("message", car_parameters);
System.out.println("one"+jsonObject);
System.out.println("two"+payload);
These are the outputs respectively, i tried converting json object to string but that did not make a difference.I'd expect to see json object's fields without escape characters like when i print out just the string.
car params{"property_name":"car_id","traceKey":"account_id,accountName,car_id"}
one{"message":"{\"property_name\":\"car_id\",\"traceKey\":\"account_id,accountName,car_id\"}"}
two{"message":"{\"property_name\":\"car_id\",\"traceKey\":\"account_id,accountName,car_id\"}"}
The escape character is caused by Java syntax. Java does not allow
String car_parameters = "{"property_name":"car_id","traceKey":"account_id,accountName,car_id"}"
is converted automatically.
You can turn it into an object, as shown below
System.out.println("toString"+JSONObject.parse(car_parameters));
output:
Object:{"traceKey":"account_id,accountName,car_id","property_name":"car_id"}
in addition:
If you send it to the client, it will also be {"message": "{property_ name":"car_ id","traceKey":"account_ id,accountName,car_ id"}"}
\It will become a part of the string, so the front end needs to handle it, but the transfer in Java does not affect the use.
You can do this. please consider what artifact you need.(My favorite is ObjectMapper.)
If you need generate from string, it is simple.
Maybe there is a lot of harsh way but I think it is not necessary now.
Remember that A first sample need new artifact as below in your pom.xml
<!-- for the JSONParser -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1</version>
</dependency>
public void JSonTest() throws JSONException, ParseException {
String car_parameters = "{\"property_name\":\"car_id\",\"traceKey\":\"account_id,accountName,car_id\"}";
System.out.println("car params"+car_parameters);
JSONParser jsonParser = new JSONParser();
Object myObject = jsonParser.parse(car_parameters);
System.out.println("One" + myObject);
// Put it separate.
JSONObject jsonObject = new JSONObject();
jsonObject.put("property_name", "car_id");
jsonObject.put("traceKey", "account_id,accountName,car_id");
System.out.println("Two"+jsonObject);
jsonObject.remove("property_name");
jsonObject.remove("traceKey");
// Put it a map first.
HashMap<String, String> myMap = new HashMap<>();
myMap.put("property_name", "car_id");
myMap.put("traceKey", "account_id,accountName,car_id");
jsonObject.put("message", myMap);
System.out.println("Three"+jsonObject);
}
And Here is output,
car params{"property_name":"car_id","traceKey":"account_id,accountName,car_id"}
One{"traceKey":"account_id,accountName,car_id","property_name":"car_id"}
Two{"traceKey":"account_id,accountName,car_id","property_name":"car_id"}
Three{"message":"{traceKey=account_id,accountName,car_id, property_name=car_id}"}

Converting malformed json array string to Java object

I have a malformed json array string which I get from an API call as follows:
[{\"ResponseCode\":1,\"ResponseMsg\":\"[{\"Code\":\"CA2305181\",\"Message\":\"Processed successfully\"}]\"}]
There is a double quote before open square bracket in the value of Response Msg property.
Is there a way to convert this into Java object ?
What I have tried so far:
I have used Jackson to parse it as follows but it gives error
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new ResponseNameStrategy());
Response[] response = mapper.readValue(strOutput1, Response[].class);
Error: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
I have also tried using Gson to parse it but it also gives error
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.create();
Response[] response = gson.fromJson(strOutput1, Response[].class);
Error: Expected BEGIN_ARRAY but was STRING at line 1 column 35 path $[0].ResponseMsg
I have gone through the following links on StackOverflow but none of them has addressed my issue:
How to Convert String Array JSON in a Java Object
Convert a JSON string to object in Java ME?
JSON Array to Java objects
Convert json String to array of Objects
converting 'malformed' java json object to javascript
I think the answer is in the comments, you appear to be trying to solve the issue on the wrong place.
You are receiving json which you wish to parse into java objects, unfortunately the json is malformed so will not parse.
As a general rule you should never be trying to solve the symptom, but should look for the root cause and fix that, it may sound trivial but fixing symptoms leads to messy, unpredictable, and unmaintainable systems.
So the answer is fix the json where it is being broken. If this is something or of your control, while you wait for the fix, you could put a hack in to fix the json before you parse it.
This way you won't compromise your parsing, and only have a small piece of string replacement to remove when the third party has fixed the issue. But do not go live with the hack, it should only be used during development.
As i mentioned in the comment, you should prepare your service response in order to parse it.
I implemented an example:
public class JsonTest {
public static void main(String args[]) throws JsonProcessingException, IOException{
String rawJson =
"[{\"ResponseCode\":1,\"ResponseMsg\":\"[{\"Code\":\"CA2305181\",\"Message\":\"Processed successfully\"}]\"}]";
String goodJson = "{"+rawJson.split("[{{.}]")[2]+"}";
ObjectMapper mapper = new ObjectMapper();
final ObjectNode node = mapper.readValue(goodJson, ObjectNode.class);
System.out.println("Pretty Print: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node));
System.out.println("Just code: " + node.get("Code"));
}
}
Which returns:
This is how I finally solved my issue:
String inputJsonStr = "[{\"ResponseCode\":1,\"ResponseMsg\":\"[{\"Code\":\"CA2305181\",\"Message\":\"Claim has been added successfully.\"}"
+ "]\"}]";
int indexOfRes = inputJsonStr.indexOf("ResponseMsg");
if(inputJsonStr.substring(indexOfRes+13,indexOfRes+14).equals("\""))
{
inputJsonStr = inputJsonStr.substring(0,indexOfRes+13) + inputJsonStr.substring(indexOfRes+14);
}
int indexOfFirstClosingSquare = inputJsonStr.indexOf("]");
if(inputJsonStr.substring(indexOfFirstClosingSquare+1, indexOfFirstClosingSquare+2).equals("\"")) {
inputJsonStr = inputJsonStr.substring(0, indexOfFirstClosingSquare+1)+inputJsonStr.substring(indexOfFirstClosingSquare+2);
}
Now inputJsonStr contains a valid json array which can be parsed into Java custom object array easily with gson as given in this SO link:
Convert json String to array of Objects

What is the best way to pass multiply parameters as one http query parameter?

I have java web application (servlet) that does user authentication using SalesForce Server OAuth Authentication Flow. This OAuth Authentication provides "state" query parameter to pass any data on callback. I have a bunch of parameters that I want to pass through this "state" query param. What is the best way to do it? In java in particularly?
Or in other words, what is the best way to pass an array or map as a single http query parameter?
You can put all in json or xml format or any other format and then encode in base64 as a one large string. Take care that params can impose some hard limit on some browser/web server.
So, I have done it this way. Thank you guys! Here are some code snippets to illustrate how it works for me:
// forming state query parameter
Map<String, String> stateMap = new HashMap<String, String>();
stateMap.put("1", "111");
stateMap.put("2", "222");
stateMap.put("3", "333");
JSONObject jsonObject = new JSONObject(stateMap);
String stateJSON = jsonObject.toString();
System.out.println("stateJSON: " + stateJSON);
String stateQueryParam = Base64.encodeBase64String(stateJSON.getBytes());
System.out.println("stateQueryParam: " + stateQueryParam);
// getting map from state query param
ObjectMapper objectMapper = new ObjectMapper();
stateMap = objectMapper.readValue(Base64.decodeBase64(stateQueryParam.getBytes()), LinkedHashMap.class);
System.out.println("stateMap: " + stateMap);
Here is output:
stateJSON: {"1":"111","2":"222","3":"333"}
stateQueryParam: eyIxIjoiMTExIiwiMiI6IjIyMiIsIjMiOiIzMzMifQ==
stateMap: {1=111, 2=222, 3=333}

JSON parser read an entry by entry from large JSON file

I have a huge JSON file (1GB) which is basically an array of objects in the below format
[{"x":"y", "p":"q"}, {"x1":"y1", "p1":"q1"},....]
I want to parse this file such the all the data is not loaded in memory.
Basically I want to get for eg: first 1000 objects in the array to memory process it and then get the next 1000 objects into the memory process it and so on util all data is read.
Is there any JSON library that supports this use case? I currently use Gson. However it loads all the data to memory when I call gson.fromJson()
Thanks in advance for the help.
It looks like Gson has a streaming API, which is what you want: https://sites.google.com/site/gson/streaming
With Jackson you can use a SAX-like approach (streaming) using a JsonParser object, in your case it would be something like this:
JsonFactory jsonFactory = new JsonFactory();
JsonParser parser = jsonFactory.createParser(new File("/path/to/my/jsonFile"));
// Map where to store your field-value pairs per object
Map<String, String> fields = new HashMap<String, String>();
JsonToken token;
while ((token = parser.nextToken()) != JsonToken.END_ARRAY) {
switch (token) {
// Starts a new object, clear the map
case START_OBJECT:
fields.clear();
break;
// For each field-value pair, store it in the map 'fields'
case FIELD_NAME:
String field = parser.getCurrentName();
token = parser.nextToken();
String value = parser.getValueAsString();
fields.put(field, value);
break;
// Do something with the field-value pairs
case END_OBJECT:
doSomethingWithTheObject(fields)
break;
}
}
parser.close();

Categories