How to parse raw values from JSON array items with Jackson? - java

Using Jackson deserialization it would be great to have an option
to deserialize JSON array of items (of any nature: object, number, string) to a Java array of String.
I see that #JsonRawValue allows to have similar functionality for nested fields.
Is it possible to implement the same for "top level" objects?
void test(){
var payload = """ [
{"a": 1, "b": "hello"},
{"a": 2, "b": "bye"},
"something"
]
""";
// What else is required to get the effect of #JsonRawValue ?
String[] rawItems = new ObjectMapper().readValue(payload, String[].class);
assertEquals("""{"a": 1, "b": "hello"}""", rawItems[0]);
assertEquals("""{"a": 2, "b": "bye"}""", rawItems[1]);
assertEquals("\"something\"", rawItems[2]);
}

You can convert the payload to com.fasterxml.jackson.databind.node.ArrayNode and then iterate through elements (JsonNode in this case) and add them to a List<String> (which you can then convert to String[] if you want).
One thing to be aware of - the formatting will not be as you expect. JsonNode has two methods - toString() which deletes all the white space and toPrettyString which adds whitespaces and newlines to the final String
String payload = """
[
{"a": 1, "b": "hello"},
{"a": 2, "b": "bye"},
"something"
]
""";
ArrayNode items = new ObjectMapper().readValue(payload, ArrayNode.class);
List<String> rawItemsList = new ArrayList<>();
for (JsonNode jsonNode : items) {
rawItemsList.add(jsonNode.toString());
}
// You can just keep using the rawItemsList, but I've converted it to array since the question mentioned it
String[] rawItemsArr = rawItemsList.toArray(new String[0]);
assertEquals("""
{"a":1,"b":"hello"}""", rawItemsArr[0]);
assertEquals("""
{"a":2,"b":"bye"}""", rawItemsArr[1]);
assertEquals("\"something\"", rawItemsArr[2]);

Related

Get values by key from JSON Multi-dimensional Array

JSON:
[{"id":141741,"name":"Group","nodeTypeId":3,"deleted":false,"hasNodeAccesses":false,"children": [{"id":141742,"name":"Division","nodeTypeId":14,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141743,"name":"Site 1","nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141746,"name":"Converting","nodeTypeId":5,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141744,"name":"Site 2","nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141748,"name":"Converting","nodeTypeId":5,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141745,"name":"Site 3","nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141750,"name":"Converting","nodeTypeId":5,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141752,"name":"ML1","nodeTypeId":12,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141755,"nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141753,"name":"ML2","nodeTypeId":12,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141756,"nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141754,"name":"ML3","nodeTypeId":12,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141757,"nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[]}]}]}]}]
Code:
public List<String> getCapexStrategyNodeNames() {
JsonNode capexStrategyNodeList = client.getCapexStrategyNodes();
JSONArray nodes = capexStrategyNodeList.getArray();
List<JSONObject> nodeList = nodes.toList();
return retrieveValues(nodeList, "name");
}
private List<String> retrieveValues(List<JSONObject> list, String key) {
return list.stream()
.map(val -> val.getString(key))
.collect(Collectors.toList());
}
Output:
[Group]
I'm only retrieving the first value
How do I retrieve all name values from a nested JSON Array?
Thanks in advance!
With your current implementation, you are getting JsonNode object and you are reading it's name property, but you are not reading that property for it's chlildren (inner objects).
You have to query all nested objects recursivly and get a value of field name.
In my opinion the simplest way achieve this is by using JsonPath.
Add this dependency in pom.xml file:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
And here is the code snippet:
public static void main(String[] args) throws JsonProcessingException {
String json = "[{\"id\":141741,\"name\":\"Group\",\"nodeTypeId\":3,\"deleted\":false,\"hasNodeAccesses\":false,\"children\": [{\"id\":141742,\"name\":\"Division\",\"nodeTypeId\":14,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141743,\"name\":\"Site 1\",\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141746,\"name\":\"Converting\",\"nodeTypeId\":5,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141744,\"name\":\"Site 2\",\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141748,\"name\":\"Converting\",\"nodeTypeId\":5,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141745,\"name\":\"Site 3\",\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141750,\"name\":\"Converting\",\"nodeTypeId\":5,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141752,\"name\":\"ML1\",\"nodeTypeId\":12,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141755,\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141753,\"name\":\"ML2\",\"nodeTypeId\":12,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141756,\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141754,\"name\":\"ML3\",\"nodeTypeId\":12,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141757,\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]}]}]}]";
List<Object> names= JsonPath.parse(json)
.read("$..name"); //Recursive descent: Searches for the/specified property name recursively and returns an array of all values with this property name. Always returns a list, even if just one property is found.
System.out.println(names);
}
If you run this code you will see output like this:
["Group","Division","Site 1","Converting","Site 2","Converting","Site 3","Converting","ML1","ML2","ML3"]
Your json has only one object, so it is correct that your output has just one item.
This is the only object present in the array at first level
[{
"id": 141741,
"name": "Group",
"nodeTypeId": 3,
"deleted": false,
"hasNodeAccesses": false,
"children": [...]
}]
So your code should return ["Group"] and it is correct.
Probably you need to navigate through the children and maybe recursively to any nested children. In this case you need to change the algorithm
Library Josson also has the capability. The output is an ArrayNode.
https://github.com/octomix/josson
Josson josson = Josson.fromJsonString("[{\"id\":141741,\"name\":\"Group\",\"nodeTypeId\":3,\"deleted\":false,\"hasNodeAccesses\":false,\"children\": [{\"id\":141742,\"name\":\"Division\",\"nodeTypeId\":14,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141743,\"name\":\"Site 1\",\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141746,\"name\":\"Converting\",\"nodeTypeId\":5,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141744,\"name\":\"Site 2\",\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141748,\"name\":\"Converting\",\"nodeTypeId\":5,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141745,\"name\":\"Site 3\",\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141750,\"name\":\"Converting\",\"nodeTypeId\":5,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141752,\"name\":\"ML1\",\"nodeTypeId\":12,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141755,\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141753,\"name\":\"ML2\",\"nodeTypeId\":12,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141756,\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]},{\"id\":141754,\"name\":\"ML3\",\"nodeTypeId\":12,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[{\"id\":141757,\"nodeTypeId\":4,\"deleted\":false,\"hasNodeAccesses\":false,\"children\":[]}]}]}]}]");
JsonNode node = josson.getNode("cumulateCollect(name, children)");
System.out.println(node.toPrettyString());
Output
[ "Group", "Division", "Site 1", "Converting", "Site 2", "Converting", "Site 3", "Converting", "ML1", "ML2", "ML3" ]

