Jackson Deserialization based on array index or key value - java

I have an API which returns an array of 2 objects. My problem is that each object has a different type. I can deserialize it if its 1 element each but when it returns multiple elements with different types I'm struggling to find out how to do that. An example of the JSON is below. One possible way to deserialize is based on the array index because we can guarantee the order and thus force a type. Another is based on the result of the path key which will always the return the same value for each element.
[
{
"path": "matter",
"result": {
"criticalDates.dateClosed": {
"id": "-2",
"name": "Date Closed",
"confirmed": false,
"confirmStatus": "Complete",
"order": 2,
"status": "Complete",
"isConfirmable": true,
"displayName": "Date Closed",
"autoCalc": false,
"__id": "e9d-4329-bb4a-03e644afdfda",
"__className": "CriticalDate",
"__tableId": "-24",
"__classes": [
"CriticalDate"
],
"date": null
},
"matterType": "Family",
"personActing.fullName": "Michael"
},
"status": "ok"
},
{
"path": "matter.cardList",
"result": [
{
"person.firstNames": "Daniel Testing",
"person.lastName": "Lastname"
},
{
"person.firstNames": "Daniel Testing",
"person.lastName": "Lastname"
}
],
"status": "ok"
}
]
What would be the appropriate way to deserialize this? Is there an annotation only approach?

One option is to use JsonTypeInfo to determine the target class.
Sample DTOs:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "path")
#JsonSubTypes({#JsonSubTypes.Type(value = MatterPath.class, name = "matter"),
#JsonSubTypes.Type(value = CardListPath.class, name = "matter.cardList")})
public abstract class AbstractPath {
private String path;
// Getters and Setters
}
//------------------------------
public class MatterPath extends AbstractPath {
private String matterType;
// Other fields, getter and setters
}
//---------------------------------
public class CardListPath extends AbstractPath{
private String cardListType;
}
Explanation:
#JsonTypeInfo - Use this annotation to determine the subclass based on the existing property. In our case path. For further details refer here.
#JsonSubTypes - Use this annotation to map the value in path field and the destination class to be used. For details refer JsonSubTypes.
Testing:
String json = "[\n" +
" {\n" +
" \"path\": \"matter\",\n" +
" \"matterType\": \"Family\"\n" +
" },\n" +
" {\n" +
" \"path\": \"matter.cardList\",\n" +
" \"cardListType\": \"ok\"\n" +
" }\n" +
"]\n";
AbstractPath[] abstractPaths = objectMapper.readValue(json, AbstractPath[].class);
System.out.println(Arrays.toString(abstractPaths));
Output:
[MatterPath{matterType='Family'}, CardListPath{cardListType='ok'}]

Related

Need JOLT spec for array input JSON

I am working on transforming a complex json using JOLT.
Input JSON:
{ "data":
[
{
"fieldname": "Name",
"fieldvalue": [ "John Doe" ]
},
{ "fieldname": "Title",
"fieldvalue": [ "Manager" ]
},
{ "fieldname": "Company",
"fieldvalue": [ "Walmart" ]
}
] }
Expected Output:
{
"finalPayload":{
"PI":{
"EmpName":"John Doe",
"EmpRole":"Manager"
},
"Company":"Walmart"
}
}
I am unable to understand how to access and assign "fieldvalue" in output based on "fieldname". Please help me with the JOLT spec.
Note: The order of name, title and company in input JSON will be jumbled and random meaning its not mandatory that under "data" array first object will be related to "Name" only.
Hi hope this helps you in resolving your issue.
You can have condition in Jolt too, by going inside the variable and checking the fieldname.
[
{
"operation": "shift",
"spec": {
"data": {
"*": {
"fieldname": {
"Name": {
"#(2,fieldvalue)": "finalPayload.PI.EmpName"
},
"Title": {
"#(2,fieldvalue)": "finalPayload.PI.EmpRole"
},
"Company": {
"#(2,fieldvalue)": "finalPayload.Company"
}
}
}
}
}
},
{
"operation": "cardinality",
"spec": {
"finalPayload": {
"PI": {
"EmpName": "ONE",
"EmpRole": "ONE"
},
"Company": "ONE"
}
}
}
]
May I introduce an alternative library to solve the issue.
https://github.com/octomix/josson
implementation 'com.octomix.josson:josson:1.3.21'
-------------------------------------------------
Josson josson = Josson.fromJsonString(
"{\"data\":[{\"fieldname\":\"Name\",\"fieldvalue\":[\"JohnDoe\"]},{\"fieldname\":\"Title\",\"fieldvalue\":[\"Manager\"]},{\"fieldname\":\"Company\",\"fieldvalue\":[\"Walmart\"]}]}");
JsonNode node = josson.getNode(
"map(" +
" finalPayload: map(" +
" PI: map(" +
" EmpName: data[fieldname='Name'].fieldvalue[0]," +
" EmpRole: data[fieldname='Title'].fieldvalue[0]" +
" )," +
" Company: data[fieldname='Company'].fieldvalue[0]" +
" )" +
")");
System.out.println(node.toPrettyString());
Output
{
"finalPayload" : {
"PI" : {
"EmpName" : "JohnDoe",
"EmpRole" : "Manager"
},
"Company" : "Walmart"
}
}

