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
Answer from Daniel Taub on Stack OverflowThis 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.
As you found in your comment, JsonNode has a .AsArray() method which internally uses pattern matching to return a JsonArray. This isn't super safe, however, since it will throw an exception if productSizes isn't deserialized as a JsonArray.
For better safety, you are probably better off either using a try-catch or implementing the pattern matching yourself, like in the following:
if (productSizes is JsonArray jArray)
{
foreach (var size in jArray)
{
Console.WriteLine(size);
Console.WriteLine();
}
} else {
Console.WriteLine("Error: productSizes is not an array!");
}
Check if productSizes is a JsonArray and iterate it:
if (productSizes is JsonArray jArr)
{
foreach (var node in jArr)
{
// ...
}
}
Read more:
- Type-testing operators and cast expressions
- JSON DOM choices
forEach will iterate over children of a JsonNode (converted to String when printed) and fieldNames() gets an Iterator<String> over keys. Here are some examples for printing elements of the example JSON:
JsonNode rootNode = mapper.readTree(option);
System.out.println("\nchoices:");
rootNode.path("choices").forEach(System.out::println);
System.out.println("\nAllKeys:");
rootNode.fieldNames().forEachRemaining(System.out::println);
System.out.println("\nChoiceSettings:");
rootNode.path("choiceSettings").fieldNames().forEachRemaining(System.out::println);
You'll probably need fields() at some point that returns an Iterator<Entry<String, JsonNode>> so you can iterate over key, value pairs.
This should do it.
Map<String, Object> treeMap = mapper.readValue(json, Map.class);
List<String> keys = Lists.newArrayList();
List<String> result = findKeys(treeMap, keys);
System.out.println(result);
private List<String> findKeys(Map<String, Object> treeMap , List<String> keys) {
treeMap.forEach((key, value) -> {
if (value instanceof LinkedHashMap) {
Map<String, Object> map = (LinkedHashMap) value;
findKeys(map, keys);
}
keys.add(key);
});
return keys;
}
This will print out result as
[required, requiredMsg, choices, exc, a, b, c, required, textbox, d, choiceSettings, type, Settings]
You are parsing the JSON when Jackson is meant to do it for you. Don't do this to yourself.
One option is to create a DTO (Data Transfer Object) that matches the format of your JSON
class Root {
private String message;
private String cod;
private int count;
private List<City> list;
// appropriately named getters and setters
}
class City {
private long id;
private String name;
private Coordinates coord;
private List<Weather> weather;
// appropriately named getters and setters
}
class Coordinates {
private double lon;
private double lat;
// appropriately named getters and setters
}
class Weather {
private int id;
private String main;
private String description;
private int temp;
// appropriately named getters and setters
}
Then use an ObjectMapper and deserialize the root of the JSON.
ObjectMapper mapper = new ObjectMapper();
Root root = mapper.readValue(yourFileInputStream, Root.class);
You can then get the field you want. For example
System.out.println(root.getList().get(0).getWeather().get(0).getTemp());
prints
74
The alternative is to read your JSON in as a JsonNode and traverse it until you get the JSON element you want. For example
JsonNode node = mapper.readTree(new File("text.json"));
System.out.println(node.get("list").get(0).get("weather").get(0).get("temp").asText());
also prints
74
Based on the answer that Sotirios Delimanolis gave me, here was my solution:
ObjectMapper mapper = new ObjectMapper();
JsonFactory jfactory = mapper.getFactory();
JsonParser jParser;
try {
jParser = jfactory.createParser( tFile );
JsonNode node = mapper.readTree( jParser);
int count = node.get("count").asInt();
for ( int i = 0; i < count; i++ ) {
System.out.print( "City: " + node.get("list").get(i).get("name").asText() );
System.out.println( " , Absolute temperature: " +
node.get("list").get(i).get("main").get("temp").asText() );
}
jParser.close();
} catch (IOException e) {
e.printStackTrace();
}
Yes, 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())