Getting attributes from JSON code with Jackson Parser - java

I want to parse a JSON string using Jackson JSON parser. The JSON code which I want to parse contains an array in which there is an object. From this object, I want to extract the text and retweet_count attributes:
[
{
"created_at": "Tue Jan 08 08:19:58 +0000 2013",
"id": 288560667345178600,
"text": "test tweet",
"source": "web",
"truncated": false,
"user": {
"id": 941802900,
"id_str": "941802900",
"location": ""
},
"contributors": null,
"retweet_count": 0,
"favorited": false,
"retweeted": false
}
]
I tried to do it using this code:
JsonFactory f = new JsonFactory();
JsonParser jp = f.createJsonParser(str);
boolean first = true;
while (jp.nextValue() != JsonToken.END_ARRAY) {
Tweet tweet = new Tweet();
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldName = jp.getCurrentName();
jp.nextToken();
if (fieldName.equals("text")) {
tweet.setText(jp.getText());
} else if (fieldName.equals("retweet_count")) {
tweet.setRetweetCount(jp.getValueAsLong());
}
}
}
However, I am not getting the expected results. I think that the the problem is that inside the 'tweet' object, I have another 'user' object and when the parser encounters the } of the user object, it thinks that it is the } of the whole tweet object. Can you please tell me how can I resolve this situation?

Is there a particular reason why you try to use Streaming API instead of tree model or data-binding? Latter two could result in much simpler code. For example:
#JsonIgnoreProperties(ignoreUnknown=true) // so we only include ones we care about
public class Tweet {
String text;
int retweet_count;
}
ObjectMapper mapper = new ObjectMapper(); // reusable (please reuse, expensive to create)
Tweet tweet = mapper.readValue(json, Tweet.class);
System.out.println("Tweet with text '"+tweet.text+"', retweet count of "+tweet.retweet_count);
with data-binding. And with tree model:
ObjectNode root = mapper.readTree(json);
String text = root.path("text").getText();
// and so on

You should probably do what StaxMan suggested and model your data correctly in objects, but the fastest way I know to get the data you want is something like the code sample below.
List<Map<String, Object>> val = readValue(json, new TypeReference<List<Map<String, Object>>>() { });
for (Map<String, Object>> map : val) {
String tweet = map.get("text");
Integer retweetCount = map.get("retweet_count");
}

Here is the updated code which would work. You need to consider the user fieldName and parse it separately, so the } of user object would not be considered as the end of the root object
while (jp.nextValue() != JsonToken.END_ARRAY) {
Tweet tweet = new Tweet();
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldName = jp.getCurrentName();
jp.nextToken();
if (fieldName.equals("user")) {
//Here the current token in BEGIN_OBJECT
while (jp.nextToken() != JsonToken.END_OBJECT) {
//You can use the user properties here
}
} else if (fieldName.equals("text")) {
tweet.setText(jp.getText());
} else if (fieldName.equals("retweet_count")) {
tweet.setRetweetCount(jp.getValueAsLong());
}
}
}

Related

Retrofit - Deserilize Json response map into a property

I'm having a problem deserializing the response that I'm getting from retrofit.
My problem is that I get the following response:
"associations": {
"1": {
"uri": "https://api.ap.org/media/v/content/690b9f679b1d4d8abc8042ca53140625?qt=FckGhfkHkvF&et=0a1aza3c0&ai=881778bb579d79e17f54b046a86a81cf",
"altids": {
"itemid": "690b9f679b1d4d8abc8042ca53140625",
"etag": "690b9f679b1d4d8abc8042ca53140625_0a1aza3c0"
},
"version": 0,
"type": "picture",
"headline": "Facebook Ads-Targeting Info"
}
}
My entity looks like the following:
public class Associations{
private Map<String, JsonMember> association;
public Map<String, JsonMember> getAssociation() {
return association;
}
public void setAssociation(Map<String, JsonMember> association) {
this.association = association;
}
}
I want that association takes the value from the map, but I don't know how to specify that it takes the object inside that association. Those association keys can be returned as any number, so I can't hard code the "1".
Any ideas?
Thanks for your help!
You can read your json to a JsonNode and iterate over its properties names with a Iterator<String> iterator obtained calling the JsonNode#fieldNames method, selecting only the properties with a numeric name (this depends from what you mean for numeric, up to you the definition of a isNumber method):
Map<String, JsonMember> map = new HashMap<>();
//reading the jsonnode labelled with "associations"
JsonNode node = mapper.readTree(json).at("/associations");
Iterator<String> iterator = node.fieldNames();
while (iterator.hasNext()) {
String next = iterator.next();
if (isNumber(next)) { //<-- ok next is numeric
map.put(next, mapper.treeToValue(node.get(next), JsonMember.class));
}
}