How do I get the path of a jackson JsonNode

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());

Getting property values from random json with nested objects and arrays

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..
}

Filter out fields from a JsonNode

I have a Jackson JsonNode of sub-type ObjectNode:
ObjectNode node = parent.path('somepath');
node has a number of sub-fields, such as you'd see in this json object:
{
"somepath": {
"a": 1,
"b": 2,
"c": 3,
"d": 4
}
}
So the above object node will have four sub-objects (all JsonNode/ObjectNodes in their own right): a, b, c and d.
Given object node, I'd like to filter out some of the subfields. For instance, let's say I'd like to filter out everything but some list of field names, say ["b", "c"]. When I re-serialize the node object it would look like this:
{
"somepath": {
"b": 2,
"c": 3
}
}
I can think of a lot of ways where I can loop through both the field name list and the keep list, and rebuild the object, but that all seems like a lot of work and very unclear. What I'd love to use is a Stream.filter() type of function:
List<String> keepList = Lists.newArrayList("b", "c");
node.stream().filter( field -> keepList.contains(field.name()));
Obviously the above code doesn't work because I can't 'stream' an ObjectNode. Is there a way I can get this to work in a similar fashion, or am I stuck going the long way around?
There is a method in ObjectNode that does exactly what you want: retain. You might use it this way:
ObjectNode node = parent.path('somepath');
node = node.retain(keepList);

How to convert string array to object using GSON/ JSON?

I have a json like this:
[
[
"Passport Number",
"NATIONALITY",
"REASONS"
],
[
"SHAIS100",
"INDIA",
""
],
[
"",
"",
"Agent ID is not matched."
],
[
"",
"",
""
]
]
I want to populate this to ArrayList<String[]>,Please tell me how to do?
And empty strings should not convert as null.
That's very simple, you just need to do the following:
1.- First create the Gson object:
Gson gson = new Gson();
2.- Then get the correspondent Type for your List<String[]> (Note that you can't do something like List<String[]>.class due to Java's type erasure):
Type type = new TypeToken<List<String[]>>() {}.getType();
3.- Finally parse the JSON into a structure of type type:
List<String[]> yourList = gson.fromJson(yourJsonString, type);
Take a look at Gson docs
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
(Serialization)
gson.toJson(ints); ==> prints [1,2,3,4,5]
gson.toJson(strings); ==> prints ["abc", "def", "ghi"]
(Deserialization)
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
==> ints2 will be same as ints
Tis is important for you: We also support multi-dimensional arrays, with arbitrarily complex element types
For null objects, Gson by default will not convert as null. Ref.
But you can configure to scan those nulls attributes if you want to do it after.
The default behaviour that is implemented in Gson is that null object fields are ignored. This allows for a more compact output format; however, the client must define a default value for these fields as the JSON format is converted back into its Java.
Here's how you would configure a Gson instance to output null:
Gson gson = new GsonBuilder().serializeNulls().create();
In your problem maybe you don't need to configure that.
I hope it helps.

Categories