jackson - read specific field from string list of jsons - java

I'm pretty new to world of jackson, and wanted to read the value of specific field from list of jsons (which is a response body of third-party api).
for a single json, using objectMapper works fine.
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(sampleString);
JsonNode idNode = rootNode.path("id");
System.out.println("id: "+ idNode.asText());
But I want to parse list of jsons (which is a string coming from a response body)
.So for example I receive this body:
[
{
"id":10,
"firstName":"Jack",
"primaryPhone":"9999999999",
"email":"jack#me.com"
},
{
"id":4,
"firstName":"Mark",
"primaryPhone":"9999999991",
"email":"mark#me.com"
},
{
"id":12,
"firstName":"Susaan",
"primaryPhone":"9999999992",
"email":"susan23#me.com"
}
]
I want to read the ids first, and if I find a specific id, return some other info from that block.
For example if id=4, read the firstName and email of that person.
But I'm not sure how to parsee list of json.
Any suggestions/comments is appreciated.

You can try,
JsonNode array = objectMapper.readValue(sampleString, JsonNode.class);
for(int i=0;i<array.length;i++){
JsonNode jsonNode = array.get(i);
JsonNode idNode = jsonNode.get("id");
String id = idNode.asText();
if(id.equals("4")){
JsonNode firstNameNode = jsonNode.get("firstName");
String firstName = firstNameNode.asText();
System.out.println("firstName = " + firstName);
JsonNode emailNode = jsonNode.get("email");
String email = emailNode.asText();
System.out.println("email = " + email);
break;
}
}

