Parse json use JsonOjbect vs map into entity with GSon - java

I'm looking for the right way to parse json use GSon library. Up to now, I have known 2 ways to do this:
Assume I have a json string like this:
{
'Events' : [{
'name' : 'exp',
'date' : '10-10-2010',
'tags' : ["tag 1", "tag2", "tag3"]
},...more events...],
'Contacts' : [{
'name' : 'John Smith',
'date' : '10-10-2010',
'tags' : ["tag 1", "tag2", "tag3"]
},...more contacts...],
}
Use JSonOjbect to get field by its name:
JsonElement jelement = new JsonParser().parse(jsonLine);
JsonObject jobject = jelement.getAsJsonObject();
jobject = jobject.getAsJsonArray("Events");
JsonArray jarray = jobject.getAsJsonArray("Contacts");
jobject = jarray.get(0).getAsJsonObject();
Use GSon to map into entity
public class Container{
List<Event> Events;
List<Contact> Contacts;
}
Container c = new GSon().fromJSon(jsonString,Container.class);
Could you tell me when I should use the first way or the second?
I have a web service could return many kinds of complex json string, and I need get data from this. What should I do?

According to the benchmarks, the first approach (GSON_DOM) is faster. This is likely because with the DOM approach you are only deserializing part of the JSON string. If you wanted even more performance you could switch to the GSON_STREAM approach which seems to do best in the benchmarks.
Practically, the second approach makes for simpler code. I would use probably use that approach first and then switch to one of the other methods if I find that JSON deserialization is taking a significant amount of time.

Related

Why can't read a .json?

I want to read a .json using library json-simple, my json file is:
{
"Subjects": {
"subject1": "MIS",
"subject2": "DBMS",
"subject3": "UML"
}
}
And my code is:
import java.io.*;
import java.util.*;
import org.json.simple.*;
import org.json.simple.parser.*;
public class JSONReadFromTheFileTest {
public static void main(String[] args) {
JSONParser parser = new JSONParser();
try{
Object obj = parser.parse(new FileReader("/Users/User/Desktop/course.json"));
JSONObject jsonObject = (JSONObject)obj;
JSONArray subjects = (JSONArray)jsonObject.get("Subjects");
Iterator iterator = subjects.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}catch(Exception e){
e.printStackTrace();
}
}
}
I would like to get in console:
subject1: MIS
subject2: DBMS
subject3: UML
But instead of that, I just get the next error:
java.lang.ClassCastException: org.json.simple.JSONObject cannot be cast to org.json.simple.JSONArray
at project.Main(Main.java:11)
But I searched in internet, and I found that if I change sintaxys of the .json in the next way:
{
"Subjects": [
"subject1: MIS",
"subject2: DBMS",
"subject3: UML"
]
}
I get in console what I want:
subject1: MIS
subject2: DBMS
subject3: UML
And you may think my problem is solved, but not yet, because I want to read the json file in the first way.
I hope someone can help me. Thanks in advance.
The first example shows a Subjects key containing a single object with several properties (subject1, subject2, etc).
Consider those properties like the member variables of a class.
In order to let you better understand if the class is Person those variables could be name and surname.
What you try to achieve in your code is extracting a JSONArray from the JSON you are providing.
Going back to the example for Person the array could be - sorry for the silly example - an Array containing phone numbers.
So what you are expecting is that one of the member properties of the class is an array.
This is not possible in the first example because the first example does not contain a json array.
This line extracts the whole json object:
JSONObject jsonObject = (JSONObject)obj;
This one tries to get an array out but no array is there in the first example:
JSONArray subjects = (JSONArray)jsonObject.get("Subjects");
Square brackets represent an array, which you've casted the getter into.
The second json shown is more correct for the code you've written, however, arrays cannot hold key-value pairs, so that's why you've made second JSON have an array of strings
To parse the first file, you'd need to start with
JSONObject subjects = (JSONObject)jsonObject.get("Subjects")
If you have full control over the file, I'd suggest just storing ["MIS", "DBMS", "UML"] then write a simple loop
for (int i = 0; i < jsonArray.length; i++) {
System.out.printf("subject%d: %s%n", i+1, jsonArray.get(i));
}
To make your code work with your json, you should not use JSONArray for "Subjects" but instead JSONObject. The reason is that [ and ] indicates beginning and ending of array element in json which is represented by JSONArray. If you have { and } element then its object represented by JSONObject. zz So replace this:
JSONArray subjects = (JSONArray)jsonObject.get("Subjects");
Iterator iterator = subjects.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
with following (I have not compiled it):
JSONObject subjects = (JSONObject)jsonObject.get("Subjects");
for(Iterator iterator = subjects.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
System.out.println(key + ": " + subjects.get(key));
}
As others mentioned you should replace JSONArray with JSONObject in your code. But I would suggest to switch to different JSON library all together. I would recommend to use Json-Jackson library. Also, there is another library that provides a wrapper over Json-Jackson library. It has JsonUtils class that can simply parse your JSON in one line. Just read first your Json from file into a String jsonString and do this.
Map<String, Object> myMap = JsonUtils.readObjectFromJsonString(jsonString, Map.class);
You will get a Map with one key "Subjects" that will have a value of a Map with your 3 keys and values:
subject1: MIS
subject2: DBMS
subject3: UML
Here is the JavaDoc for the JsonUtils class. The library could be found as Maven artifact and on Github (including source code and Javadoc).

