I have a json that looks something like this
{
"a": {
"b": [
{
"c": {
"d": [{ "f": "value" }]
}
},
{
"c": {
"d": [{ "f": "value" }]
}
}
]
}
}
I'm looping the data inside b using the following code, then getting the array at d again in a nested loop
for (JsonNode abNode : rootNode.at("/a/b")) {
for (JsonNode cdNode : abNode.at("/c/d")) {
//cdNode.get("f")
}
}
Inside the for loop how can I get the path of something like node.get("f") so that I would get /a/b/0/c/d/0/f and then /a/b/1/c/d/0/f? Does jackson have something to get this or another library? The only thing I can think of right now is just switching to a for i=0 loop
What you are trying to do is also called as Xpath in technical terms.
It is used for html, xml based languages as well as now available in json
You can try jsonpath for this case:
https://www.baeldung.com/guide-to-jayway-jsonpath
https://github.com/json-path/JsonPath
Google's popular GSON library has a method, namely getPath, maybe useful for your purpose:
String json = "...";
JsonReader reader = new JsonReader(new StringReader(json));
System.out.println(reader.getPath());
Related
I'm currently working on a diagram / tree graph generator, to achieve this I'm using two libraries: GraphView to generate the graph and ZoomLayout to move around the view. The main idea of this project is to save all JSON's within an AWS database and then load a list of all the created graphs.
Since the GraphView library doesn't have capability to change or add data from the nodes I decided to create a JSON parser in order to notify new changes and redraw the shape of the graph. So far I managed to create a JSON parser that can read the following format.
example.json
{
"name": "A",
"children": [
{
"name": "B",
"children": [
{
"name": "G",
"children": [
{}
]
}
]
},
{
"name": "C",
"children": [
{
"name": "D",
"children": [
{
"name": "E",
"children": [
{}
]
},
{
"name": "F",
"children": [
{}
]
}
]
}
]
}
]
}
The parser uses a class to iterate over all the nodes within the JSON string named Nodes.
Nodes.kt
class Nodes(
var name: String,
val children: MutableList<Nodes>
){
override fun toString(): String {
return "\nName:$name\nChildren:[$children]"
}
fun hasChildren(): Boolean {
return !children.isNullOrEmpty()
}
}
With that JSON, the app generates the following graph:
The problem
Within this section you can enter a new string which will replace the current one in the selected node. This is done by editing the string without any mapping, using the String.replace() method. But this method doesn't allow me to erase or add new nodes to the current JSON string.
To map the JSON properly I decided to make use of GSON and a MutableList. First I set up the MutableList with the data from the current JSON and then I add a new node in front of the clicked node. The issue is that when I try to print the MutableList as a string the app throws an stackoverflow. This also happens if I try to map it to JSON format using GSON.
This the code that I use to replace the JSON.
// Method used to replace the current JSON with a new one by replacing the selected node with new data
private fun replaceJson(oldData: String, newData: String): Graph {
newGraph = Graph()
newStack.clear()
mNodesList.clear()
val gson = Gson()
var mappedNodes: Nodes = gson.fromJson(json, Nodes::class.java)
val mapper = ObjectMapper()
newStack.push(mappedNodes)
while (newStack.isNotEmpty()) {
replaceData(newStack.pop(), oldData, newData)
}
var position = -1
for(element in mNodesList){
if(element.name == currentNode!!.data.toString()){
println("Adding new node to ${mNodesList.indexOf(element)}")
position = mNodesList.indexOf(element)
}
}
mNodesList.add(position + 1, Nodes(newData, mNodesList))
for(node in mNodesList){
println(node.name)
}
//Stackoverflow
// println(mNodesList.toString())
//Stackoverflow
// val newJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mNodesList)
// println("json::: \n $newJson")
json = json.replace(oldData, newData, ignoreCase = false) //WIP Not final
return newGraph
}
// This method replaces some node data with the newly entered data
// this method uses recursivity to load all children and names in order
private fun replaceData(nodes: Nodes, oldData: String, newData: String) {
for (node in nodes.children) {
if (node.hasChildren()) {
if (node.name == oldData) {
mNodesList.add(node)
newGraph.addEdge(Node(nodes.name), Node(newData)) //<--- replaces data
newStack.push(Nodes(newData, node.children))
} else {
mNodesList.add(node)
newGraph.addEdge(Node(nodes.name), Node(node.name))
newStack.push(node)
}
}
}
}
I read some posts where people uses HashMaps but I'm quite lost and I don't think I understand how JSON mapping works.
Summary
I'm looking for a way to add and delete nodes from the string (JSON) provided above, but I don't quite know how to fix what I already have. It's the first time I'm working with JSON and Lists with Kotlin so I would greatly apreciate any information or help, any insights on how to improve or workaround will also be apreciated.
If anyone wants to see the code it's currently public in my GitHub repository.
PD: I tried providing as much information as possible, if the question is still unclear I will try to improve it.
In case anyone is in a similar situation, here's the solution I came up with.
I ended up simplifying the JSON structure I was using since having a nested JSON was giving me so many problems. I decided to link children and parents in another way. This is the current JSON structure:
{
"nodes": [
{
"data": "A",
"parent": "root"
},
{
"data": "B",
"parent": "A"
},
{
"data": "C",
"parent": "A"
},
{
"data": "G",
"parent": "B"
},
{
"data": "D",
"parent": "C"
},
{
"data": "E",
"parent": "D"
},
{
"data": "F",
"parent": "D"
},
{
"data": "H",
"parent": "F"
},
{
"data": "I",
"parent": "H"
},
{
"data": "J",
"parent": "I"
},
{
"data": "K",
"parent": "J"
}
]
}
I also remade my Nodes class, and separated it two parts: Nodes.kt and SingleNode.kt.
Now the Nodes class only contains a list of SingleNode, and SingleNode contains the data of the node and its parent.
/**
* This class represets all the nodes
* #param nodes represents a list of all the existing nodes
*/
class Nodes(var nodes: List<SingleNode>)
/**
* This class represents the instance of a single node
* #param data name of the node
* #param name of its parent or upper node
*/
class SingleNode(var data: String, var parent: String)
Once I had those classes, I used the GSON library to map the JSON string into a Nodes object.
val tree: Nodes = gson.fromJson(json, Nodes::class.java)
With this structure I was able to map the nodes into a LinkedHashMap, which I can then use to add, remove or edit any key and value (which represent the name of the node and the parent).
By using a mutableListOf<SingleNode> and GSON I can then recreate a JSON based on the previously modified HashMap.
Is there a library or a simple recursive way to get all the property values (without property names or json specific characters) from a random json?
For example, from this object:
{
"a": "aVal",
"b": {
"b1": "b1Val"
},
"c": [
"cVal",
{
"c1":"c1Val"
},
[["c3Val"]]
]
}
I need the values marked with the Val suffix: aVal, b1Val, cVal, c1Val, c3Val
You can use org.json library.
pass the JSON string in a constructor and work with objects after that. Info
Example:
JSONObject root = new JSONObject("{\"a\": \"aVal\",\"b\": {\"b1\": \"b1Val\"},\"c\": [\"cVal\",{\"c1\":\"c1Val\"},[[\"c3Val\"]]]}");
for (Object objKey : root.names()) {
//do things here..
}
I am trying to define java generic type in method and I am not able to do so. I went through lot of posts but haven't figured it out.
I have a JSON which will be converted to java LinkedHashMap by Mule Dataweave. Here is simple JSON
{
"a": {
"b": {
"c": [{
"name": "abc"
},
{
"name": "xyz"
}
],
"d": "e"
},
"f": "g"
}
}
Now I want to use that JSON converted to LinkedHashMap in java method.
I tried something like
public void test(LinkedHashMap<String, LinkedHashMap<String, LinkedHashMap>> payload)
but the value can be recursive LinkedHashMap until I get key and value as String. I don't know how deep it can go as it is based on JSON response. How can I define it in java generics?
As Java Does not support Union Types you can not model this other than
public void test(LinkedHashMap<String, Object> payload)
Where you know that an Object is Either a Map<> or String or List.
Simply put, how do I retrieve {"value1":123"} using Jackson in a non-chaining way?
{
"aaa": [
{
"value1": "123"
}
],
"bbb": [
{
"value2": "456"
}
]
}
I tried using:
jsonNode.at("/aaa[Array][0]) but i get missing node in response.
Any help would be good.
The correct json path expression would be "/aaa/0/value1"
Use:
jsonNode.at("/aaa/0/value1")
use this below code :
JsonNode node = mapper.readTree(json);
System.out.println(node.path("aaa").get(0)); // {"value1":"123"}
use jackson-databind.
use this
node.path("aaa").get(0).get("value1") // 123.
Using node.path("aaa").get(0) is what retrieved the first item from array. Any other ideas like node.path("aaa[0]") or node.path("aaa/0") do not work.
I have a situation where I want to retrieve an array from JSON output. The array and its respective members never change. However, the hierarchy of the JSON output that contains the array does change periodically (beyond my control). I am struggling to figure out how to reliably extract the array without knowing the structure of the JSON hierarchy.
Example:
In JSON output #1 we have a bunch of objects with a sailors array nested snuggly in between:
{
"eat": "grub",
"drink": "rum",
"sailors": [
{
"firstName": "John",
"lastName": "Doe"
},
{
"firstName": "Anna",
"lastName": "Smith"
},
{
"firstName": "Peter",
"lastName": "Jones"
}
]
}
In JSON output #2 we have the same sailors array but its located in a different part of the hierarchy:
{
"eat": "grub",
"drink": "rum",
"boats": [
{
"name": "blackpearl"
},
{
"name": "batavia",
"crew": [
{
"name": "captain"
},
{
"name": "deckswab",
"anotherObject": {
"sailors": [
{
"firstName": "John",
"lastName": "Doe"
},
{
"firstName": "Anna",
"lastName": "Smith"
},
{
"firstName": "Peter",
"lastName": "Jones"
}
]
}
}
]
}
]
}
In both cases I want to end up with a Gson JsonArray of the sailors array.
JsonParser parser = new JsonParser();
JsonObject root = parser.parse(json).getAsJsonObject();
JsonArray sailors = root.get("sailors").getAsJsonArray();
The above code works fine for the JSON #1 output. I know that I can rewrite it for JSON output #2 by stringing together a bunch of get and getAsJsonObject/Array methods..
However, I’m not sure how to adapt it so that it will always find the array no matter where it is located in the hierarchy without have to re-write the code each time. This is important for me since the JSON output will likely change again the future and I would like my code to be a little bit more durable so that I do not have to re-write or update it whenever the JSON hierarchy changes again.
Please help!
You may want to use recursion and do this manually (using org.json). You may also use Gson or Jackson to read the tree from the JSON string.
You should have a getSailorsArray(String json) method that returns an array of all sailors.
To do this recursively, create methods to read a JSONArray, read a JSONObject and read a value. Each method will accept an object as argument. Check if the type of object is a JSONArray or a JSONObject or a value and call the appropriate recursive method. If it is an array, you may call the getJsonObject method in loop.
Put in your logic to check for the sailors key, and return the array when you encounter it. There, you have it!