Having trouble accessing "endpoints" from below Json

I want all the URL present under the "endpoints" json.
I tried with access it through converting the String to JSON and after that using JSON parsar. But couldn't successed.
{
"process":{
"id":"epsswitch/process/create-switch-bdl",
"views":{
"selection":{
"uri":"//scp/sdf/sdf/s/df",
"controller":"scp/switching/createswitchingbdl/controllers/selection"
},
"customers":{
"uri":"//scp/vv/vv/views/Customers",
"controller":"scp/x/vc/controllers/customers"
},
"instructions":{
"uri":"//scp/df/fd/views/Information",
"controller":"scp/switching/fd/controllers/instructions"
},
"confirm":{
"uri":"//scp/switching/createswitchingbdl/views/Confirmation",
"controller":"scp/switching/createswitchingbdl/controllers/confirm"
}
},
"endpoints":{
"data":{
"uri":"/epsswitch/create/data?cc=true&al=true&ac=true"
},
"bank":{
"uri":"/fdd/df/df/v1/bank/df/",
"method":"GET"
}
}
}
There are multiple ways you can parse an JSON string. I use json simple (org.json.simple), java json are some of them. The following code uses the latter. It takes all the keys from the json string and retrieves all the values inside each key.
String sJSONString = "<your json object here>";
JSONObject jsObjects = new JSONObject(sJSONString);
//This is where you get your keys
Iterator<String> keys = jsObjects.keys();
//Use while if you need to poll for each key in JSON string
while(keys.hasNext())
{
String keyN = keys.next();
JSONObject jsoObjN = new JSONObject();
jsoObjN = (JSONObject)jsObjects.get(keyN);
//<Your logic here>
}
Following code help me to achive this.
final JsonNode studentNode = mapper.readTree(sPhone.getProcessJson());
JsonNode process = studentNode.get("process");
JsonNode endpoints = process.get("endpoints");
for (JsonNode jsonNode : endpoints) {
JsonNode uri = jsonNode.get("uri");
}

How to check if a JsonNode is a single element or an Array in Java?

I am traversing a json response with JsonNode (com.fasterxml.jackson.databind.JsonNode)
How can I check if a given JsonNode is a single element or an Array?, because I need to traverse it deeper, and update some values (for example, the name value)
I can have a json response like this: (with a single element)
{ person: {
name: "name1",
address: "address1"
}
}
or I can have a json response like this: (with a )
{ "person": [
{
"name": "name1",
"address": "address1"
},
{
"name": "name2",
"address": "address2"
}
]
}
For an single element, I have this code:
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonString);
JsonNode personNode = root.findPath("person");
if(!personRootNode.isMissingNode())
((ObjectNode)nameNode).put("name","UPDATED NAME");
And, for an array element, I have this code
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonString);
JsonNode personNode = root.findPath("person");
if(!personNode .isMissingNode())
for(JsonNode node: personRootNode){
if(!node.isMissingNode()) {
((ObjectNode)node).put("name","UPDATED NAME");
}
}
I want to mix the logic in a single place, because the unique difference is the for loop
I can wrap the replace logic in a function/method. But how do I check if the current node is an element or an array?
You can call isArray() function on JsonNode object. For example :
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(s2);
JsonNode personNode = root.findPath("person");
if(personNode.isArray()) {
///array found
} else {
// non-array element
}
use the below method. Enter the JSONObject to check and node name.(used the org.json.JSONObject)
private void checkNodeStatus(JSONObject jsonObject, String node) {
if (jsonObject.optJSONArray(node) != null) {
} else if (jsonObject.optString(node) != null) {
JSONArray array = new JSONArray();
array.put(jsonObject.getJSONObject(node));
jsonObject = jsonObject.put(node, array);
} else {
System.out.println("error in checkNodeStatus > node : " + node);
}
}
if It is a JSONObject, It converts to JSONArray.