Using group-by type of operation on JSON objects

I have JSON data in the following format.
[{
"id": 16966,
"post": "This is about road!",
"category": "road",
},
.
.
.]
I want to group JSON entries according to their categories. So, I will get all road related entries in one datastructure, (say list). I know that I can put the data into Mongo DB or even a relational database and do querying. Without doing that, is there some easy method to do this?
If you gonna read the entire JSON file, an easy way is to first read all the entries into a List<Data> and then group them in a Map<String, List<Data>>.
class Data {
private int id;
private String post;
private String category;
//getter, equals, hashcode, toString, etc.
}
and then:
public class Test {
public static void main(String args[] ) throws Exception {
Gson gson = new Gson();
List<Data> list = gson.fromJson(new BufferedReader(new FileReader("myJson.json")), new TypeToken<List<Data>>(){}.getType());
Map<String, List<Data>> groupedMap = list.stream().collect(Collectors.groupingBy(Data::getCategory));
groupedMap.forEach((k, v) -> System.out.println(k + " => " + v));
}
}
which outputs:
road => [Data [id=16966, post=This is about road!, category=road], Data [id=16965, post=This is about road!, category=road]]
land => [Data [id=16961, post=This is about land!, category=land]]
I added some entries to the file. I guess you could write your own deserializer too to get rid of the step when you have a temporary list and store directly in the map, but you asked for an easy way :-). Also note that I'm using java-8 and Gson, but you can also achieve this without it (but you'll write more code).
Take a look on JXPath. This library allows running XPath queries on collections of java objects. So, you can map JSON to your java model using one of popular JSON parser (e.g. Jackson or Gson) and then JXPath to run XPath queries on your collection.
For more information refer here: http://commons.apache.org/proper/commons-jxpath/

GSON 2.0+ - Deserialize and parse arbitrary fields

I am pulling a JSON string from a web service, that is styled in the version below. The JSON strings fields for the id's displayed would be arbitrary depending on what was sent in the get. I have followed some examples with gson where they talk about taking the json and running it into an object to handle the arbitrary field values.
How to decode JSON with unknown field using Gson? and Dealing with randomly generated and inconsistent JSON field/key names using GSON
My question is, what do I do with the object once I create it to pull out specific fields. I had a custom class to pull the values from the inside of the object (id, name, profile) etc, but I'm lost in how I would be able to reference the two, or how I would extract the information from the object into an arraylist or something of the sort.
{
"415943": {
"id": 415943,
"name": "Zoro Roronoa",
"profileIconId": 580,
"revisionDate": 1390848107000,
"summonerLevel": 30
},
"19758386": {
"id": 19758386,
"name": "Zoro",
"profileIconId": 535,
"revisionDate": 1390855130000,
"summonerLevel": 30
}
}
Main.java
Gson gson = new Gson();
Object o = gson.fromJson(jsonStatsString, Object.class);
You can use your custom object that contains the mapped fields in the JSON, and have them converted to a List for you:
List<MyObject> items =
gson.fromJson(json, new TypeToken<List<MyObject>>() { }.getType());

How can i sort Nested JSON Array?

