Strategies for iterating through JsonPath array - java

I just started exploring JsonPath today. I want to explore not just what's possible to do with it, but some effective strategies.
For instance, let's say I have to iterate through an array contained within one element in the json string.
I'm using the "store" example from https://github.com/jayway/JsonPath#path-examples .
To get the list of books itself, I would imagine I could do something like this:
List<?> allBooks = JsonPath.<List<?>>read(context, "$.store.book");
Does it make sense to think about it this way?
It's the logic for iterating through this that I'm uncertain about.
I would have thought I could define a "Book" pojo and then do something like this:
for (int ctr = 0; ctr < allBooks.size(); ++ ctr) {
Book book = JsonPath.<Book>read(context, ".[" + ctr + "]");
System.out.println("book[" + book + "]");
}
However, this doesn't work. The "read" method at this point returns a JSONArray.
The last line in the code sample at https://github.com/jayway/JsonPath#what-is-returned-when is close to what I'm looking at, but this requires parsing the json in every iteration. It seems like the "DocumentContext" class has "read" methods that can take a type parameter, but not "JsonPath".
What are some reasonable strategies for navigating something like this?

JSON path will just return you a list of Maps as you've no doubt already seen. You need a way to tell it how to map these values to an object - for this you will need a custom configuration. There are other providers like Gson etc., but I've only used Jackson.
Configuration configuration = Configuration
.builder()
.jsonProvider(new JacksonJsonProvider())
.mappingProvider(new JacksonMappingProvider())
.build();
The second step is to specify generic type information with a TypeRef and pass it along when reading the tag.
List<Book> allBooks = JsonPath.using(configuration)
.parse(context)
.read("$.store.book", new TypeRef<List<Book>>() {});
As a result you get a nice list of Book objects.

Related

Shortest possible way to get "id" from JSONObject

I have a org.json.JSONObject like below:
{"entries":
[{"entry":{"createdAt":"2020-06-25T18:10:22.571+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Administrator","id":"admin"},"modifiedAt":"2020-09-28T15:42:50.253+0000","modifiedByUser":{"displayName":"Administrator","id":"admin"},"name":"1000024_Resume 1-K_User1 (2020-1601307769393).doc","id":"a9aa23ac-3cca-4fd7-9f82-ec31c2b969f0","nodeType":"hr:HR_Type","content":{"sizeInBytes":48128,"mimeTypeName":"Microsoft Word","mimeType":"application/msword","encoding":"UTF-8"},"parentId":"7db2d13f-db92-4401-aff1-cecddd78db45"}},
{"entry":{"createdAt":"2020-06-25T18:10:23.014+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Administrator","id":"admin"},"modifiedAt":"2020-07-10T20:40:33.123+0000","modifiedByUser":{"displayName":"Sarah Campbell","id":"SACAMPBELL"},"name":"Test.DOC","id":"29cfee8d-5614-4c81-9bfa-581334cc39e9","nodeType":"hr:Test_Type","content":{"sizeInBytes":35328,"mimeTypeName":"Microsoft Word","mimeType":"application/msword","encoding":"UTF-8"},"parentId":"79d3b939-b7e9-4bed-be67-428eb5da0f16"}},
{"entry":{"createdAt":"2020-07-10T15:06:06.252+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Test Display","id":"CN158931"},"modifiedAt":"2020-09-28T15:39:40.349+0000","modifiedByUser":{"displayName":"Administrator","id":"admin"},"name":"1000536_Test Display September 2014.doc","id":"9eea5068-48dc-4e1f-9a19-e7d9749ba3db","nodeType":"hr:Test_Type","content":{"sizeInBytes":58243,"mimeTypeName":"Microsoft Word","mimeType":"application/msword","encoding":"UTF-8"},"parentId":"79d3b939-b7e9-4bed-be67-428eb5da0f16"}},
{"entry":{"createdAt":"2020-07-10T21:09:50.889+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Test Display-Name","id":"CN103107"},"modifiedAt":"2020-07-10T21:11:26.528+0000","modifiedByUser":{"displayName":"Some-CGT12","id":"CN103107"},"name":"Test Display123.jpg","id":"df7c76a1-67b9-4673-8fb7-1a2470d42c1d","nodeType":"hr:Test_Type","content":{"sizeInBytes":237560,"mimeTypeName":"JPEG Image","mimeType":"image/jpeg","encoding":"UTF-8"},"parentId":"7db2d13f-db92-4401-aff1-cecddd78db45"}},
{"entry":{"createdAt":"2020-07-10T21:09:51.706+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Test Display-Name","id":"CN103107"},"modifiedAt":"2020-07-10T21:09:51.706+0000","modifiedByUser":{"displayName":"Some-TEst2","id":"CN103107"},"name":"batman.jpg","id":"88ac8b96-5965-4668-9e94-2b2e3509e0f8","nodeType":"hr:HR_Type","content":{"sizeInBytes":5588,"mimeTypeName":"JPEG Image","mimeType":"image/jpeg","encoding":"UTF-8"},"parentId":"79d3b939-b7e9-4bed-be67-428eb5da0f16"}}]
,"pagination":{"maxItems":100,"hasMoreItems":false,"totalItems":5,"count":5,"skipCount":0}}
How do I get the value for all "id"s (a9aa23ac-3cca-4fd7-9f82-ec31c2b969f0, 29cfee8d-5614-4c81-9bfa-581334cc39e9, 9eea5068-48dc-4e1f-9a19-e7d9749ba3db, df7c76a1-67b9-4673-8fb7-1a2470d42c1d, 88ac8b96-5965-4668-9e94-2b2e3509e0f8) from above without using for loops. That means I dont want to iterate through all the objects and then get "id" value.
I am trying to do something like:
org.json.JSONObject myJSONObject = new org.json.JSONObject(response.getBody()).getJSONObject("entries").getJSONObject("entry").getString("id");
If possible, I am looking for one-liner using Java 1.6/1.7/1.8 for above.
I am not sure if there is a single liner solution, but it becomes very easy using JSON PATH library
https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path
Configuration cf = Configuration.builder().options(Option.SUPPRESS_EXCEPTIONS).build();
DocumentContext ctx = JsonPath.using(cf).parse(jsonStr);
List<String> ids = ctx.read("$.entries[*].entry.id");

