Api I'm trying to get info from https://prices.runescape.wiki/api/v1/osrs/latest
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create("https://prices.runescape.wiki/api/v1/osrs/latest"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readTree(response.body());
JsonNode data = json.get("data");
List<Data> items = new ArrayList<>();
data.forEach(item -> {
Data d = new Data(
item.get("high").asInt(),
item.get("highTime").asInt(),
item.get("low").asInt(),
item.get("lowTime").asInt()
);
items.add(d);
});
Problem is the object is the itemID. So if I want the item id 6. I can't do it since it's not an attribute of the object.
"2":{"high":164,"highTime":1672078170,"low":160,"lowTime":1672078164} ``
2 is the itemID and the object.
Below is when I loop through the arraylist and print it out
Data{high=157, highTime=1672071161, low=150, lowTime=1672071151}
Data{high=187987, highTime=1672071066, low=182005, lowTime=1672070881}
Data{high=189903, highTime=1672071052, low=186820, lowTime=1672070884}
Data{high=190000, highTime=1672070957, low=184882, lowTime=1672070984}
Your JSON has the following structure:
{
"data":{
"2":{ ... },
"6":{ ... },
"8":{ ... },
"10":{ ... },
"12":{ ... }
...
}
}
And property "data" is associated not with an object, or a List<Data>, but with a Map<Integer,Data> (or Map<String,Data>.
There are several ways how you can parse:
You can define an object with a single property Map<Integer,Data> data.
public static class DataWrapper {
private Map<Integer, Data1> data;
// getters, setters
}
Usage example:
String json = """
{
"data":{ ... }
}
""";
ObjectMapper mapper12 = new ObjectMapper();
DataWrapper dataWrapper = mapper12.readValue(json12, DataWrapper.class);
Map<Integer, Data1> dataMap = dataWrapper.getData();
System.out.println("item 6: " + dataMap.get(6));
Another approach would be to create a JsonNode by parsing the given JSON, access the node mapped to the property "data" and parse it as a Map using ObjectReader and its method ObjectReader.readValue(). To generate ObjectReader we can make use of the method ObjectMapper.readerFor() which expects a TypeReference.
That's how the second approach might be implemented:
String json = """
{
"data":{ ... }
}
""";
ObjectMapper mapper = new ObjectMapper();
JsonNode tree = mapper.readTree(json);
ObjectReader reader = mapper.readerFor(new TypeReference<Map<Integer, Data>>() {});
Map<Integer, Data> itemsMap = reader.readValue(tree.get("data"));
System.out.println("item 6: " + itemsMap.get(6));
Output:
item 6: Data{high=195500, highTime=1672079035, low=182009, lowTime=1672078518}
Related
I am a receiving a JSON object and I need to save the values to my DB. But I'm having an issue figuring out how to retrieve the particular values in the JSON object.
In this case, I want to retrieve the values of 'originationNumber' and 'messageBody'
The response object -
{"originationNumber":"***","destinationNumber":"***","messageKeyword":"KEYWORD_***","messageBody":"Answer ","previousPublishedMessageId":"1slamq6mdpucd8q4i7iabf1sikc629ga253tr6o0","inboundMessageId":"88bc02fc-aff3-4277-ac1d-f27b6d3b6abb"}
Method to receive message -
public String getReceivedMessages(Messaging receivedMessage) {
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(awsAccessKey, awsSecretKey);
AmazonSQS sqsClient = AmazonSQSClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(String.valueOf(awsRegion)).build();
StringBuilder sb = new StringBuilder();
String queueUrl = "https://sqs.us-east-1.amazonaws.com/1234567/GetReceivedMessages";
List<Message> messages = sqsClient.receiveMessage(new ReceiveMessageRequest(queueUrl)
.withMaxNumberOfMessages(1).withWaitTimeSeconds(20)).getMessages();
for (Message message : messages) {
sb.append(message.getBody());
sqsClient.deleteMessage(queueUrl, message.getReceiptHandle());
}
// Save messages to DB
String userId = connectionRequestRepository.getUserId();
Date date = new Date();
Timestamp now = new Timestamp(date.getTime());
receivedMessage.setUserId(userId);
receivedMessage.setOriginationNumber("");
receivedMessage.setDestinationNumber("***");
receivedMessage.setMessageBody("");
receivedMessage.setMessageType("RECEIVED");
receivedMessage.setCreatedAt(now);
messagingRepository.save(receivedMessage);
System.out.println(sb); <--- Prints response object to console
return sb.toString();
}
You can use jackson library for that.
Solution 1: You can use ObjectMapper as below:
Message Class to map JSON to Java Object:
public class Message {
private String originationNumber;
private String messageBody;
// public getter and setters methods
}
Create Object From JSON String:
Message message = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
message = objectMapper.readValue(payload, Message.class);
} catch (JsonProcessingException e) {
// Log Or do some action as per need
}
Here message will have those values. DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is used here to skip exception as there are other fields also in JSON and not present in Java Class (as we don't need them).
Solution 2: Alternatively you can use JsonNode from same Jackson library and read nodes one by one as below:
ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = mapper.readTree("{\"originationNumber\":\"***\",\"destinationNumber\":\"***\",\"messageKeyword\":\"KEYWORD_***\",\"messageBody\":\"Answer \",\"previousPublishedMessageId\":\"1slamq6mdpucd8q4i7iabf1sikc629ga253tr6o0\",\"inboundMessageId\":\"88bc02fc-aff3-4277-ac1d-f27b6d3b6abb\"}");
String originationNumber = actualObj.get("originationNumber");
String messageBody = actualObj.get("messageBody");
In this approach you won't need to create Message class.
You can convert the json string into a json object
https://www.javatpoint.com/how-to-convert-string-to-json-object-in-java
String string = "{\"originationNumber\":\"***\",\"destinationNumber\":\"***\",\"messageKeyword\":\"KEYWORD_***\",\"messageBody\":\"Answer \",\"previousPublishedMessageId\":\"1slamq6mdpucd8q4i7iabf1sikc629ga253tr6o0\",\"inboundMessageId\":\"88bc02fc-aff3-4277-ac1d-f27b6d3b6abb\"}";
JSONObject json = new JSONObject(string);
System.out.println(json.toString());
String destinationNumber = json.getString("destinationNumber");
System.out.println(destinationNumber);
where ur json like
{"originationNumber":"***",
"destinationNumber":"***",
"messageKeyword":"KEYWORD_***",
"messageBody":"Answer","previousPublishedMessageId":"1slamq6mdpucd8q4i7iabf1sikc629ga253tr6o0",
"inboundMessageId":"88bc02fc-aff3-4277-ac1d-f27b6d3b6abb"
}
it like
"key":Value
i think ur code will be like
receivedMessage.setUserId(userId);
receivedMessage.setOriginationNumber("originationNumber");
receivedMessage.setDestinationNumber("destinationNumber");
receivedMessage.setMessageBody("messageBody");
receivedMessage.setMessageType("RECEIVED");
receivedMessage.setCreatedAt(now);
messagingRepository.save(receivedMessage);
I would like to replace JSON.simple with Jackson on the following code snippet:
JSONObject request = new JSONObject();
request.put("String key", /String value/);
request.put("String key", /int value/);
...
It looks like that:
ObjectMapper mapper = new ObjectMapper();
JsonNode request = mapper.createObjectNode();
((ObjectNode) request).put("String key", /String value/);
((ObjectNode) request).put("String key", /int value/);
I found a bit too complicated and ugly with the casting and extra declaration. Am I doing it wrong, any advice?
(I would like to send this JSON through REST entity)
Use ObjectNode instead of JsonNode. Try this:
ObjectNode request = mapper.createObjectNode();
request.put("key", "val");
System.out.println(request.toString());
Your code should be like that
ObjectMapper mapper = new ObjectMapper();
ObjectNode request = mapper.createObjectNode();
request.put("String key", /String value/);
request.put("String key", /int value/);
This link has pretty good information. I feel you should have objects instead of manually building your json.
https://www.baeldung.com/jackson-object-mapper-tutorial
public class Car {
private String color;
private String type;
// standard getters setters
}
Then
ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
String output = objectMapper.writeValueAsString(car);
Output:
{"color":"yellow","type":"renault"}
I am writing automation script to validate json responses of REST APIs and i am using faster xml to serialize and convert java object to json format.
I have a user case where I have to get the json response and add a new array element to an existing array and post it back.
The json response after GET looks like this :
{
"name":"test",
"id":"1234",
"nodes":[
{
"nodeId":"node1"
},
{
"nodeId":"node2"
}
]
}
To this json response, I need to add a third entry for nodes array
{ "nodeId": "node3" } and then post this.
Can someone please help me understand how to add a new array element to an existing array?
You can try:
//Your JSON response will be in this format
String response = "{ \"name\":\"test\", \"id\":\"1234\", \"nodes\":[ { \"nodeId\":\"node1\" }, { \"nodeId\":\"node2\" } ] }";
try {
JSONObject jsonResponse = new JSONObject(response);
JSONArray nodesArray = jsonResponse.getJSONArray("nodes");
JSONObject newEntry = new JSONObject();
newEntry.put("nodeId","node3");
nodesArray.put(newEntry);
jsonResponse.put("nodes",nodesArray);
} catch (JSONException e) {
e.printStackTrace();
}
Now you can post your jsonResponse.toString() as required.
I would rather go for cleaner approach, create Object with below structure -
public class Response{
private String name;
private int id;
private List<Node> nodes;
<Getter & Setter>
}
public class Node{
private String nodeId;
}
Serialize the json -
Response response = objectMapper.readValue(responseJson,
Response.class);
Add the new incoming node object to response -
response.getNodes().add(New Node("{new node Value}"));
Deserialize before post -
objectMapper.writeValueAsString(response);
Details ---
One of my POJO SomeResponseObject for an api response has attribute
#JsonProperty("s_summary")
private Map<String, SummaryObject> summary
which further has few more attributes. These are summed in json as follows :
{
"s_summary": {
"rewardSubscription": {
"accountId": "XYZ",
"startDate": "2015-12-29T19:00:00+05:30",
"endDate": "2017-06-21T00:00:00+05:30",
"isActive": true,
"entityId": "ID123",
"status": "ACTIVE"
}
}
}
This POJO(json) is further modified by our service to return a RESPONSE as :
{
"rewardSubscription": {
"accountId": "XYZ",
"startDate": "2015-12-29T19:00:00+05:30",
"endDate": "2017-06-21T00:00:00+05:30",
"isActive": true,
"entityId": "ID123",
"status": "ACTIVE"
}
}
Narrowing Down ---
Now when we are writing tests against this API call. We end up being unable to map the response to any specific POJOs(java response class). Test code -
JSONObject responseObject = new JSONObject(responseFromService.getResponseBody())
.getJSONObject("RESPONSE");
ObjectMapper objectMapper = new ObjectMapper();
SomeResponseObject summaryResponse = objectMapper.getObjectMapper()
.readValue(responseObject.toString(), SomeResponseObject.class); // And this wouldn't work.
Question --
Is there any way we can cast the current API response or wrap it somehow to be mapped to the actual POJO(SomeResponseObject.class)?
Thanks in advance.
Problem
You receive an object with a rewardSubscription field, or, in your case, a map, with a rewardSubscription key. You can't convert a map to an object of SomeResponseObject type directly.
Solution
Option 1
Convert json to a map manually and set it to the SomeResponseObject instance:
JSONObject responseObject = new JSONObject(responseFromService.getResponseBody())
.getJSONObject("RESPONSE");
ObjectMapper objectMapper = new ObjectMapper();
Map<String, SummaryObject> summaryMap = objectMapper.readValue(responseObject.toString(), new TypeReference<Map<String, SummaryObject>>() {});
SomeResponseObject response = new SomeResponseObject();
response.setSummaryMap(summaryMap);
Option 2
So as not to manually convert map each time, write a custom deserializer that will handle both cases. The deserialize method should be similar to this:
#Override
public SomeResponseObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = jp.readValueAsTree();
JsonNode sSummaryNode = rootNode.get("s_summary");
if (sSummaryNode != null) {
// Usual case.
return objectMapper.treeToValue(sSummaryNode, SomeResponseObject.class);
} else {
// Special case - when received a map.
Map<String, SummaryObject> summaryMap = objectMapper.readValue(rootNode.toString(), new TypeReference<Map<String, SummaryObject>>() {});
SomeResponseObject response = new SomeResponseObject();
response.setSummaryMap(summaryMap);
return response;
}
}
And then in the code you don't care:
ObjectMapper objectMapper = new ObjectMapper();
SomeResponseObject response = objectMapper.readValue(json, SomeResponseObject.class);
I am trying to use org.json.JSONObject to build the following target json string:
{"und":[{"value":"some#one.com"}]}
Here is my code:
JSONObject und = new JSONObject();
und.accumulate("und", new JSONObject().put("value", "some#one.com"));
System.out.println( und.toString() );
But it produces the following:
{"und":{"value":"some#one.com"}}
How can I produce the target json string?
Thanks and regards.
EDIT
Thanks to SLaks's input, here is the code that produces the target string:
JSONObject und = new JSONObject();
JSONArray arr = new JSONArray();
und.put("und", arr);
arr.put(new JSONObject().put("value", "some#one.com"));
System.out.println( und.toString() );
You might want to take a look at Jackson, it's one of the most efficient and supported JSON libraries available on Java.
If you are familiar with unmarshalling/deserialization, you can turn an POJO into json and vise versa.
#JsonSerialize(include = JsonSerialize.Inclusion.NON_DEFAULT)
public class SomeBean {
Und[] und;
// TODO: Getters and setters
public static Und class {
public String value;
// TODO: Getters and setters
}
}
If you are directly parsing a JSON string or file, you can use the ObjectMapper class
SomeBean someBean = new ObjectMapper().readValue("input goes here", SomeBean.class);
// If you want just a string you can pass in the String class
String json = new ObjectMapper().readValue("input", String.class);
If the JSON is coming from a web service, check out Spring's restTemplate, super easy to work with.
RestTemplate restTemplate = new RestTemplate();
SomeBean someBean = restTemplate.getForEntity("URI goes here", SomeBean.class);
String json = restTemplate.getForEntity("URI goes here", String.class);