How can i sort Nested JSON Array? Like for a JSON below...
{
"id":"rtmc05.lax.someabc.net",
"name":"rtmc05.lax.someabc.net",
"tenants":[{
"id":"rtmc",
"name":"rtmc"
},{
"id":"hrs",
"name":"hrs"
},{
"id":"amotelbe1",
"name":"amotelbe"
},{
"id":"cds",
"name":"cds"
},{
"id":"idx-server",
"name":"idx-server",
"tenants":[{
"id":"amotelbe",
"name":"amotelbe",
"tenants":[{
"id":"amotelui",
"name":"amotelui"
}]
}]
}]
}
There's a few parts implicit to your question, and it's not clear where you're having trouble:
How do you take a JSON string and make usable Java objects out of it. (I'm assuming Java, not JavaScript, since you've tagged your question with "java".)
How do you sort those objects after they're made?
How do you handle sorting the nested parts? (In your example, "idx-server" has sub-tenants.)
Not sure exactly which parts of this you're having trouble with, so here's some notes for all three.
Part 1: Getting Java objects
I agree with the other guy that Jackson is a good JSON parser to use. Here's a couple lines of code you could use to parse some JSON:
String jsonString = "..."; // Load this in whatever way makes sense for you
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> parsedJson = mapper.readValue(jsonString, Map.class);
If your JSON string is really huge, then there are other readValue overloads that you can use to avoid reading the whole String into memory.
Part 2: Sorting Java objects
Once you've got the parsed JSON, sorting is just a matter of calling Collections.sort(...), passing in the tenants array. Plus you'll need to write a Comparator that defines the ordering that you want. For example, here's a comparator that sorts by name:
public class NameComparator implements Comparator<Map<String,Object>> {
public int compare(Map<String,Object> o1, Map<String,Object> o2) {
String name1 = (String) o1.get("name");
String name2 = (String) o2.get("name");
return name1.compareTo(name2);
}
}
Then you get the tenants array out (Jackson makes them into ArrayList objects) and call Collections.sort(...). For example,
List<Map<String,Object>> tenants =
(List<Map<String,Object>>) parsedJson.get("tenants");
Collections.sort(tenants, new NameComparator());
Part 3: Handling the nesting
The clearest way to do this is to add some extra code to walk through your JSON looking for any object with a tenants array, and sort it. For example, here's a recursive function that should do it:
public static void recursiveSortTenants(Map<String,Object> jsonObject) {
List<Map<String,Object>> tenants =
(List<Map<String,Object>>) jsonObject.get("tenants");
if (tenants != null) {
Collections.sort(tenants, new NameComparator());
// For each tenant, see if it has sub-tenants. If so,
// call this function again to sort them.
for (Map<String,Object> tenant : tenants) {
if (tenants.containsKey("tenants")) {
recursiveSortTenants(tenant);
}
}
}
}
Hope this helps!
Parse it into (javascript) objects then write a sort function that sorts an array of such javascript objects.
Deserialize it to POJOs(with Gson or Jackson), and write a Comparator for that POJOs.

From a given Map to a json string (or JSONObject)

In Java, I've these key/value pairs passed by post (for example):
form_id=undefined
frmb[0][cssClass]=input_text
frmb[0][required]=checked
frmb[0][values]=First Name
frmb[1][cssClass]=input_text
frmb[1][required]=checked
frmb[1][values]=Last Name
frmb[2][cssClass]=textarea
frmb[2][required]=undefined
frmb[2][values]=Bio
frmb[3][cssClass]=checkbox
frmb[3][required]=undefined
frmb[3][title]=What's on your pizza?
frmb[3][values][2][baseline]=undefined
frmb[3][values][2][value]=Extra Cheese
frmb[3][values][3][baselise]=undefined
frmb[3][values][3][value]=Pepperoni
frmb[3][values][4][baseline]=undefined
frmb[3][values][4][value]=Beef
frmb[4][cssClass]=radio
frmb[4][required]=undefined
frmb[4][title]=Do you like pizza?
frmb[4][values][2][baseline]=checked
frmb[4][values][2][value]=Yes
frmb[4][values][3][baseline]=undefined
frmb[4][values][3][value]=No
frmb[5][cssClass]=select
frmb[5][multiple]=checked
frmb[5][required]=checked
frmb[5][title]=Select a pizza type:
frmb[5][values][2][baseline]=checked
frmb[5][values][2][value]=Margherita
frmb[5][values][3][baseline]=undefined
frmb[5][values][3][value]=Napoli
frmb[5][values][4][baseline]=undefined
frmb[5][values][4][value]=Marinara
I've to create the following json:
[{"cssClass":"input_text","required":"checked","values":"First Name"},{"cssClass":"input_text","required":"checked","values":"Last Name"},{"cssClass":"textarea","required":"undefined","values":"Bio"},{"cssClass":"checkbox","required":"undefined","title":"What's on your pizza?","values":{"2":{"value":"Extra Cheese","baseline":"undefined"},"3":{"value":"Pepperoni","baseline":"undefined"},"4":{"value":"Beef","baseline":"undefined"}}},{"cssClass":"radio","required":"undefined","title":"Do you like pizza?","values":{"2":{"value":"Yes","baseline":"checked"},"3":{"value":"No","baseline":"undefined"}}},{"cssClass":"select","required":"checked","multiple":"checked","title":"Select a pizza type:","values":{"2":{"value":"Margherita","baseline":"checked"},"3":{"value":"Napoli","baseline":"undefined"},"4":{"value":"Marinara","baseline":"undefined"}}}]
How could I do?
I don't succeed in parsing the keys, grouping the elements which do part of the same JSONObject.
You can use the Gson library for this. It does not support serializaton of nested maps (I assume your output represents a nested map). You can write a custom serializer/deserializer or create your own serialization method following the instructions in this thread
You can do something like this:
JSONArray jsonItems = new JSONArray();
for (int i = 0; i < frmb.size(); i++) {
JSONObject json = new JSONObject();
json.put("cssClass", frmb[i][cssClass]);
json.put("required",frmb[i][required]);
//put json object to json array
jsonItems.put(json);
}
Check here for more details
I resolved by parsing the keys and by building the JSONObjects with the use of two temporary hashtables.

Categories

Resources