Compare with name and update that json element.
Resource resource = new ClassPathResource("gateway-fields.json"); //read from json file
JsonFactory jsonFactory = new JsonFactory();
ObjectMapper objectMapper = new ObjectMapper(jsonFactory);
JsonNode arrayNode = objectMapper.readTree(resource.getFile()).get("fields");
if (arrayNode.isArray()) {
for (JsonNode jsonNode : arrayNode) {
String nameFieldNode = jsonNode.get("name").asText();
if("RefNo".equals(nameFieldNode)){
((ObjectNode)jsonNode).put("name", "1112");
}
}
}
Answer from Kaustubh Khare on Stack OverflowYes, the Jackson manual parser design is quite different from other libraries. In particular, you will notice that JsonNode has most of the functions that you would typically associate with array nodes from other APIs. As such, you do not need to cast to an ArrayNode to use. Here's an example:
JSON:
{
"objects" : ["One", "Two", "Three"]
}
Code:
final String json = "{\"objects\" : [\"One\", \"Two\", \"Three\"]}";
final JsonNode arrNode = new ObjectMapper().readTree(json).get("objects");
if (arrNode.isArray()) {
for (final JsonNode objNode : arrNode) {
System.out.println(objNode);
}
}
Output:
"One"
"Two"
"Three"
Note the use of isArray to verify that the node is actually an array before iterating. The check is not necessary if you are absolutely confident in your data structure, but it's available should you need it (and this is no different from most other JSON libraries).
In Java 8 you can do it like this:
import java.util.*;
import java.util.stream.*;
List<JsonNode> datasets = StreamSupport
.stream(obj.get("datasets").spliterator(), false)
.collect(Collectors.toList())
This will work for you :
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
Map<String, String> map = new HashMap<>();
addKeys("", root, map, new ArrayList<>());
map.entrySet()
.forEach(System.out::println);
private void addKeys(String currentPath, JsonNode jsonNode, Map<String, String> map, List<Integer> suffix) {
if (jsonNode.isObject()) {
ObjectNode objectNode = (ObjectNode) jsonNode;
Iterator<Map.Entry<String, JsonNode>> iter = objectNode.fields();
String pathPrefix = currentPath.isEmpty() ? "" : currentPath + "-";
while (iter.hasNext()) {
Map.Entry<String, JsonNode> entry = iter.next();
addKeys(pathPrefix + entry.getKey(), entry.getValue(), map, suffix);
}
} else if (jsonNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) jsonNode;
for (int i = 0; i < arrayNode.size(); i++) {
suffix.add(i + 1);
addKeys(currentPath, arrayNode.get(i), map, suffix);
if (i + 1 <arrayNode.size()){
suffix.remove(arrayNode.size() - 1);
}
}
} else if (jsonNode.isValueNode()) {
if (currentPath.contains("-")) {
for (int i = 0; i < suffix.size(); i++) {
currentPath += "-" + suffix.get(i);
}
suffix = new ArrayList<>();
}
ValueNode valueNode = (ValueNode) jsonNode;
map.put(currentPath, valueNode.asText());
}
}
For the json you gave the output will be :
name-items-name-1-2=2nditem
name-items-name-1-1=firstitem
name-items-stock-1-1=12
name-first-1=John
name-items-stock-1-2=23
company=John Company
name-last-1=Doe
elements() gives you an iterator for subnodes and fields() gives you the properties.
With that, you can code a recursive function that walks through all nodes.
ArrayNode implements Iterable. Iterable has a spliterator() method. You can create a sequential Stream from a Spliterator using
ArrayNode arrayNode = (ArrayNode) json.get("xyz");
StreamSupport.stream(arrayNode.spliterator(), false)
An ArrayNode class provides random access: you can get size() and an element by index (using get(index)). This is all you need to create a good stream:
Stream<JsonNode> nodes = IntStream.range(0, files.size()).mapToObj(files::get);
Note that this solution is better than using default spliterator (as suggested by other answerers) as it can split well and reports the size properly. Even if you don't care about parallel processing, some operations like toArray() will work more effectively as knowing the size in advance will help to allocate an array of proper size.
Assuming that a row is one of the entries in a ArrayNode the following simple approach may be useful. It uses the JsonNode abstraction instead of a series of nested Map objects which I personally prefer since JsonNode provides a series of utility methods that are helpful when dealing with this kind of data (data where the structure is possibly unknown or very dynamic so that it can't be easily transformed to a POJO).
The testcase below illustrates how to find the number of rows and how to print the values. To get hold of the values the method JsonNode.elements() is used and the number of rows is simply a call to the size()-method.
public class ArrayNodeTest {
@Test
public void verifySizeAndPrintRows() throws IOException {
final String jsonStr =
"[{\"key11\":\"value11\",\"key12\":\"value12\"},\n" +
"{\"key21\":\"value21\",\"key22\":\"value22\"},\n" +
"{\"keyn1\":\"valuen1\",\"keyn2\":\"valuen2\"}]";
final ObjectMapper mapper = new ObjectMapper();
final JsonNode jsonNode = mapper.readTree(jsonStr);
// Verify size
Assert.assertEquals(3, jsonNode.size());
// And print rows
for (final JsonNode row : jsonNode) {
final Iterable<JsonNode> iterable = () -> row.elements();
iterable.forEach(elem -> System.out.println(elem.asText()));
}
}
}
to identify the number of rows
What's a "row"? Is a key/value pair a row? Is an element of the JSON array (no matter how many key/value pairs it contains) a row?
print only the values on the screen
Jackson has nothing to do with printing anything on any screens. Jackson can be used to populate a Java data structure from the input JSON, and the populated Java data structure can then be used however you want.
Given the JSON structure in the original question, a simple solution would be to bind to a list (or array) of maps, and then just iterate through the list of maps, accessing all of the values. Following is such an example.
import java.io.File;
import java.util.Map;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
Map<String, String>[] maps = mapper.readValue(new File("input.json"), Map[].class);
for (Map<String, String> map : maps)
{
for (Map.Entry<String, String> entry : map.entrySet())
{
System.out.println(entry.getValue());
}
}
}
}
Output:
value11
value12
value21
value22
valuen1
valuen2
Acquire an ObjectReader with ObjectMapper#readerFor(TypeReference) using a TypeReference describing the typed collection you want. Then use ObjectReader#readValue(JsonNode) to parse the JsonNode (presumably an ArrayNode).
For example, to get a List<String> out of a JSON array containing only JSON strings
CopyObjectMapper mapper = new ObjectMapper();
// example JsonNode
JsonNode arrayNode = mapper.createArrayNode().add("one").add("two");
// acquire reader for the right type
ObjectReader reader = mapper.readerFor(new TypeReference<List<String>>() {
});
// use it
List<String> list = reader.readValue(arrayNode);
If an Iterator is more useful...
...you can also use the elements() method of ArrayNode. Example see below.
sample.json
Copy{
"first": [
"Some string ...",
"Some string ..."
],
"second": [
"Some string ..."
]
}
So, the List<String> is inside one of the JsonNodes.
Java
When you convert that inner node to an ArrayNode you can use the elements() method, which returns an Iterator of JsonNodes.
CopyFile file = new File("src/test/resources/sample.json");
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(file);
ArrayNode arrayNode = (ArrayNode) jsonNode.get("first");
Iterator<JsonNode> itr = arrayNode.elements();
// and to get the string from one of the elements, use for example...
itr.next().asText();
New to Jackson Object Mapper?
I like this tutorial: https://www.baeldung.com/jackson-object-mapper-tutorial
Update:
You can also use .iterator() method of ArrayNode. It is the same:
Same as calling
.elements(); implemented so that convenience "for-each" loop can be used for looping over elements of JSON Array constructs.
from the javadocs of com.fasterxml.jackson.core:jackson-databind:2.11.0