Storing values of an array on a separate file as the array values changes

So I am running some code which runs over 300k times. Each time this code runs, it returns up to 300k values. I am currently storing the results I get in an ArrayList:
List<List<Object>> thisList = new ArrayList<List<Object>();
for (int i = 0; i < 300000; i++) {
thisList.add(new ArrayList<Object>());
}
for (int i = 0; i < 300000; i++) {
List<Object> result = someCode();
for (Object obj : result) {
thisList.get(obj.id).add(obj.value);
}
}
In this code, everytime obj is obtained, it has a value obj.id which specifies the index in the List where obj.value has to be stored.
What would be the most efficient way to store the results elsewhere as the search continues? My code seems to stop working past iteration 400, most likely due to low memory issues. I have considered using a simple text document where each line represents a List<Object> but through some Googling, it seems there is no way to append to a specific line, and all suggestions seems to point towards overwriting the entire text document. I've never worked with databases before which is why I am trying to avoid that for now.
Would appreciate if someone can give me suggestions on what I could do.
Edit: Is there a method which does not use a database, where after each iteration of the outer for loop, the data can be stored?
For example, given a file which currently contains
List 0: obj.value1 obj.value2
List 1: obj.value1 obj.value4
...
List 300000: obj.value3 obj.value8
and result contains
{obj<1, 100>, obj<0, 3>, ...}
where each object is of the form obj<id, value>, the file becomes
List 0: obj.value1 obj.value2 obj.value3
List 1: obj.value1 obj.value4 obj.value100
...
List 300000: obj.value3 obj.value8
You could store it in an XML file using JAXB api
Here is a link with a little tutorial on JAXB:
https://dzone.com/articles/using-jaxb-for-xml-with-java
Or you could also store it in a JSON file usin json-simple api
Here's another little tutorial:
https://stackabuse.com/reading-and-writing-json-in-java/
These are the links to download JAXB and json-simple from maven:
JAXB: https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api
json-simple: https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple
Hope it'll be useful to you