You can use Json Path.
So, the query would be something like this:
$[?(#.id == 4)].firstName

You can create a POJO like the one below:
class Record {
private Long id;
private String firstName;
//Getters and setters
}
And deserialise the json into List of these POJOS, e.g.:
ObjectMapper mapper = new ObjectMapper();
List<Record> records = mapper.readValue("", new TypeReference<List<Record>>() { });
Once done, you can filter out the records with stream, e.g.:
List<Record> filtered = records.stream()
.filter(r -> r.getId() = 12)
.collect(Collectors.toList());

Related

Error thrown when deserialising 2D Json Array using Jackson

I am trying to deserialize a two things from JSON. The format of the first one is as follows:
String json = "[{\"name\":\"Random\"," +
"\"coordinates\":[[-3.1,55.4],[-3.1,55.9],[-3.7,55.3],[-3.8,55.7],[-3.0,55.8]]}]";
This is the second:
String json2 = "[{\"name\":\"Random\"," + "\"longitude\":-3.1, \"latitude\":55}]
My code is simple, and is as follows:
ObjectMapper mapper = new ObjectMapper();
var asArray = mapper.readValue(json, NoFlyZone[].class);
var asArray2 = mapper.readValue(json, LngLat.class);
The NoFlyZone class:
record NoFlyZone(LngLat[] coordinates) {
#JsonIgnoreProperties("name")
NoFlyZone (#JsonProperty("coordinates") double[][] coordinates) {
this(doubleArrayToLngLatArray(coordinates));
}
private static LngLat[] doubleArrayToLngLatArray(double[][] coordinates) {
var coordinateArray = new LngLat[coordinates.length];
for (int i = 0; i < coordinates.length; i++) {
coordinateArray[i] = new LngLat(coordinates[i][0], coordinates[i][1]);
}
System.out.println(coordinateArray);
return coordinateArray;
}
}
And finally, the LngLat class:
record LngLat(double lng, double lat) {
LngLat (#JsonProperty("longitude") double lng,
#JsonProperty("latitude") double lat) {
this.lng = lng;
this.lat = lat;
}
}
I have tried deserialising them in the way shown above, but a MismatchedInputException is thrown when trying to deserialize the first string, with the error message "Cannot deserialize value of type uk.ac.ed.inf.LngLat from Array value (token JsonToken.START_ARRAY)...". I'm not sure why this is happening, so any help would be appreciated.
I have also tried adding the annotation
#JsonFormat(shape = JsonFormat.Shape.ARRAY)
and fixing it as detailed in Alexander's answer, but then the second string throws an error when attempting to be deserialised.
Since your record LngLat is represented as JSON-array (like "[-3.1, 55.4]") you need to customize its deserialization.
And for that you can use #JsonFormat annotation providing the attribute shape with the value of JsonFormat.Shape.ARRAY. That would instruct Jackson to populate the record properties from the array in order of their declaration.
#JsonFormat(shape = JsonFormat.Shape.ARRAY)
record LngLat(double lng, double lat) {}
And enclosing record NoFlyZone would be simplified to (special method for parsing array of LngLat is redundant):
#JsonIgnoreProperties("name")
record NoFlyZone(LngLat[] coordinates) {}
Usage example:
String json = "[{\"name\":\"Random\"," +
"\"coordinates\":[[-3.1,55.4],[-3.1,55.9],[-3.7,55.3],[-3.8,55.7],[-3.0,55.8]]}]";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json, new TypeReference<List<NoFlyZone>>() {}));
Output:
note: toString() method of the NoFlyZone has been overridden to display the array correctly
[NoFlyZone{coordinates=[LngLat[lng=-3.1, lat=55.4], LngLat[lng=-3.1, lat=55.9], LngLat[lng=-3.7, lat=55.3], LngLat[lng=-3.8, lat=55.7], LngLat[lng=-3.0, lat=55.8]]}]
Update
If you need to support the two entirely different structures of JSON, then you also need to customize deserialization on the NoFlyZone level (because its JSON-shape differs).
One of the ways to do that is to introduce the factory method annotated with #JsonCreator. It would expect a single argument of type Map<String, JsonNode> in order to be able to funnel all the properties thought it.
We also need to set the attribute of ignoreUnknown of #JsonIgnoreProperties to true.
Note: the definition of LngLat remains the same (as shown above annotated with #JsonFormat).
#JsonIgnoreProperties(ignoreUnknown = true)
public record NoFlyZone(LngLat[] coordinates) {
#JsonCreator
public static NoFlyZone getInstance(Map<String, JsonNode> fields) throws IOException {
boolean isArray = fields.containsKey("coordinates");
LngLat[] longLat;
if (isArray) {
ObjectReader reader = new ObjectMapper().readerFor(LngLat[].class);
longLat = reader.readValue(fields.get("coordinates")); // parsing JsonNode which corresponds to "coordinates" property
} else {
longLat = new LngLat[] { // creating a single-element array
new LngLat(
Double.parseDouble(fields.get("longitude").toString()),
Double.parseDouble(fields.get("latitude").toString())
)
};
}
return new NoFlyZone(longLat);
}
// toString()
}
Usage example:
String json1 = "[{\"name\":\"Random\"," +
"\"coordinates\":[[-3.1,55.4],[-3.1,55.9],[-3.7,55.3],[-3.8,55.7],[-3.0,55.8]]}]";
String json2 = "[{\"name\":\"Random\"," + "\"longitude\":-3.1, \"latitude\":55}]";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json1, new TypeReference<List<NoFlyZone>>() {}));
System.out.println(mapper.readValue(json2, new TypeReference<List<NoFlyZone>>() {}));
Output:
[NoFlyZone{coordinates=[LngLat[lng=-3.1, lat=55.4], LngLat[lng=-3.1, lat=55.9], LngLat[lng=-3.7, lat=55.3], LngLat[lng=-3.8, lat=55.7], LngLat[lng=-3.0, lat=55.8]]}]
[NoFlyZone{coordinates=[LngLat[lng=-3.1, lat=55.0]]}]

How to create a JSON Array of JSON objects with a Java for loop?

