How to Parse Nested Value Into List From JSON Array Using Jackson
Without having tried it, this looks like a JSON-file that would come from serializing a Sightings-object from something like
public AnimalSighting {
public int id;
public String name;
public String location;
}
public Sightings {
public int count;
public ArrayList<AnimalSighting> results;
}
Then deserialization would be straightforward with new ObjectMapper().readValue(file, Sightings.class);
Of course the classes could use private attributes with getters and setters and then possibly need some annotations (@JsonCreator/@JsonProperty/@JsonGetter/@JsonSetter, depending on what exactly you are doing), but the general principle remains.
Parsing deeply nested JSON properties with Jackson
JsonPath library allows one to select only required fields and thn you can use Jackson
to convert raw data to POJO
class. Example solution could look as follows:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.jayway.jsonpath.JsonPath;
import java.io.File;
import java.util.List;
import java.util.Map;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
List<Map> nodes = JsonPath.parse(jsonFile).read("$..value[*].user.name");
ObjectMapper mapper = new ObjectMapper();
CollectionType usersType = mapper.getTypeFactory().constructCollectionType(List.class, User.class);
List<User> users = mapper.convertValue(nodes, usersType);
System.out.println(users);
}
}
class User {
@JsonProperty("first")
private String firstName;
@JsonProperty("last")
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
Above code prints:
[User{firstName='x', lastName='y'}]
Parsing nested JSONArrays with Java
It looks like your data is an array of array of objects, not strings. You need to do something like this:
JSONObject jsonResult = new JSONObject(result);
JSONArray data = jsonResult.getJSONArray("data");
if(data != null) {
String[] names = new String[data.length()];
for(int i = 0 ; i < data.length() ; i++) {
JSONArray arr = data.getJSONArray(i);
dataObj = arr.getJSONObject(0)
Iterator<String> keys = dataObj.keys();
names[i] = dataObj.getString(keys.next())
}
System.out.println(names);
}
I probably have some syntax issues in this answer, but the main idea is that you're trying to get a string while you're dealing with array of objects (each data index is an array of objects. It just so happens to be that there's only one object in each such array)
Jackson JSON - Extract data from nested array
As your results
response is dynamic I would suggest you not to include your results
object in your deserialization. Include the below code in your POJO class which does the deserailization.
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
Now the Map
with the name additionalProperties
will contain the any additional objects that you hadn't tried to capture in the Parent object ( in this case it will have your results
object). It stores either Object
or list of Objects
depending upon your response and you can access it with the key value results
.
As suggested by FlemingJp using a generic type seems like a much feasible solution for this question so I am including that part as well in this answer.
public class Response<T> {
private boolean success;
private String message;
private T result;
public T getResult() {
return result;
}
public String getMessage () {
return message;
}
public boolean getSuccess() {
return success;
}
}
Example Usage
ObjectMapper objectMapper = new ObjectMapper();
// When the result is one object
Response<DistanceStat> res = objectMapper.readValue(data, new TypeReference<Response<DistanceStat>>() {});
// For a collection of objects under result
Response<Collection<DistanceStat>> res = objectMapper.readValue(data, new TypeReference<Response<Collection<DistanceStat>>>() {});
Edit:
You can deserialize in jackson as follows:
ObjectMapper mapper = new ObjectMapper();
String jsonInString //which contains your json data in string
ParentNode parentNode = mapper.readValue(jsonInString, ParentNode.class);
PraentNode
being your parent class.
Edit 2:
Note
: This is very specific to your situation.
These are the POJO classes you will be needing, the parent class to which you have to deserialize
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"success",
"message"
})
public class Example {
@JsonProperty("success")
private Boolean success;
@JsonProperty("message")
private String message;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("success")
public Boolean getSuccess() {
return success;
}
@JsonProperty("success")
public void setSuccess(Boolean success) {
this.success = success;
}
@JsonProperty("message")
public String getMessage() {
return message;
}
@JsonProperty("message")
public void setMessage(String message) {
this.message = message;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
I intentionally not included the Result
class in here. Now whatever the Result
response you get will be stored in the Map
named additional properties
as an object
which can be a list<Result>
or class Result
Now you have two structures for Result
object. You can store them in same POJO. so let's construct two different POJO classes for each structure.
Result1.java
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"Frequency",
"Range"
})
public class Result1 {
@JsonProperty("Frequency")
private Double frequency;
@JsonProperty("Range")
private Double range;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("Frequency")
public Double getFrequency() {
return frequency;
}
@JsonProperty("Frequency")
public void setFrequency(Double frequency) {
this.frequency = frequency;
}
@JsonProperty("Range")
public Double getRange() {
return range;
}
@JsonProperty("Range")
public void setRange(Double range) {
this.range = range;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Result2.java:
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"Bid",
"Ask",
"Last"
})
public class Result2 {
@JsonProperty("Bid")
private Double bid;
@JsonProperty("Ask")
private Double ask;
@JsonProperty("Last")
private Double last;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("Bid")
public Double getBid() {
return bid;
}
@JsonProperty("Bid")
public void setBid(Double bid) {
this.bid = bid;
}
@JsonProperty("Ask")
public Double getAsk() {
return ask;
}
@JsonProperty("Ask")
public void setAsk(Double ask) {
this.ask = ask;
}
@JsonProperty("Last")
public Double getLast() {
return last;
}
@JsonProperty("Last")
public void setLast(Double last) {
this.last = last;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Now you deserialize the JSON using the following piece of code:
ObjectMapper mapper = new ObjectMapper();
String jsonInString //which contains your json data in string
Example example= mapper.readValue(jsonInString, Example.class);
Here comes the tricky part, now your map contains the Results
response but we are not sure if it is a list of objects or object.
Now to get the Results
we will first try to identify if it's a collection or not ( as list is a collection) which will help us identify.
Object resultObject = example.getAdditionalProperties().getKey("results");
if ( resultObject instanceof Collection<?>){
List<Result1> results=resultObject;
}
else{
Result2 results=resultObject;
}
Hope this solves your issue!!
Let me know if you need clarification with anything!!
Getting value from nested JSON Array via GSON on Android?
You can use JACKSON or GSON library for fast parsing of Json data using model class.
JACKSON and GSON are dedicated to processing (serializing/deserializing) JSON data.
Raw Parsing via GSON
JsonObject fetchedJSON = parsedJSON.getAsJsonObject();
//weather is an array so get it as array not as string
JsonArray jarray = fetchedJSON.getAsJsonArray("weather");
// OR you use loop if you want all main data
jobject = jarray.get(0).getAsJsonObject();
String main= jobject.get("main").getAsString();
Although if you want raw parsing then you can do like ::
JSONObject obj = new JSONObject(yourString);
JSONArray weather = obj.getJSONArray("weather");
for (int i = 0; i < arr.length(); i++)
{
String main= arr.getJSONObject(i).getString("main");
......
}
Java FastXML JSON library: How to parse a nested JSON structure
Two things to consider:
- If you want to keep streaming, calling
jp.readValueAsTree()
is not a good idea as that will allocate memory to create the tree with all data under the current node (in your code, everything under "matches"), unless you know for sure this is a small tree. - To get to a value in an array, there is no need to create an array first. The streaming API will let you walk to the value in the array you need.
As @MChaker shows in his answer, if you need most of the data provided in the JSON file, creating a model to hold the data will be beneficial. But if you just need a couple of values, Jackson will let you do that. Allthough, I must admit, getting data in a truly streaming fashion will require some creativity to find ways of keeping track of where you are and what data you expect.
Following code shows the easy way and the streaming way:
import java.io.*;
import java.util.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
/**
* http://stackoverflow.com/q/30288878/3080094
* Using jackson-databind-2.5.3 which uses
* jackson-annotations-2.5.0 and
* jackson-core-2.5.3
* @author vanOekel
*/
public class JsonTest {
public static void main(String[] args) {
try {
new JsonTest().getNames();
} catch (Exception e) {
e.printStackTrace();
}
}
ObjectMapper jsonMapper = new ObjectMapper();
JsonFactory jsonFactory = new JsonFactory();
void getNames() throws Exception {
final String resourceName = "some.json";
JsonNode jn;
try (InputStream in =
Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName)
) {
if (in == null) {
throw new FileNotFoundException("File not found: " + resourceName);
}
jn = jsonMapper.readTree(in);
}
findByPath(jn);
try (InputStream in =
Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName)
) {
if (in == null) {
throw new FileNotFoundException("File not found: " + resourceName);
}
JsonParser jsonParser = jsonFactory.createParser(in);
findInStream(jsonParser);
}
}
final String[] path = new String[] {"myReport", "docReports", "part1/.", "myAnalysis", "matches", "name"};
void findByPath(JsonNode jn) {
JsonNode matchesNamesNode = jn;
for (int i = 0; i < path.length - 1; i++) {
matchesNamesNode = matchesNamesNode.path(path[i]);
}
if (matchesNamesNode.isMissingNode()) {
throw new RuntimeException("No node with names found.");
}
System.out.println("Tree names: " + matchesNamesNode.findValuesAsText(path[path.length - 1]));
}
void findInStream(JsonParser jp) throws Exception {
int pathIndex = 0;
List<String> names = new ArrayList<String>();
boolean breakOnClose = false;
while (jp.nextToken() != null) {
final String fieldName = jp.getCurrentName();
if (fieldName == null) {
continue;
}
if (breakOnClose && fieldName.equals(path[path.length - 2])) {
System.out.println("Stopping search at end of node " + fieldName);
break;
}
if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
continue;
}
// System.out.println("Field " + fieldName);
if (pathIndex >= path.length - 1) {
if (fieldName.equals(path[path.length - 1])) {
// move from field name to field value.
jp.nextToken();
String name = jp.getValueAsString();
if (name == null) {
throw new RuntimeException("No value exists for field " + fieldName);
}
names.add(name);
System.out.println("Found " + fieldName + " value: " + name);
}
} else if (fieldName.equals(path[pathIndex])) {
System.out.println("Found node " + path[pathIndex]);
pathIndex++;
if (pathIndex >= path.length - 1) {
System.out.println("Looking for names ...");
breakOnClose = true;
// prevent breaking on "matches" value json-token.
jp.nextFieldName();
}
}
}
System.out.println("Streaming names: " + names);
}
}
How to get data from nested JSON?
If I understood correctly, you want a loop
Bean data = new Gson().fromJson(req, Bean.class);
for (Data d : data.getData()) {
System.out.println(d.getName());
for (String u : d.getUrls()) {
System.out.println(u);
}
}
Otherwise, you can already access things by index
Data first = data.getData().get(0);
System.out.println(first.getName()); // google
Related Topics
Json to Pojo With Nested Values
How to Exclude an Android App from Battery Optimization Using Code
Filling a List With All Enum Values in Java
How to Inject a Map Using the @Value Spring Annotation
How to Sort Arraylist of Objects by Timestamp and Get Last Five Elements
@Notnull:Validation Custom Message Not Displaying
How to Reconnect Kafka Producer Once Closed
How to Get Response as Json With Responseentity in Java
Using Loop to Accept Additional User Input Until User Enters a Value That Ends the Input
Jpa Repository Query - Java.Lang.Object; Cannot Be Cast to Model
How to Post Data in an Array in Android Using Retrofit 2
Update Elements in a Jsonobject
After Spring Boot 2.0 Migration: Jdbcurl Is Required With Driverclassname