Deserialize/Parse JSON in Java

I am searching a good and dynamic way to parse JSON in Java.
I've seen things such as:
List<String> list = new ArrayList<String>();
JSONArray array = obj.getJSONArray("test");
for(int i = 0 ; i < array.length() ; i++){
list.add(array.getJSONObject(i).getString("testKey"));
}
But that's not what I'm searching. In C# I had something like that:
dynamic results = JsonConvert.DeserializeObject<dynamic>(json);
info.Text = results["test"]["testKey"];
Here's an example of my JSON:
{"date":"07.05.2017 11:44",
"monday":{"1":{"subject":"test","room":"test","status":"test"}}}
So for example I would like to make:
results["monday"]["1"]["subject"];
I hope someone understands my problem and can help me.
Thanks in advance!
The core Java runtime does not offer a JSON parser (edit: technically, it does, see bottom of answer), so you will need a library. See Jackson, Gson, perhaps others.
Even with that, you will not get the dynamic features you want, because Java is statically typed. Example with Jackson:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
map.get("monday").get("1").get("subject");
^^^
This fails because the result of get("monday") is Object, not Map
The "right" approach in Java-land, would be to create a class (or set of classes) that represents your JSON model, and pass it to the JSON parser's "object mapper". But you said "dynamic" so I'm not exploring this here.
So you'll need to cast to Map when you know it's not primitive values:
((Map<String,Map<String,String>>)map.get("monday")).get("1").get("subject");
This works but with a warning about unchecked cast...
All in all, Java is not a dynamic language and I see no way to do exactly what you want (perhaps I'm missing approaches that are still slightly easier than what I have suggested).
Are you limited to Java-the-language or Java-the-platform? In the latter case you can use a dynamic language for the Java platform, such as Groovy, who has excellent features to parse JSON.
EDIT: a funny alternative is to use Java's own JavaScript implementation. This works and is easy and dynamic, but I don't know if it's "good":
String json = "{\"date\":\"07.05.2017 11:44\",\n" +
"\"monday\":{\"1\":{\"subject\":\"test\",\"room\":\"test\",\"status\":\"test\"}}}";
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.put("data", json);
System.out.println(engine.eval("JSON.parse(data)['monday']['1']['subject']"));
If you are sure about the value you want to get then you can do following as well :
String str = "{\"date\":\"07.05.2017 11:44\", \"monday\":{\"1\":{\"subject\":\"test\",\"room\":\"test\",\"status\":\"test\"}}}";
JSONObject results= new JSONObject(str);
String str1 = results.getJSONObject("monday").getJSONObject("1").getString("subject");
System.out.println(str1);
For array kind of results, we have to write logic for that. In this case org.json library is used.
You can use GCON library:
https://github.com/google/gson
Very good for parsing JSON objects.

Extract just one line from a big JSON in Java

Since my very first days of Java + JSON I tried to extract just some certain parts of a JSON.
But no matter if which of the libraries I used:
Gson
json-simple
javax.json
it never was possible to make it quick and comfortable. Mostly for easy task or even prototyping. It already cost me many hours of different approaches.
Going trough the hierarchy of an JSON
Object jsonObject = gson.fromJson(output, Object.class);
JsonElement jsonTree = gson.toJsonTree(jsonObject);
JsonArray commitList = jsonTree.getAsJsonArray();
JsonElement firstElement = commitList.get(0);
JsonObject firstElementObj = firstElement.getAsJsonObject();
System.out.println(firstElementObj.get("sha"));
JsonElement fileList = firstElementObj.get("files");
This is dirty code for a reason. It shows how many early approaches looks like and how many people cannot achieve it to do it better early.
Deserializing JSON to a Java Object
Your have to analyse the complete JSON to create an complete Java-Object representation just to get access to some single memebers of it. This is a way I never wanted to do for prototyping
JSON is an easy format. But using libraries like that is quite difficult and often an problem for beginner. I've found several different answers via Google and even StackOverflow. But most were quite big larged which required to create a own specific class for the whole JSON-Object.
What is the best approach to make it more beginner-friendly?
or
What is the best beginner-friendly approach?
Using Jackson (which you tagged), you can use JsonPointer expressions to navigate through a tree object:
ObjectMapper mapper = new ObjectMapper();
JsonNode tree = mapper
.readTree("[ { \"sha\": \"foo\", \"files\": [ { \"sha\": \"bar\" }, { \"sha\": \"quux\" } ] } ]");
System.out.println(tree.at("/0/sha").asText());
for (JsonNode file : tree.at("/0/files")) {
System.out.println(file.get("sha").asText());
}
You could also use the ObjectMapper to convert just parts of a tree to your model objects, if you want to start using that:
for (JsonNode fileNode : tree.at("/0/files")) {
FileInfo fileInfo = mapper.convertValue(fileNode, FileInfo.class);
System.out.println(fileInfo.sha);
}
If your target class (FileInfo) specifies to ignore unknown properties (annotate target class with #JsonIgnoreProperties(ignoreUnknown = true) or disable DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES on the mapper), then you can simply declare the properties you are interested in.
"Best" is whatever works to get you going.
Generate Plain Old Java Objects from JSON or JSON-Schema
One little helper I found via my research was an Online-Tool like
http://www.jsonschema2pojo.org/
This is a little help, when you know about that. But the negative side I mentioned at point 2 is still there.
You can use JsonSurfer to selectively extract value or object from big json with streaming JsonPath processor.
JsonSurfer jsonSurfer = JsonSurfer.gson();
System.out.println(jsonSurfer.collectOne(json, "$[0].sha"));
System.out.println(jsonSurfer.collectOne(json, "$[0].files"));

How to use an array value as field in Java? a1.section[2] = 1;

New to Java, and can't figure out what I hope to be a simple thing.
I keep "sections" in an array:
//Section.java
public static final String[] TOP = {
"Top News",
"http://www.mysite.com/RSS/myfeed.csp",
"top"
};
I'd like to do something like this:
Article a1 = new Article();
a1.["s_" + section[2]] = 1; //should resolve to a1.s_top = 1;
But it won't let me, as it doesn't know what "section" is. (I'm sure seasoned Java people will cringe at this attempt... but my searches have come up empty on how to do this)
Clarification:
My article mysqlite table has fields for the "section" of the article:
s_top
s_sports
...etc
When doing my import from an XML file, I'd like to set that field to a 1 if it's in that category. I could have switch statement:
//whatever the Java version of this is
switch(section[2]) {
case "top": a1.s_top = 1; break;
case "sports": a1.s_sports = 1; break;
//...
}
But I thought it'd be a lot easier to just write it as a single line:
a1["s_"+section[2]] = 1;
In Java, it's a pain to do what you want to do in the way that you're trying to do it.
If you don't want to use the switch/case statement, you could use reflection to pull up the member attribute you're trying to set:
Class articleClass = a1.getClass();
Field field = articleClass.getField("s_top");
field.set(a1, 1);
It'll work, but it may be slow and it's an atypical approach to this problem.
Alternately, you could store either a Map<String> or a Map<String,Boolean> inside of your Article class, and have a public function within Article called putSection(String section), and as you iterate, you would put the various section strings (or string/value mappings) into the map for each Article. So, instead of statically defining which sections may exist and giving each Article a yes or no, you'd allow the list of possible sections to be dynamic and based on your xml import.
Java variables are not "dynamic", unlink actionscript for exemple. You cannot call or assign a variable without knowing it at compile time (well, with reflection you could but it's far to complex)
So yes, the solution is to have a switch case (only possible on strings with java 1.7), or using an hashmap or equivalent
Or, if it's about importing XML, maybe you should take a look on JAXB
If you are trying to get an attribute from an object, you need to make sure that you have "getters" and "setters" in your object. You also have to make sure you define Section in your article class.
Something like:
class Article{
String section;
//constructor
public Article(){
};
//set section
public void setSection(Section section){
this.section = section;
}
//get section
public String getSection(){
return this.section;
}

Categories

Resources