I have a for loop which iterates and generates key value pairs for different employees.
I need to create a JSON array like below and write it to a JSON file at the end.
I am having trouble figuring out the ideal way to code it (JSON Objects -> JSON Array -> JSON file?).
I am open to use json-simple/GSON.
Desired JSON file format:
[
{
"employeeFirstName": "Mark",
"employeeLastName": "Williams",
"employeeDepartment": "Sales",
},
{
"employeeFirstName": "John",
"employeeLastName": "Carpenter",
"employeeDepartment": "Accounts",
},
{
"employeeFirstName": "David",
"employeeLastName": "Hunter",
"employeeDepartment": "Marketing",
},
]
I tried using a JSONObject and add it to a JSONArray. But, couldn't figure how to code it for iterations.
My current Java class:
public class Test {
public void createEmployeesJSONArrayFile(ITestContext iTestContext) {
for (ITestResult testResult : iTestContext.getFailedTests().getAllResults()) {
System.out.println("employeeFirstName: " + testResult.getEmployeeFirstName()));
System.out.println("employeeLastName: " + testResult.getEmployeeLastName());
System.out.println("employeeDepartment: " + testResult.getEmployeeDepartment());
}
}
}
What is the simplest or ideal way to achieve this?
A simple way to achieve this would be to use Gson, an API provided by Google. You could write the Collection of ITestResult objects to a file. The toJson function will take the Collection of ITestResult objects and write them to the the given Appenable object, which in this case is a BufferedWriter which points to a file.
(untested, one sec, not at workstation)
Collection<ITestResult> results = iTestContext.getFailedTests().getAllResults();
new GsonBuilder()
.create()
.toJson(results, Files.newBufferedWriter(Paths.get("path", "to", "file")));
If your goal is to write to file eventually, you can also use jackson apis.
ObjectMapper mapper = new ObjectMapper();
//To add indentation to output json
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Collection<ITestResult> results = iTestContext.getFailedTests().getAllResults();
try{
mapper.writeValue(new File("/somepath/output.json"), results);
catch (IOException){
e.printStackTrace();
}
Note: Recommended to use single instance of object mapper
For following snippet:
public static final class Node {
class Employee {
private final String employeeFirstName;
private final String employeeLastName;
private final String employeeDepartment;
public Employee(String employeeFirstName, String employeeLastName, String employeeDepartment) {
this.employeeFirstName = employeeFirstName;
this.employeeLastName = employeeLastName;
this.employeeDepartment = employeeDepartment;
}
}
List<Employee> employees = Arrays.asList(
new Employee("Mark", "Williams", "Sales"),
new Employee("John", "Carpenter", "Accounts"),
new Employee("David", "Hunter", "Marketing"));
// String json = ...
}
Using gson-utils
String json = GsonUtils.writeValue(data);
Using jackson-utils
String json = JacksonUtils.writeValue(data);

How to convert a JsonNode containing a list of string to a comma separated string in Java

JsonNode:
{
name: ["bishal", "jaiswal", "Turtle"],
title: "jaiswal"
}
I want to extract the list of name from jsonNode as a comma separated string in an efficient way:
Output:
nameList ="bishal,jaiswal,Turtle"
Apart from converting the jsonNode of name array to a list & then using the join method to make the comma separated string. Any better way to do this ?
Here is the hack
ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonNode = objectMapper.readTree("{\"name\":[\"bishal\",\"jaiswal\",\"Turtle\"],\"title\":\"jaiswal\"}");
final JsonNode name = jsonNode.get("name");
final Iterator<JsonNode> iterator = name.iterator();
StringBuilder s = new StringBuilder();
while (iterator.hasNext()){
final JsonNode next = iterator.next();
if(s.length() > 0){
s.append(",");
}
s.append(next);
}
System.out.println(s.toString());
Hope so it will help you.
You can use Jackson JSON in this case...
Jackson is a very popular and efficient java based library to serialize or map Java objects to JSON and vice versa
For e.g.
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(jsonData); //jsonData is your json string here...
JsonNode names = rootNode.path("name");
Iterator<JsonNode> elements = names.elements();
while(elements.hasNext()){
JsonNode name = elements.next();
System.out.println("Names are = "+name);
}
Please can you let me know if this has solved your problem? Thanks

Having trouble accessing "endpoints" from below Json

I want all the URL present under the "endpoints" json.
I tried with access it through converting the String to JSON and after that using JSON parsar. But couldn't successed.
{
"process":{
"id":"epsswitch/process/create-switch-bdl",
"views":{
"selection":{
"uri":"//scp/sdf/sdf/s/df",
"controller":"scp/switching/createswitchingbdl/controllers/selection"
},
"customers":{
"uri":"//scp/vv/vv/views/Customers",
"controller":"scp/x/vc/controllers/customers"
},
"instructions":{
"uri":"//scp/df/fd/views/Information",
"controller":"scp/switching/fd/controllers/instructions"
},
"confirm":{
"uri":"//scp/switching/createswitchingbdl/views/Confirmation",
"controller":"scp/switching/createswitchingbdl/controllers/confirm"
}
},
"endpoints":{
"data":{
"uri":"/epsswitch/create/data?cc=true&al=true&ac=true"
},
"bank":{
"uri":"/fdd/df/df/v1/bank/df/",
"method":"GET"
}
}
}
There are multiple ways you can parse an JSON string. I use json simple (org.json.simple), java json are some of them. The following code uses the latter. It takes all the keys from the json string and retrieves all the values inside each key.
String sJSONString = "<your json object here>";
JSONObject jsObjects = new JSONObject(sJSONString);
//This is where you get your keys
Iterator<String> keys = jsObjects.keys();
//Use while if you need to poll for each key in JSON string
while(keys.hasNext())
{
String keyN = keys.next();
JSONObject jsoObjN = new JSONObject();
jsoObjN = (JSONObject)jsObjects.get(keyN);
//<Your logic here>
}
Following code help me to achive this.
final JsonNode studentNode = mapper.readTree(sPhone.getProcessJson());
JsonNode process = studentNode.get("process");
JsonNode endpoints = process.get("endpoints");
for (JsonNode jsonNode : endpoints) {
JsonNode uri = jsonNode.get("uri");
}

Get jsonArray Using Native query entity manager

I have an service class as below:
public class RulesService {
#PersistenceContext
private EntityManager em;
public JSONArray getReportingTableData(String Query) {
List<Object> list = em.createNativeQuery(Query).getResultList();
return /*convert the above list as json array**/;
}
}
So, if the query is "select col1 as name,col2 as agefrom table1" then my jsonArray should be
[{"name":"test","age":"24"},{"name":"test1","age":"26"}]
I don't want to use pojo or entity class here, because the query will get change dynamically and there are many number of tables here, so I don't want to create seperate java class for each table.That is the reason am trying to make it as a JSONArray.
Can anyone please give me the right solution do it.
You could do that with Jackson's ObjectMapper. This tutorial is very interesting.
List<Object> list = em.createNativeQuery(Query).getResultList();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(out, list);
final byte[] data = out.toByteArray();
System.out.println(new String(data));
you can use ObjectMapper to do dynamically.
public JSONArray getReportingTableData(String Query) {
List<Object> list = em.createNativeQuery(Query).getResultList();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
String arrayToJson = objectMapper.writeValueAsString(list);
JSONArray array = new JSONArray(arrayToJson);
return /*convert the above list as json array**/;
}
I guess you want to do like bellow. your query result is list of array. [["test","24"],["test2","26"]] and you want to convert it to key-value
List<Map<String,String>> result = listOfarray.stream()
.map(arr->{
Map<String,String> map = new HashMap<>();
map.put("name",arr[0]);
map.put("age",arr[1]);
return map;
})
.collect(Collectors.toList());
Sorry guys, I might be late but someone might find this interesting and save his/her day.
Reference
[https://gist.github.com/baxtheman/44fd1601380d415eeec53d9e6d5587dc][1]
public List<ObjectNode> getQuery(
Integer anno,
Integer settimana) {
Query q = em.createNativeQuery(
"NATIVE SQL....",Tuple.class);
List<Tuple> results = q.getResultList();
List<ObjectNode> json = _toJson(results);
return json;
}
private List<ObjectNode> _toJson(List<Tuple> results) {
List<ObjectNode> json = new ArrayList<ObjectNode>();
ObjectMapper mapper = new ObjectMapper();
for (Tuple t : results)
{
List<TupleElement<?>> cols = t.getElements();
ObjectNode one = mapper.createObjectNode();
for (TupleElement col : cols)
{
one.put(col.getAlias(), t.get(col.getAlias()).toString());
}
json.add(one);
}
return json;
}
Its a late answer but got it when i needed it.
The pretty easy and simple thing worked for me is
String[] columns = {"id","name","salary","phone","address", "dob"};
String query = "SELECT id,name,salary,phone,address,dob from users ";
List<Object[]> queryResp = em.createNativeQuery(query).getResultList();
List<Map<String,String>> dataList = new ArrayList<>();
for(Object[] obj : queryResp) {
Map<String,String> row = new HashMap<>(columns.length);
for(int i=0; i<columns.length; i++) {
if(obj[i]!=null)
row.put(columns[i], obj[i].toString());
else
row.put(columns[i], "");
}
dataList.add(row);
}
//Creating the ObjectMapper object
ObjectMapper mapper = new ObjectMapper();
//Converting the Object to JSONString
String jsonString = mapper.writeValueAsString(dataList);

Categories

Resources