How to filter list of data in list in java with sql or nested dynamic condition

How to filter data dynamically using java, Assume we have data (list of map / json array without pojo mapping).
[
{
"id": "1001",
"type": "Internal",
"status": "Closed"
},
{
"id": "1002",
"type": "External",
"status": "Closed"
},
{
"id": "1003",
"type": "Internal",
"status": "Open"
},
{
"id": "1004",
"type": "Internal",
"status": "Open"
}
]
Now we need out put filtered data as id > 1001 and ( type: 'External' or status: 'Open" )
[
{
"id": "1002",
"type": "External",
"status": "Closed"
},
{
"id": "1003",
"type": "Internal",
"status": "Open"
},
{
"id": "1004",
"type": "Internal",
"status": "Open"
}
]
Any Suggestions how to achieve this ?
Use JSON path , below are few JSONPath queries I have posted as an sample example
P.S :- Ur "Id" should be of type Integer for operation > or <
Code:-
public static void main(String[] args) {
String jsonData = "[\r\n" +
" {\r\n" +
" \"id\": 1001,\r\n" +
" \"type\": \"Internal\",\r\n" +
" \"status\": \"Closed\"\r\n" +
" },\r\n" +
" {\r\n" +
" \"id\": 1002,\r\n" +
" \"type\": \"External\",\r\n" +
" \"status\": \"Closed\"\r\n" +
" },\r\n" +
" {\r\n" +
" \"id\": 1003,\r\n" +
" \"type\": \"Internal\",\r\n" +
" \"status\": \"Open\"\r\n" +
" },\r\n" +
" {\r\n" +
" \"id\": 1004,\r\n" +
" \"type\": \"Internal\",\r\n" +
" \"status\": \"Open\"\r\n" +
" }\r\n" +
"]";
String filterId = "$.[?(#.id > 1001)]"; //For Id > 1001
String filterType = "$.[?(#.type in ['External'])]"; //for External type
String filterTypeAndId = "$.[?((#.id > 1001) && (#.type in ['Internal']))]"; //for External type with Id > 1001
String filterTypeAndId2 = "$.[?((#.id > 1001) && (#.type in ['Internal', 'External']))]"; //for External type with Id > 1001
DocumentContext documentContext = JsonPath.parse(jsonData);
System.out.println(documentContext.read(filterId).toString());
System.out.println(documentContext.read(filterType).toString());
System.out.println(documentContext.read(filterTypeAndId).toString());
System.out.println(documentContext.read(filterTypeAndId2).toString());
}
results are :-
[{"id":1002,"type":"External","status":"Closed"},{"id":1003,"type":"Internal","status":"Open"},{"id":1004,"type":"Internal","status":"Open"}]
[{"id":1002,"type":"External","status":"Closed"}]
[{"id":1003,"type":"Internal","status":"Open"},{"id":1004,"type":"Internal","status":"Open"}]
[{"id":1002,"type":"External","status":"Closed"},{"id":1003,"type":"Internal","status":"Open"},{"id":1004,"type":"Internal","status":"Open"}]
We can use javascript ability to resolve condition. We can use Java ScriptEngineManager to run javascript expression :
Expression can be type = 'Internal' AND status ='Open'
private String getResult(String expression) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
return engine.eval(expression).toString();
} catch (Exception e) {
}
return null;
}
You can achieve this with the following approach :-
Convert json to string
Construct JSONArray >> JSONArray jsonArray = new JSONArray(converted_json_String)
pull out jsonArrayObject from JsonArray and construct a list
Use Collections.sort method and deploy your comparision logic in it on the JSON keys
and construct your sorted json.
try this code, I am assuming you have a list of objects.
List<YourCLass> filteredList = list
.stream()
.filter(obj -> obj.getId() > 1001 && (obj.getStatus().equals("Open") || obj.getType().equals("External")))
.collect(Collectors.toList());
filteredList have the list of objects you are expecting, using Object mapper you can get it as json format.
String filteredObjJson = new ObjectMapper().writeValuesAsString(filteredList);
add this dependency in pom.xml or download the lib and add it to your libraries
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

