I have an input json like:
{
"Employee": [
{
"employee1.id": 1
},
{
"employee1.name": "John"
},
{
"employee1.address.street": "main street"
},
{
"employee1.address.pin": "123456"
},
{
"employee2.id": 2
},
{
"employee2.name": "Mike"
},
{
"employee2.address.street": "51 street"
},
{
"employee2.address.pin": "234543"
}
]
}
And I am trying to convert it into:
{
"employee1":{
"id": 1,
"name": "John",
"address": {
"street": "main street",
"pin": "123456"
}
},
"employee2":{
"id": 2,
"name": "Mike",
"address": {
"street": "51 street",
"pin": "234543"
}
}
}
I tried to split the key from input json with dot i.e. '.' and tried to iterate over to construct a map: Map<String, Object>
But the problem is the input json key depth can go beyond 2 i.e. in future the input key can be like:
{
"employee1.address.tempAddress.street": "main street"
},
{
"employee1.address.permanentAddress.street": "main street"
}
So, is there any library available to achieve this, or anything closely related to this using which I can achieve this?
The comments about the input format being "weird" (to put it politely) are spot-on: This is a convoluted way to represent simple hierarchical data.
But sometimes we have to deal with sub-optimal input formats, that's why we can fix them like this:
private static JSONObject arrayToStructure(JSONArray inputArray) {
JSONObject output = new JSONObject();
for (Object o : inputArray) {
assert o instanceof JSONObject;
JSONObject jso = (JSONObject) o;
assert jso.length() == 1;
String key = jso.keys().next();
Object value = jso.get(key);
setJsonPath(output, key, value);
}
return output;
}
private static void setJsonPath(JSONObject target, String key, Object value) {
String[] keyParts = key.split("\\.");
JSONObject parent = target;
for (int i = 0; i < keyParts.length - 1; i++) {
String keyPart = keyParts[i];
if (parent.has(keyPart)) {
parent = parent.getJSONObject(keyPart);
} else {
JSONObject child = new JSONObject();
parent.put(keyPart, child);
parent = child;
}
}
assert !parent.has(keyParts[keyParts.length - 1]);
parent.put(keyParts[keyParts.length - 1], value);
}
This code uses JSON-Java, probably better known as org.json:json.
And it can be used like this:
public static void main(String[] args) {
String input = """
[
{
"employee1.id": 1
},
{
"employee1.name": "John"
},
{
"employee1.address.street": "main street"
},
{
"employee1.address.pin": "123456"
},
{
"employee2.id": 2
},
{
"employee2.name": "Mike"
},
{
"employee2.address.street": "51 street"
},
{
"employee2.address.pin": "234543"
}
]""";
JSONArray inputArray = new JSONArray(input);
JSONObject structure = arrayToStructure(inputArray);
System.out.println(structure.toString(2));
}
Note that this code is lacking proper sanity checks (some of which I've hinted at using assert) and the nature of this "protocol" means that you can get misleading, ambiguous or straight up malicious inputs. For example nothing stops the input from having both "employee1.id": 1 and "employee1.id": 2 in there. How that is to be interpreted is up to the parser and such ambiguity is a great source of bugs and potential security issues.
Library Josson can do the transformation by one expression.
https://github.com/octomix/josson
Josson josson = Josson.fromJsonString(
"{" +
" \"Employee\": [" +
" {" +
" \"employee1.id\": 1" +
" }," +
" {" +
" \"employee1.name\": \"John\"" +
" }," +
" {" +
" \"employee1.address.street\": \"main street\"" +
" }," +
" {" +
" \"employee1.address.pin\": \"123456\"" +
" }," +
" {" +
" \"employee2.id\": 2" +
" }," +
" {" +
" \"employee2.name\": \"Mike\"" +
" }," +
" {" +
" \"employee2.address.street\": \"51 street\"" +
" }," +
" {" +
" \"employee2.address.pin\": \"234543\"" +
" }" +
" ]" +
"}");
JsonNode node = josson.getNode("Employee.mergeObjects().unflatten('.')");
System.out.println(node.toPrettyString());
Output
{
"employee1" : {
"id" : 1,
"name" : "John",
"address" : {
"street" : "main street",
"pin" : "123456"
}
},
"employee2" : {
"id" : 2,
"name" : "Mike",
"address" : {
"street" : "51 street",
"pin" : "234543"
}
}
}
Related
I have 2 JSON objects
First
{
"fields": [
{
"name": "address",
"type": "string",
"default": "NONE"
},
{
"name": "age",
"type": "int",
"default": "NONE"
}
]
}
and second JSON
{
"fields": [
{
"name": "age",
"type": "int",
"default": "NONE"
},
{
"name": "address",
"type": "string",
"default": "NONE"
}
]
}
Please note that
fields object may not be always named "name","type" and "default"
the field values may not be always primitives. For example an
address which is of type "ArrayList".
How do I compare the JSON objects in such cases since these are equivalent JSON objects except for field value ordering. I am using Java 11 for my work. I tried using a custom Field class and map individual fields element to the class and then sort it but then the solution is not generic.
Following is one solution I am trying
public class JsonComparator {
public static boolean compareJson(String json1, String json2) {
JSONObject obj1 = new JSONObject(json1);
JSONObject obj2 = new JSONObject(json2);
JSONArray fields1 = obj1.getJSONArray("fields");
JSONArray fields2 = obj2.getJSONArray("fields");
fields1 = sortJsonArray(fields1);
fields2 = sortJsonArray(fields2);
for (int i = 0; i < fields1.length(); i++) {
JSONObject field1 = fields1.getJSONObject(i);
JSONObject field2 = fields2.getJSONObject(i);
for (String key : field1.keySet()) {
if (!field1.get(key).equals(field2.get(key))) {
return false;
}
}
}
return true;
}
private static JSONArray sortJsonArray(JSONArray array) {
ArrayList<JSONObject> sortedList = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
sortedList.add(array.getJSONObject(i));
}
sortedList.sort(new Comparator<JSONObject>() {
#Override
public int compare(JSONObject o1, JSONObject o2) {
return o1.toString().compareTo(o2.toString());
}
});
return new JSONArray(sortedList);
}
}
but need to analyze the JSON objects since all the JSON object need not contain the field "fields" and one JSON can be superset of other so trying to see if a better library available to give a solution.
You could try JSONCompare from JSONAssert library
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
</dependency>
with compareMode as STRICT,LENIENT,IGNORE_ORDER etc.
public static boolean isTwoJsonEquals(JSONObject firstObj,JSONObject secondObj, JSONCompareMode compareMode){
try {
JSONCompareResult result = JSONCompare.compareJSON(firstObj, secondObj, compareMode);
return result.passed();
} catch (JSONException e) {
e.printStackTrace();
}
return false;
}
You may try library Josson & Jossons. It has JSON join, set and compare operation.
https://github.com/octomix/josson
Deserialization
Jossons jossons = new Jossons();
jossons.putDataset("json1", Josson.fromJsonString(
"{" +
" \"fields\": [" +
" {" +
" \"name\": \"address\"," +
" \"type\": \"string\"," +
" \"default\": \"NONE\"" +
" }," +
" {" +
" \"name\": \"age\"," +
" \"type\": \"int\"," +
" \"default\": \"NONE\"" +
" }" +
" ]" +
"}"));
jossons.putDataset("json2", Josson.fromJsonString(
"{" +
" \"fields\": [" +
" {" +
" \"name\": \"age\"," +
" \"type\": \"int\"," +
" \"default\": \"NONE\"" +
" }," +
" {" +
" \"name\": \"gender\"," +
" \"type\": \"string\"," +
" \"default\": \"NONE\"" +
" }" +
" ]" +
"}"));
Subtract right from left
JsonNode node = jossons.evaluateQuery("json1->fields <-< json2->fields");
System.out.println(node.toPrettyString());
// Output:
// [ {
// "name" : "address",
// "type" : "string",
// "default" : "NONE"
// } ]
Subtract left from right
JsonNode node = jossons.evaluateQuery("json1->fields >-> json2->fields");
System.out.println(node.toPrettyString());
// Output:
// [ {
// "name" : "gender",
// "type" : "string",
// "default" : "NONE"
// } ]
Symmetric difference
JsonNode node = jossons.evaluateQuery("json1->fields <-> json2->fields");
System.out.println(node.toPrettyString());
// Output:
// [ {
// "name" : "address",
// "type" : "string",
// "default" : "NONE"
// }, {
// "name" : "gender",
// "type" : "string",
// "default" : "NONE"
// } ]
Equals
JsonNode node = jossons.evaluateQuery("json1->fields <==> json2->fields");
System.out.println(node.toPrettyString());
// Output:
// false
Not equals
JsonNode node = jossons.evaluateQuery("json1->fields <!=> json2->fields");
System.out.println(node.toPrettyString());
// Output:
// true
colleagues! I have two objects:
1.
{
"name": "test_name",
"contact": [
{
"street": "st.1",
"phoneNumber": "test_n"
}
]
}
OR
{
"name": "test_name",
"contact": [
{
"street": "st.1",
"phoneNumber": "test_n"
}
],
"additionalInfo": null
}
2.
{
"name": "test_name_2",
"additionalInfo": {
"lastName": "ln",
"age": 24
}
}
I use JsonObject (jackson from java) and I want to set to the first object these fields that it has null, and the second does not. For example, I want to get the first object like:
{
"name": "test_name",
"contact": [
{
"street": "st.1",
"phoneNumber": "test_n"
}
],
"additionalInfo": {
"lastName": "ln",
"age": 24
}
}
Because from the first object this field is null. Is it possible to do something? Thank u!
As far as I know, there is no out of box solution for that.
Based on answer given here, I modified merge() function to meet your needs.
Here is implementation:
public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {
Iterator<String> fieldNames = updateNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode jsonNode = mainNode.get(fieldName);
// if field exists and is an embedded object
if (jsonNode != null && jsonNode.isObject()) {
merge(jsonNode, updateNode.get(fieldName));
} else {
if (mainNode instanceof ObjectNode) {
JsonNode updateNodeValue = updateNode.get(fieldName);
JsonNode mainNodeValue = mainNode.get(fieldName);
if ((Objects.isNull(mainNodeValue) || mainNodeValue.isNull()) && (Objects.nonNull(updateNodeValue) && !updateNodeValue.isNull())) {
((ObjectNode) mainNode).put(fieldName, updateNodeValue);
}
}
}
}
return mainNode;
}
This function will basically take only those properties that exist in the object being passed as the second argument and add those properties to the object being passed as the first argument.
As far as I understand, that's what you need.
If you run code below:
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
final String jsonStringOne = "{\n" +
" \"name\": \"test_name\",\n" +
" \"contact\": [\n" +
" {\n" +
" \"street\": \"st.1\",\n" +
" \"phoneNumber\": \"test_n\"\n" +
" }\n" +
" ]\n" +
"}";
final String jsonStringTwo = "{\n" +
" \"name\": \"test_name_2\",\n" +
" \"additionalInfo\": {\n" +
" \"lastName\": \"ln\",\n" +
" \"age\": 24\n" +
" }\n" +
"}";
final JsonNode to= mapper.readTree(jsonStringOne);
final JsonNode from = mapper.readTree(jsonStringTwo);
final JsonNode jsonNode = merge(to, from);
System.out.println(jsonNode);
}
You shoud see output like this:
{
"name":"test_name",
"contact": [{"street":"st.1","phoneNumber":"test_n"}],
"additionalInfo":{"lastName":"ln","age":24}
}
I retrieved the following JSON-object. These json array consists of posts. As can be seen from the below, each post has a userId, id, title and a body. Now I need to iterate through this json and get the id of the posts IF the userId is 5 (in this case all four id's will be stored because in all of them the userId is 5
[
{
"userId": 5,
"id": 21,
"title": "A title",
"body": "A body"
},
{
"userId": 5,
"id": 52,
"title": "B title",
"body": "B body"
},
{
"userId": 5,
"id": 13,
"title": "C title",
"body": "C body"
},
{
"userId": 5,
"id": 44,
"title": "D title",
"body": "D body"
},
]
and then I need to store the id values in an array like this:
postIds = [21, 52, 13, 44]
So far I have done this
GetPosts[] getPosts = getPostResponse.as(GetPosts[].class);
String[] userPostIds;
for (int i = 0; i < getPosts.length; i++) {
if(getPosts[0].getId().equals(userId)) {
}
}
Then I tried to write a for loop like this but I know it is wrong. Maybe the statement in if parentheses is true, but for loop needs to be something else and I need to write something inside the if statement but I cannot progress here.
Edit: I now modified the for/if loop like this:
for (int i = 0; i < getPosts.length; i++) {
Integer userPostIds = null;
if (getPosts[i].getId().equals(userId)) {
userPostIds = getPosts.getId();
}
System.out.println(userPostIds);
}
But this time userPostIds is printed like this:
null
null
null
null
whereas I was expecting it to be:
21
52
13
44
Let's assume that you have stored your JSON as a String. You can use Jackson object mapper to map JSON to Object.
private static String JSON = "[\n" +
" {\n" +
" \"userId\": 5,\n" +
" \"id\": 21,\n" +
" \"title\": \"A title\",\n" +
" \"body\": \"A body\"\n" +
" },\n" +
" {\n" +
" \"userId\": 5,\n" +
" \"id\": 52,\n" +
" \"title\": \"B title\",\n" +
" \"body\": \"B body\"\n" +
" },\n" +
" {\n" +
" \"userId\": 5,\n" +
" \"id\": 13,\n" +
" \"title\": \"C title\",\n" +
" \"body\": \"C body\"\n" +
" },\n" +
" {\n" +
" \"userId\": 5,\n" +
" \"id\": 44,\n" +
" \"title\": \"D title\",\n" +
" \"body\": \"D body\"\n" +
" }\n" +
"]";
You need to create object for mapping:
public class Foo {
private Integer userId;
private Integer id;
private String title;
private String body;
// getters
// setters
}
Then you can map your JSON to list of objects:
private static List<Foo> mapJsonToObject() {
var om = new ObjectMapper();
List<Foo> list = null;
try {
list = Arrays.asList(om.readValue(JSON, Foo[].class));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return list;
}
In the end you need to iterate through this list to get all ids.
public static void main(String[] args) {
var list = mapJsonToObject();
var ids = list.stream().map(Foo::getId).collect(Collectors.toList());
System.out.println(ids.toString());
}
Thankfully, the hard part (deserialization) is complete per the OP.
Therefore, to iterate over the object list, I recommend something similar to the following, presuming that the post.getId returns a String (Otherwise, you may need to do a cast conversion)
Log.info("Store all the posts of the user");
GetPosts[] getPosts = getPostResponse.as(GetPosts[].class);
ArrayList<Integer> userIdsFromPosts = new ArrayList<Integer>();
if(getPosts != null) { // Avoiding a Possible NPE in list iteration...Just in case ;)
for (GetPosts post : getPosts) {
Integer currentUserId = post.getId();
if (!userIdsFromPosts.contains(currentUserId)) {
userIdsFromPosts.add(currentUserId);
}
}
}
return userIdsFromPosts;
One final note, if you are expecting a VERY large number of posts, then you should use a HashSet instead of ArrayList for performance reasons (when we run the .contains() method, each element of the ArrayList is iterated over).
I have the following code
JSONArray jobj = new select_data().get_patient_summary(p_id);
JSONObject jInnerObject = new JSONObject();
jInnerObject.put("weight", weight.get(0));
jobj.put(jInnerObject);
The outcome is:
{
"data": [
{
"pre_transfusional_haemoglobin_level": "",
"rhesus_blood_group_system": "",
"patient_id": "1",
"surname": "Demetriou",
"DOB": "2004-02-04",
"health_insurance_information": "ac",
"name": "Andreas",
"blood_group": "",
"transfusion_frequency": ""
},
{
"weight": "90"
}
],
"success": 1
}
Can I insert "weight" in the previous string, like:
{
"data": [
{
"pre_transfusional_haemoglobin_level": "",
"rhesus_blood_group_system": "",
"patient_id": "1",
"surname": "Demetriou",
"DOB": "2004-02-04",
"health_insurance_information": "ac",
"name": "Andreas",
"blood_group": "",
"transfusion_frequency": "",
"weight": "90"
},
],
"success": 1
}
EDITED for complete executable code:
import org.json.JSONObject;
public class test {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
String input = "{\r\n" +
" \"data\": [\r\n" +
" {\r\n" +
" \"pre_transfusional_haemoglobin_level\": \"\",\r\n" +
" \"rhesus_blood_group_system\": \"\",\r\n" +
" \"patient_id\": \"1\",\r\n" +
" \"surname\": \"Demetriou\",\r\n" +
" \"DOB\": \"2004-02-04\",\r\n" +
" \"health_insurance_information\": \"ac\",\r\n" +
" \"name\": \"Andreas\",\r\n" +
" \"blood_group\": \"\",\r\n" +
" \"transfusion_frequency\": \"\"\r\n" +
" },\r\n" +
" ],\r\n" +
" \"success\": 1\r\n" +
"}";
JSONObject jobj = new JSONObject(input);
jobj.getJSONArray("data").getJSONObject(0).put("weight", 90);
System.out.println(jobj.toString(1));
}
}
I have a json template file as below
{
"value": "abc",
"Treatments": [{
"value": "def",
"Stages": [{
"value": "ghi"
}]
}]
}
And a json data
{ "abc": {
"labelabc": "Assembly name abc",
"typeabc": "STRING_TYPE abc",
"formatabc": "N abc"
},
"def": {
"labeldef": "Assembly name def",
"typedef": "STRING_TYPE def",
"formatdef": "N def"
},
"ghi": {
"labelghi": "Assembly name ghi",
"typeghi": "STRING_TYPE ghi",
"formatghi": "N ghi"
}
}
I'm looking for a solution to parse the template into
{
"labelabc": "Assembly name abc",
"typeabc": "STRING_TYPE abc",
"formatabc": "N abc",
"Treatments": [
{
"labeldef": "Assembly name def",
"typedef": "STRING_TYPE def",
"formatdef": "N def",
"Stages": [
{
"labelghi": "Assembly name ghi",
"typeghi": "STRING_TYPE ghi",
"formatghi": "N ghi"
}
]
}
]
}
Meant:
Replace "value" entry with the jsonobject value in json data file
I already used the code below to achieve the result with 2 levels template. But failed with above 3 levels template
public static JsonElement generateTemplate(JsonElement templateJson, JsonElement dataTemplate) {
if (templateJson.isJsonArray()) {
for (JsonElement jsonElement1 : templateJson.getAsJsonArray()) {
generateTemplate(jsonElement1, dataTemplate);
}
} else if (templateJson.isJsonObject()) {
for (Iterator<Map.Entry<String, JsonElement>> iterator = templateJson.getAsJsonObject().entrySet()
.iterator(); iterator.hasNext();) {
Map.Entry<String, JsonElement> entry = iterator.next();
if (entry.getKey().equals("value")) {
templateJson = dataTemplate.getAsJsonObject().get(entry.getValue().getAsString()).getAsJsonObject();
} else {
if (entry.getValue().isJsonObject()) {
generateTemplate(entry.getValue(), dataTemplate);
} else
templateJson.getAsJsonObject().add(entry.getKey(), p.parse(
dataTemplate.getAsJsonObject().get(entry.getValue().getAsString()).getAsJsonObject()
));
}
generateTemplate(entry.getValue(), dataTemplate);
}
}
return templateJson;
}
Very appreciated your advices
You could use recursion. This is main idea, you can adjust actual replacements according to your needs later in modifyObject method which for "value": "abc" would expect object held in data under
"abc": {
"labelabc": "Assembly name abc",
"typeabc": "STRING_TYPE abc",
"formatabc": "N abc"
}
private static void modifyObject(JsonObject obj, JsonObject replacement) {
obj.remove("value");
for (String key : replacement.keySet()) {
obj.addProperty(key, replacement.get(key).getAsString());
}
}
public static JsonElement traverse(JsonElement element, JsonObject allReplacements) {
if (element.isJsonObject()) {
JsonObject asJsonObject = element.getAsJsonObject();
///keys can change after we modify object,
//but we want to iterate only over original keys
Set<String> originalKeys = new HashSet<>(asJsonObject.keySet());
for (String key : originalKeys) {
if (key.equals("value")) {
String value = asJsonObject.get(key).getAsString();
modifyObject(asJsonObject, allReplacements.getAsJsonObject(value));
} else {
traverse(asJsonObject.get(key), allReplacements);
}
}
} else if (element.isJsonArray()) {
for (JsonElement innerElement : element.getAsJsonArray()) {
traverse(innerElement, allReplacements);
}
}
return element;
}
Usage:
public static void main(String[] args) throws Exception {
String jsonTemplate =
"{\n" +
" \"value\": \"abc\",\n" +
" \"foo\": \"bar\",\n" +
" \"Treatments\": [ {\n" +
" \"value\": \"def\",\n" +
" \"Stages\": [ [ {\n" +
" \"value\": \"ghi\"\n" +
" } ] ]\n" +
" } ]\n" +
"}";
String jsonData = "{ \"abc\": {\n" +
" \"label\": \"Assembly name abc\",\n" +
" \"type\": \"STRING_TYPE abc\",\n" +
" \"format\": \"N\"\n" +
" },\n" +
" \"def\": {\n" +
" \"label\": \"Assembly name def\",\n" +
" \"type\": \"STRING_TYPE\",\n" +
" \"format\": \"N\"\n" +
" },\n" +
" \"ghi\": {\n" +
" \"label\": \"Assembly name ghi\",\n" +
" \"type\": \"STRING_TYPE\",\n" +
" \"format\": \"N\"\n" +
" }\n" +
"}";
JsonParser jsonParser = new JsonParser();
JsonElement template = jsonParser.parse(jsonTemplate);
JsonObject data = (JsonObject) jsonParser.parse(jsonData);
JsonElement obj = template.deepCopy();//in case we don't want to modify original template
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println(gson.toJson(traverse(obj, data)));
}
Output:
{
"foo": "bar",
"Treatments": [
{
"Stages": [
[
{
"label": "Assembly name ghi",
"type": "STRING_TYPE",
"format": "N"
}
]
],
"label": "Assembly name def",
"type": "STRING_TYPE",
"format": "N"
}
],
"label": "Assembly name abc",
"type": "STRING_TYPE abc",
"format": "N"
}