Extract just one line from a big JSON in Java - 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"));

Related

Is there a way to move a particular JSON key to the top of the JSON which has been created using the Jackson JsonGenerator?

I am creating a JSON using the Jackson JsonGenerator approach and everything seems to work perfectly fine apart from a minor glitch. I would like to move the particular key present in the created JSON to the top of the JSON.
I am creating a key schema which appears at the bottom of the JSON. I would like to know if there is a way to move it to the top of the JSON after finished creating the JSON.
Following is the JSON that I am creating:
public class JacksonTest {
public static void main(String[] args) throws IOException {
StringWriter jsonObjectWriter = new StringWriter();
JsonGenerator jsonGenerator = new JsonFactory().createGenerator(jsonObjectWriter).useDefaultPrettyPrinter();
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("a", "Value-A");
jsonGenerator.writeStringField("c", "Value-C");
jsonGenerator.writeStringField("b", "Value-B");
jsonGenerator.writeStringField("schema", "2.0");
jsonGenerator.close();
jsonGenerator.flush();
System.out.println(jsonObjectWriter.toString());
}
}
Following is the output I am getting:
{
"a" : "Value-A",
"c" : "Value-C",
"b" : "Value-B",
"schema" : "2.0"
}
I would like to convert the JSON something like this:
{
"schema" : "2.0",
"a" : "Value-A",
"c" : "Value-C",
"b" : "Value-B"
}
I would like to fetch the schema from my created JSON and add it to the top of the JSON.
Please Note:
I am aware the JSON order does not matter but I am doing this for better readability purposes. If there is a way then it would be really useful for the reader to understand the JSON better.
I am aware that I can create the schema at first in this code but this is just a sample application that I have provided for ease of understanding. In my real application, I have a Map that is populated dynamically throughout the execution and I am adding it at the end of the JSON creation. If I add at the first then I will miss out on a few of the values which are populated after creation so I am adding at the end but I want this to appear at the top of the JSON so trying to move to the top.
I want to know if there is a direct way to do it rather than looping over the JSON, as my created JSON in the real application can be pretty large.
I would really appreciate it if there was a way to do it or is there any workaround for this.

Query JSON String using Jackson API with good performance

I am working on a use case where I need to parse different JSON strings and query for specific fields basing on the "type" of the JSON. The "type" is a field in the JSON string.
I am using Jackson API to perform this task after going through the blogs and benchmarks, as it is the fastest.
I am able to parse the JSON and achieve what I want but the issue is with the performance.
public String generate(String inputJson, List<String> idParams,final String seperator) throws Exception {
JsonNode node;
StringBuilder sb = new StringBuilder();
try {
node = new ObjectMapper().readTree(new StringReader(inputJson));
idParams.forEach(e -> {
String value = node.findValue(e).asText();
sb.append(value).append(seperator);
});
} catch (Exception e) {
throw e;
}
return sb.toString();
}
In the above method, I am getting the field details as a List. With the help of forEach(), i am able to fetch the values by finding using the fields.
The culprit is the list iterator as I have to search the whole json tree to find the value for each element. Is there a better approach to optimize. I would also like to get the inputs on other JSON parsing libraries which can improve the performance here.
I am also thinking of parsing the whole json once and writing the Keys and Values to a HashMap. But, I have very few fields which i really care about the remaining fields are not needed.
take a look at JsonPath . It offers xpath-like rich query language that allows for search and retrieval of individual or few elements from the JSON tree.
Consider using Jackson Streaming API -
https://github.com/FasterXML/jackson-docs/wiki/JacksonStreamingApi
Take a look at this example -
http://www.baeldung.com/jackson-streaming-api

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.

Strategies for iterating through JsonPath array

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.

How to parse a jsonObject or Array in Java

I have an API request from my CRM that can either return a jsonObject if there is only one result, or a jsonArray if there are multiple results. Here are what they look like in JSON Viewer
JsonObject:
JsonArray:
Before you answer, this is not my design, it's my CRM's design, I don't have any control over it, and yes, I don't like how it is designed either. The only reason I am not storing the records in my own database and just parsing that, which would be MUCH easier, is because my account is having issues not running some workflows that would allow me to auto add the records. Is there any way to figure out if the result is an object or an array using java? This is for an android app by the way, I need it to display the records on the phone.
You should use OPT command instead of GET
JSONObject potentialObject=response.getJsonObject("resuslt")
.getJsonObject("Potentials");
// here use opt. if the object is null, it means its not the type you specified
JSONObject row=potentialObject.optJsonObject("row");
if(row==null){
// row is json array .
JSONArray rowArray=potentialObject.getJsonArray("row");
// do whatever you like with rowArray
} else {
// row is json object. do whatever you like with it
}
ONE
You can use instanceof keyword to check the instances as in
if(json instanceof JSONObject){
System.out.println("object");
}else
System.out.println("array");
TWO
BUT I think a better way to do this is choose to use only JSONArray so that the format of your results can be predicated and catered for. JSONArrays can contain JSONObjects. That is they can cover the scope of JSONObject.
For example when you get the response (either in a JSONObject or a JSONArray), you need to store that in an instance. What instance are you going to store it in? So to avoid issues use JSONArray to store the response and provide statements to handle that.
THREE
Also you can try method overloading in java or Generics
Simplest way is to use Moshi, so that you dont have to parse, even in the case of the Model changing later, you have to change your pojo and it will work.
Here is the snippet from readme
String json = ...;
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);
BlackjackHand blackjackHand = jsonAdapter.fromJson(json);
System.out.println(blackjackHand);
https://github.com/square/moshi/blob/master/README.md

Categories

Resources