How to set nested JSON data to datatables?

I have a (nested) data structure containing objects and arrays. And trying to sent datatables but only one value displaying.
JSON data:
{
"data": [{
"name": "name1",
"value": "value1",
"list": [{
"sname": "sname1",
"svalue": "svalue1"
}, {
"sname": "sname2",
"svalue": "svalue2"
}]
}]
}
JSON data getting through URL by using Java.
jQuery code:
var pk = $("#pk").val();
console.log(pk);
url = "/register/search?id=" + pk;
console.log(url);
$('#largeTable').DataTable({
"ajax": url,
"bDestroy": true,
"columns": [{
"data": "name"
},
{
"data": "value"
},
{
"data": "list.1.sname"
},
{
"data": "list.1.svalue"
},
{
"data": null,
"defaultContent": editview
}
]
});
Here it is possible to display either first or second list values by using list.1 or list.0
But I want two values at a time.
If you used render or mRender you can do what you want with the object. For example you can traverse the array like in this example.
$('#largeTable').DataTable({
"columnDefs": [
{"targets": [0], "title":"name", "data":"name"},
{"targets": [1], "title":"value", "data":"value"},
{"targets": [2], "title":"list", "data":"list", "type":"html"
"render":function(data){
var listArray = data;
var listHtml = "";
for(var i=0;i<listArray.length;i++) {
listHtml += listArray[i].sname + " " + listArray[i].svalue + "<br>";
}
return listHtml;
},
}]
});
$.ajax({
"type":"GET",
"url":url,
"success":function(data,status) {
var jsonData = $.parseJSON(data);
$('#largeTable').dataTable().fnAddData(jsonData);
}
Your list in json data structure is an array. So, you should use
list.forEach(function(element) {
//console.log(element);
});
You could create an object and build JSON dynamically and set it to "columns" array.
Here is an example:
// make an empty object
var myObject = {};
// set the "list1" property to an array of strings
myObject.list1 = ['1', '2'];
// you can also access properties by string
myObject['list2'] = [];
// accessing arrays is the same, but the keys are numbers
myObject.list2[0] = 'a';
myObject['list2'][1] = 'b';
myObject.list3 = [];
// instead of placing properties at specific indices, you
// can push them on to the end
myObject.list3.push({});
// or unshift them on to the beginning
myObject.list3.unshift({});
myObject.list3[0]['key1'] = 'value1';
myObject.list3[1]['key2'] = 'value2';
myObject.not_a_list = '11';

How to parse the JSON objects with each different key and value, using Java?

I know the answer for parsing the JSON of this type:
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil's Food"}
where there is key value pair, with key being same(like 'id' here) and value differing, and we use a for loop to parse it quickly.
(For those who'd like to see how to parse above JSON, please go to this link: How to parse nested JSON object using the json library?)
However, the JSON I'm trying to parse is a different one which doesn't have a same key like 'Id' as above for every different value, but every key is a new key with a different value. Below is the example:
{
"disclaimer": "Exchange rates are ...........blah blah",
"license": "Data sourced from various .......blah blah",
"timestamp": 1446886811,
"base": "USD",
"rates": {
"AED": 3.67266,
"AFN": 65.059999,
"ALL": 127.896
.
.
All the currency values.
.
}
}
I'm not sure how to parse the above one with all different keys of currencies (currency like AED and their value) and pop them up in a drop down list.
Do I have to write a new line of code for each different currency and value pair or it is in some way possible to use a for loop for this one as well.
Can someone provide some lines code if possible?
you can use org.json for this thing.
E.g.:
JSONObject json = new JSONObject("<jsonString>");
Iterator<String> keys = json.keys();
while (keys.hasNext()) {
String key = keys.next();
System.out.println("Key :" + key + " Value :" + json.get(key));
}
You could use GSON in this case. I will just print the currencies with the according rate but you can build a different data structure(a map for example) and use it in your system.
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.util.Map;
public class Main {
public static void main(String[] args) throws IOException {
String jsonString = "{\n" +
" \"disclaimer\": \"Exchange rates are ...........blah blah\",\n" +
" \"license\": \"Data sourced from various .......blah blah\",\n" +
" \"timestamp\": 1446886811,\n" +
" \"base\": \"USD\",\n" +
" \"rates\": {\n" +
" \"AED\": 3.67266,\n" +
" \"AFN\": 65.059999,\n" +
" \"ALL\": 127.896\n" +
" }\n" +
"}";
JsonObject jsonObject = new JsonParser().parse(jsonString).getAsJsonObject();
for(Map.Entry<String, JsonElement> currency: jsonObject.getAsJsonObject("rates").entrySet()){
System.out.println("Currency "+ currency.getKey()+" has rate " + currency.getValue());
}
}
}

Parsing nested JSON

I have the following JSON:
{
"registration": {
"name": "Vik Kumar",
"first_name": "Vik",
"last_name": "Kumar",
"bloodGroup": "B-",
"gender": "male",
"birthday": "10\/31\/1983",
"email": "vik.ceo\u0040gmail.com",
"cellPhone": "1234123456",
"homePhone": "1234123457",
"officePhone": "1234123458",
"primaryAddress": "jdfjfgj",
"area": "jfdjdfj",
"location": {
"name": "Redwood Shores, California",
"id": 103107903062719
},
"subscribe": true,
"eyePledge": false,
"reference": "fgfgfgfg"
}
}
I am using the following code to parse it:
JsonNode json = new ObjectMapper().readTree(jsonString);
JsonNode registration_fields = json.get("registration");
Iterator<String> fieldNames = registration_fields.getFieldNames();
while(fieldNames.hasNext()){
String fieldName = fieldNames.next();
String fieldValue = registration_fields.get(fieldName).asText();
System.out.println(fieldName+" : "+fieldValue);
}
This works fine and it print all the values except for location which is kind of another level of nesting. I tried the same trick as above code to pass json.get("location") but that does not work. Please suggest how to make it work for location.
You need to detect when you are dealing with a (nested) Object using JsonNode#isObject:
public static void printAll(JsonNode node) {
Iterator<String> fieldNames = node.getFieldNames();
while(fieldNames.hasNext()){
String fieldName = fieldNames.next();
JsonNode fieldValue = node.get(fieldName);
if (fieldValue.isObject()) {
System.out.println(fieldName + " :");
printAll(fieldValue);
} else {
String value = fieldValue.asText();
System.out.println(fieldName + " : " + value);
}
}
}
Thus, when you reach an object, such as location, you'll call the printAll recursively to print all its inner values.
org.codehaus.jackson.JsonNode json = new ObjectMapper().readTree(jsonString);
org.codehaus.jackson.JsonNode registration_fields = json.get("registration");
printAll(registration_fields);
Since location is nested within registration, you need to use:
registration_fields.get("location");
to get it. But isn't it already processed by the while-loop, why do you need to get it separately?

Categories