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();
}
}
}
Related
I'm trying to parse the below Json using the Gson lib in Java. When using other languages, such as C#, this JSON is parsed into an array, however it seems Gson converts this into a set of java attributes (which to be honest, makes more sense to me). Does anyone know if I can change this behaviour of the Gson lib?
{
"Outer": {
"0": {
"Attr1": 12345,
"Attr2": 67890
},
"1": {
"Attr1": 54321,
"Attr2": 09876
}
}
}
The below code demonstrates how Gson parses the array as a JsonObject. To be clear, I realise I've referenced outer as a JsonObject but I was just doing this to demonstrate the code. If I try and reference outer as an JsonArray, the code fails.
String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.setLenient()
.serializeNulls()
.create();
JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");
System.out.println(outer);
System.out.println(outer.isJsonArray());
Result:
{"0":{"Attr1":12345,"Attr2":67890},"1":{"Attr1":54321,"Attr2":"09876"}}
false
//edit
I'm using this current simple Json as an example, however my application of this code will be to parse Json that's of varying and unknown shape. I therefore need Gson to automatically parse this to an array so that the isJsonArray returns true.
TL;DR: See "Using Deserializer" section at the bottom for parsing straight to array.
That JSON does not contain any arrays. An array would use the [...] JSON syntax.
Normally, a JSON object would map to a POJO, with the name in the name/value pairs mapping to a field of the POJO.
However, a JSON object can also be mapped to a Map, which is especially useful when the names are dynamic, since POJO fields are static.
Using Map
The JSON object with numeric values as names can be mapped to a Map<Integer, ?>, e.g. to parse that JSON to POJOs, do it like this:
class Root {
#SerializedName("Outer")
public Map<Integer, Outer> outer;
#Override
public String toString() {
return "Root[outer=" + this.outer + "]";
}
}
class Outer {
#SerializedName("Attr1")
public int attr1;
#SerializedName("Attr2")
public int attr2;
#Override
public String toString() {
return "Outer[attr1=" + this.attr1 + ", attr2=" + this.attr2 + "]";
}
}
Test
Gson gson = new GsonBuilder().create();
Root root;
try (BufferedReader in = Files.newBufferedReader(Paths.get("test.json"))) {
root = gson.fromJson(in, Root.class);
}
System.out.println(root);
Output
Root[outer={0=Outer[attr1=12345, attr2=67890], 1=Outer[attr1=54321, attr2=9876]}]
Get as Array
You can then add a helper method to the Root class to get that as an array:
public Outer[] getOuterAsArray() {
if (this.outer == null)
return null;
if (this.outer.isEmpty())
return new Outer[0];
int maxKey = this.outer.keySet().stream().mapToInt(Integer::intValue).max().getAsInt();
Outer[] arr = new Outer[maxKey + 1];
this.outer.forEach((k, v) -> arr[k] = v);
return arr;
}
Test
System.out.println(Arrays.toString(root.getOuterAsArray()));
Output
[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]
Using Deserializer
However, it would likely be more useful if the conversion to array is done while parsing, so you need to write a JsonDeserializer and tell Gson about it using #JsonAdapter:
class Root {
#SerializedName("Outer")
#JsonAdapter(OuterArrayDeserializer.class)
public Outer[] outer;
#Override
public String toString() {
return "Root[outer=" + Arrays.toString(this.outer) + "]";
}
}
class OuterArrayDeserializer implements JsonDeserializer<Outer[]> {
#Override
public Outer[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Parse JSON array normally
if (json.isJsonArray())
return context.deserialize(json, Outer[].class);
// Parse JSON object using names as array indexes
JsonObject obj = json.getAsJsonObject();
if (obj.size() == 0)
return new Outer[0];
int maxKey = obj.keySet().stream().mapToInt(Integer::parseInt).max().getAsInt();
Outer[] arr = new Outer[maxKey + 1];
for (Entry<String, JsonElement> e : obj.entrySet())
arr[Integer.parseInt(e.getKey())] = context.deserialize(e.getValue(), Outer.class);
return arr;
}
}
Same Outer class and test code as above.
Output
Root[outer=[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]]
I'll asume your JsonObject is a POJO class such like:
public Inner[] outer;
If you want an array of objects you can change your code to:
Inner[] jo = gson.fromJson(json, Inner[].class);
Jackson – Marshall String to JsonNode will be useful in your case.with following pom:-
//POM FILE
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
//JAVA CODE
//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//read JSON like DOM Parser
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode idNode = rootNode.path("id");
System.out.println("id = "+idNode.asInt());
JsonNode phoneNosNode = rootNode.path("phoneNumbers");
Iterator<JsonNode> elements = phoneNosNode.elements();
while(elements.hasNext()){
JsonNode phone = elements.next();
System.out.println("Phone No = "+phone.asLong());
}
You can use the JsonNode class's method findParent findValue and findPath which reduce your code as compare to another parsing library.
Please refer below code
1.To get an array of Objects (outerArray)
2.You can extract a JsonArray (outerJsonArray) containing values of inner objects in Outer (in case keys aren't significant for further use)
String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().serializeNulls().create();
JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");
Object[] outerArray = outer.entrySet().toArray();
// outerArray: [0={"Attr1":12345,"Attr2":67890}, 1={"Attr1":54321,"Attr2":"09876"}]
JsonArray outerJsonArray = new JsonArray();
outer.keySet().stream().forEach(key -> {
outerJsonArray.add(outer.get(key));
});
//jsonArray=[{"Attr1":12345,"Attr2":67890},{"Attr1":54321,"Attr2":"09876"}]
System.out.println(outer);
System.out.println(outerJsonArray.isJsonArray() + " " + outerJsonArray);
I have simple json which looks like this :
[
{
"id":"0",
"name":"Bob",
"place":"Colorado",
},
{
"id":"1",
"name":"John",
"place":"Chicago",
},
{
"id":"2",
"name":"Marry",
"place":"Miami",
}
]
What I want is using Java to create list of strings (List<String>) that contains all 'names'. I have some experience using Gson and I think about something like:
Gson gson = new Gson();
String[] stringArray= gson.fromJson(jsonString, " ".class);
The problem with this method is that I should create some POJO class which I didn`t in this case. Is it any way I can achieve it without creating separate class with this 'name' property ?
Using Jackson to parse, and Java 8 Streams API for extracting only the name field; the following may help you:
// Your string
jsonString = "[{ \"id\":\"0\", \"name\":\"Bob\", \"place\":\"Colorado\" }, { \"id\":\"1\", \"name\":\"John\", \"place\":\"Chicago\"}, { \"id\":\"2\", \"name\":\"Marry\", \"place\":\"Miami\" }]";
// using Jackson to parse
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.getTypeFactory();
List<MyInfo> myObjectList = objectMapper.readValue(jsonString, typeFactory.constructCollectionType(List.class, MyInfo.class));
// Java 8 Collections
List<String> nameList = myObjectList.stream().map(MyInfo::getName).collect(Collectors.toList());
Beware, it implies the usage of a MyInfo class representing your a Java class in which Json objects of yours would fit in.
You can use JSONArray to get value from key 'name'. Like this:
JSONArray jSONArray = new JSONArray(yourJson);
List<String> list = new ArrayList<>();
for (int i = 0; i < jSONArray.length(); i++) {
JSONObject object = (JSONObject) jSONArray.get(i);
String value = object.getString("name");
System.out.println(value);
list.add(value);
}
You may try the following code snippet,
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
List<String> ls = new ArrayList<String>();
JSONObject jsonObj = new JSONObject();
JSONArray jsonArr = new JSONArray();
JSONParser jsonParse = new JSONParser();
String str = "[{\"id\": \"0\",\"name\": \"Bob\",\"place\": \"Colorado\"},"
+ "{\"id\": \"1\",\"name\": \"John\",\"place\": \"Chicago\"},"
+ "{\"id\": \"2\",\"name\": \"Marry\",\"place\": \"Miami\"}]";
try {
jsonArr= (JSONArray) jsonParse.parse(str); //parsing the JSONArray
if(jsonArr!=null){
int arrayLength =jsonArr.size(); //size is 3 here
for(int i=0;i<arrayLength;i++){
jsonObj = (JSONObject) jsonParse.parse(jsonArr.get(i).toString());
ls.add(jsonObj.get("name").toString()); //as we need only value of name into the list
}
System.out.println(ls);
}
} catch (ParseException e) {
e.printStackTrace();
}catch(Exception e1){
e1.printStackTrace();
}
As you have array, use JSONArray and used jsonParse to avoid any parsing error.
I have used json-simple API to acheive the above.
I have few cases where I want to convert a snake_case JSON to nested JSON
e.g.
{
"snake_case": {
"test": "value"
}
}
to
{
"snake": {
"case": {
"test": "value"
}
}
}
Is there any way to do this in java other then manually parsing the strings with _ or there any libraries are there in java?
Consider your JSON data as String:
String strjson="{snake_case: {test: value}}";
then
JSONObject jj=new JSONObject(strjson);
JSONObject jfinal=new JSONObject();
Iterator<String> itr=jj.keys();
while(itr.hasNext())
{
String key=itr.next();
if(key.contains("-"))
{
JSONObject jkey=jj.getJSONObject(key);
JSONObject jnew=new JSONObject();
jnew.put(key.split("-")[1],jkey);
jfinal.put(key.split("-")[0],jnew);
}
}
you can get the output in jfinal.
You could BSON to achieve this. Here is the code you would use.
//import java.util.ArrayList;
//import org.bson.Document;
//Declare three json object
Document root= new Document();
Document rootSnake = new Document();
Document rootSnakeCase = new Document();
//Add value to the most nested object
rootSnakeCase.append("test","value");
//combine the objects together
if (!rootSnakeCase.isEmpty()){
rootSnake.append("case",rootSnakeCase);
}
if (!rootSnake.isEmpty()){
root.append("snake",rootSnake);
}
//output code
System.out.println(root.toJson());
I need to parse this type of JSON data to java objects:
{"id": 1, "blob": "example text"}
{"id": 2, "blob": {"to": 1234, "from": 4321, "name": "My_Name"}}
I am using Gson, and don't know how to get around this particular problem, of "blob" sometimes being a string and sometimes an object.
One solution to your problem is to write a TypeAdapter for your class, however if you have only cases like that in your example, you can achieve the same result letting Gson do the job for you using the most generic class you can for deserialization.
What I mean is shown in the below code.
package stackoverflow.questions.q19478087;
import com.google.gson.Gson;
public class Q19478087 {
public class Test {
public int id;
public Object blob;
#Override
public String toString() {
return "Test [id=" + id + ", blob=" + blob + "]";
}
}
public static void main(String[] str){
String json1 = "{\"id\": 1, \"blob\": \"example text\"}";
String json2 = "{\"id\": 2, \"blob\": {\"to\": 1234, \"from\": 4321, \"name\": \"My_Name\"}}";
Gson g = new Gson();
Test test1 = g.fromJson(json1, Test.class);
System.out.println("Test 1: "+ test1);
Test test2 = g.fromJson(json2, Test.class);
System.out.println("Test 2: "+ test2);
}
}
and this is my execution:
Test 1: Test [id=1, blob=example text]
Test 2: Test [id=2, blob={to=1234.0, from=4321.0, name=My_Name}]
In second case, blob will be deserialized as a LinkedTreeMap, so you can access its elements using ((Map) test2.blob).get("to") for example;
Let me know if it's enough or if you are interested also in the type adapter solution.
Try this one
Your POJO
class FromToName{
String to;
String from;
String name;
#Override
public String toString() {
return "FromToName [to=" + to + ", from=" + from + ", name=" + name
+ "]";
}
}
Your conversion code
String json ="{\"id\": 1, \"blob\": \"example text\"}";
//String json = "{\"id\": 2, \"blob\": {\"to\": 1234, \"from\": 4321, \"name\": \"My_Name\"}}";
Gson gson = new Gson();
JsonElement element = gson.fromJson (json, JsonElement.class);
JsonObject jsonObj = element.getAsJsonObject();
JsonElement id = jsonObj.get("id");
System.out.println(id);
if(jsonObj.get("blob") instanceof JsonPrimitive ){
JsonElement blob = jsonObj.get("blob");
System.out.println(blob);
}else{
FromToName blob = gson.fromJson (jsonObj.get("blob"), FromToName.class);
System.out.println(blob);
}
If you have any doubt in this let me know
Take that as a JSON Element and then use isMethods() to figure out the type at runtime.
Documentation
JsonParser jp = new JsonParser();
JsonElement ele = jp.parse(jsonString).getAsJsonObject().get("blob");;
if (ele.isJsonObject()) {
//do related stuff here
} else if (ele.isJsonArray()) {
//do related stuff here
}
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());
}
}
}