Jackson JSON Java nested object and arrays

I have an example nested json object like below :
{
"payload": {
"id": "1",
"apiResp": {
"apiRespDetails": {
"report": {
"reportId": "reportid1",
"reportDetails": [
{
"code": "1",
"rating": "good"
},
{
"code": "2",
"rating": "bad"
},
{
"code": "3",
"rating": "fair"
}
]
}
}
}
}
}
I only need the report object, I do not need any of its parent object details. What would be the best way to get just that using the Jackson API ?
I have created a Java Class called Report.java with fields reportId (String) and reportDetails(List of ReportDetail ) where ReportDetail is another class with String fields code , rating etc. Do I need to use some Deserializer, JsonTreeParser mechanism?Thanks.
The solution for this is jayway Java implementation for JsonPath.
JsonPath is the json equivalent for the XPath query language for XML.
the query langauge is quite powerful, as can be seen in the examples on the github readme.
Here is a quick demo to get you started:
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import com.jayway.jsonpath.*;
import net.minidev.json.JSONArray;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.*;
public class JsonPathDemo2
{
public static void main(String[] args)
{
// query: search for any report property below root
String jsonPathQuery = "$..report";
try (InputStream is = Files.newInputStream(Paths.get("C://temp/xx.json"))) {
Object parsedContent =
Configuration.defaultConfiguration().jsonProvider().parse(is, StandardCharsets.UTF_8.name());
System.out.println("hasJsonPath? " + hasJsonPath(jsonPathQuery).matches(parsedContent));
Object obj = JsonPath.read(parsedContent, jsonPathQuery);
System.out.println("parsed object is of type " + obj.getClass());
System.out.println("parsed object to-string " + obj);
JSONArray arr = (JSONArray)obj;
System.out.println("first array item is of type " + arr.get(0).getClass());
System.out.println("first array item to-string " + arr.get(0));
} catch (Exception e) {
e.printStackTrace();
}
}
}
output:
hasJsonPath? true
parsed object is of type class net.minidev.json.JSONArray
parsed object to-string [{"reportId":"reportid1","reportDetails":[{"code":"1","rating":"good"},{"code":"2","rating":"bad"},{"code":"3","rating":"fair"}]}]
first array item is of type class java.util.LinkedHashMap
first array item to-string {reportId=reportid1, reportDetails=[{"code":"1","rating":"good"},{"code":"2","rating":"bad"},{"code":"3","rating":"fair"}]}
Hi found two solutions using jackson fasterxml api.
In the first one you can just use the findValue method on the jsonNode and pass in the string value of property/object you are looking for
String jsonresponse = "above json string";
JsonFactory jsonFactory = new JsonFactory();
JsonParser jp = jsonFactory.createParser(jsonresponse);
jp.setCodec(new ObjectMapper());
JsonNode jsonNode = jp.readValueAsTree();
JsonNode reportNode = jsonNode.findValue("report");
ObjectMapper mapper = new ObjectMapper();
Report report = mapper.convertValue(reportNode, Report.class);
This other solution use JsonToken which travels the json response till you find what you are looking for
JsonFactory factory = new JsonFactory();
factory.setCodec(new ObjectMapper());
JsonParser parser = factory.createParser(jsonresponse);
while(!parser.isClosed()){
JsonToken jsonToken = parser.nextToken();
if(JsonToken.FIELD_NAME.equals(jsonToken)){
String fieldName = parser.getCurrentName();
if("report".equals(fieldName)) {
jsonToken = parser.nextToken();
Report report = parser.readValueAs(Report.class);
} else {
jsonToken = parser.nextToken();
}
}
}

Parse json with mixed child objects via gson and send back as list

I'm facing a problem related to parsing json which have mixed arrays of child classes, and I need to provide that as java list back to client.
Sample JSON:
{
"status": "OK",
"results": [
{
"type": "one",
"Id": "2170676",
"count": "456",
"title": "title",
"description": "description",
"oneMemberOne": "11",
"oneMemberTwo": "12",
}
{
"type": "two",
"Id": "2170677",
"count": "123",
"title": "title",
"description": "description",
"twoMemberOne": "21",
"twoMemberTwo": "22",
}
]
}
I created one Parent class and two child class from this:
Num : type, Id, count, title, description fields
One extends Num : oneMemberOne, oneMemberTwo
Two extends Num : twoMemberOne, twoMemberTwo
Now my question:
I have a method for asking results. Say it List<Num> getResults()
I parse the data properly like this:
List<Num> result = new ArrayList<Num>();
JsonParser parser = new JsonParser();
JsonObject jsonObject = parser.parse(lastResponsePayload).getAsJsonObject()
JsonArray results = jsonObject.get("results").getAsJsonArray();
for (JsonElement element : results) {
JsonObject trs = element.getAsJsonObject();
String type = trs.get("type").getAsString();
if (type.equals("one") {
One one = new Gson().fromJson(element, One.class);
result.add(product);
} else if (type.equals("two") {
Two two = new Gson().fromJson(element, Two.class);
result.add(metaproduct);
}
return result;
Now, on client side, after I get the list, i have to do this:
List<Num> results = getResults();
List<One> oness = new ArrayList<One>();
List<Two> twoss = new ArrayList<Two>();
for(Num n : results) {
if(n.type.equals("one")
oness.add((One) n);
else
twoss.add((Two) n);
Is this a good design for this scenario ?
User of this API has to downcast everytime based on the type field of parent class. Because webservice gives me mixed array of child classes, I have to do this. Is there any better approach to this problem ?
One another approach in my mind is to create a Result class which contains two members One and Two and provide my a List<Result> instead of List<Num>,
but then user has to check whether member is null or not and then take appropriate steps.
Thank you in advance.
I will suggest that you have another class something like this. It prevents client code from spinning through list again and parsing out records. Please note I have not run this code to test it.
public class Result {
private List<One> oness;
private List<Two> twoss;
public List<One> getOness() {
return oness;
}
public void setOness(List<One> oness) {
this.oness = oness;
}
public List<Two> getTwoss() {
return twoss;
}
public void setTwoss(List<Two> twoss) {
this.twoss = twoss;
}
}
And change
List<Num> getResults()
To
Result getResults()
also user will not have to check for nulls if you modify your parsing logic. Please see that in case we don't have results we are not returning null list but EmptyList.
Result result = new Result();
JsonParser parser = new JsonParser();
JsonObject jsonObject = parser.parse(lastResponsePayload).getAsJsonObject()
JsonArray results = jsonObject.get("results").getAsJsonArray();
List<One> oness = null;
List<Two> twoss = null;
for (JsonElement element : results) {
JsonObject trs = element.getAsJsonObject();
String type = trs.get("type").getAsString();
if (type.equals("one")) {
if(oness == null) {
oness = new ArrayList<One>();
result.setOness(oness);
}
One one = new Gson().fromJson(element, One.class);
oness.add(product);
} else if (type.equals("two")) {
if(twoss == null) {
twoss = new ArrayList<Two>();
result.setTwoss(twoss);
}
Two two = new Gson().fromJson(element, Two.class);
twoss.add(metaproduct);
}
if(oness == null) {
result.setOness(Collections.<One>EMPTY_LIST);
}
if(twoss == null) {
result.setTwoss(Collections.<Two>EMPTY_LIST);
}
return result;
Hope it helps :)

Categories

Resources