Remove a nested JSON object with particular key, value with Jackson - java

I want to remove blob columns from JSON objects.
I need to check if any of the object has "#type": "blob", the entire column should be dropped.
Ex. following is a record from a DB. 'experience', 'hitpoints', 'name', 'uuid', 'image' (optional) are the columns. since the record has a blob column i.e image. It should be dropped.
Sample I/P:
{
"experience": 14248,
"hitpoints": 9223372036854775807,
"name": "Aaron1",
"uuid": "78edf902-7dd2-49a4-99b4-1c94ee286a33",
"image": {
"#type": "blob",
"content_type": "image/jpeg",
"digest": "sha1–4xlj1AKFgLdzcD7a1pVChrVTJIc=",
"length": 3888349
}
},
{
"experience": 14252,
"hitpoints": 92233720368512345,
"name": "Aaron2",
"uuid": "78edf902-7dd2-49a4-99b4-1a94ff286a45",
}
Sample O/P:
{
"experience": 14248,
"hitpoints": 9223372036854775807,
"name": "Aaron1",
"uuid": "78edf902-7dd2-49a4-99b4-1c94ee286a33",
},
{
"experience": 14252,
"hitpoints": 92233720368512345,
"name": "Aaron2",
"uuid": "78edf902-7dd2-49a4-99b4-1a94ff286a45",
}
Is there a way to achieve this by using optimized JSON parsing.
Currently, my logic follows the steps:
I'm parsing through the entire object using a function where I'm looping through the node to read the object.
Calling the 'blobChecker' function on every object.
Assigning null to the node if it contains blob.
Skipping the null node in the original function that invokes 'blobChecker'
The original function to parseJSON:
parseJsonNode(JsonNode node){
blobNodeChecker(node);
if(node!=null)
//The funtionality
}
The blobNodeChecker Function:
blobNodeChecker(JsonNode node) {
Boolean isBlob = false;
String blobNode = null;
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> next = fields.next();
String key = next.getKey();
String val = next.getValue().toString().toLowerCase();
if (key.equals("#type")) {
if (val.contains("blob")) {
isBlob = true;
break;
}
}
}
if (isBlob) {
node = null;
}
return node;
}

How about something like below. You can directly read a path and depending upon that delete a node. No need to loop all keys.
String tt = " {" +
" \"experience\": 14248," +
" \"hitpoints\": 9223372036854775807," +
" \"name\": \"Aaron1\"," +
" \"uuid\": \"78edf902-7dd2-49a4-99b4-1c94ee286a33\"," +
" \"image\": {" +
" \"#type\": \"blob\"," +
" \"content_type\": \"image/jpeg\"," +
" \"digest\": \"sha1–4xlj1AKFgLdzcD7a1pVChrVTJIc=\"," +
" \"length\": 3888349" +
" }" +
" }";
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
JsonFactory factory = mapper.getFactory();
JsonParser createParser = factory.createParser(tt);
JsonNode actualObj1 = mapper.readTree(createParser);
JsonNode path = actualObj1.path("image").path("#type");
if( path != null && "blob".equalsIgnoreCase(path.asText())) {
((ObjectNode)actualObj1).remove("image");
}
System.out.println(actualObj1.toString());

Related

How to remove key value pairs from a JSON file in java

I was wondering if anyone can help me or hint me towards how to edit the attached dummy JSON file in Java.
As you can see I have a head object that contains many values and children that follow the same pattern.
I wanted to know if there was a way to remove all the keys where the value is -1.
Following is what I was trying based on many websites using jackson:
try {
// create object mapper instance
ObjectMapper mapper = new ObjectMapper();
// convert JSON file to map
Map<?, ?> map = mapper.readValue(Paths.get("test.json").toFile(), Map.class);
// print map entries
for (Map.Entry<?, ?> entry : map.entrySet()) {
isInteger = main.isObjectInteger(entry.getValue());
// System.out.println("if value is all: " + entry.getKey() + "=" + entry.getValue());
//
The above code will display the structure of the file, however my problem is reaching the -1 values inside the children and removing them.
Using the .getClass and .simpleName methods I know that it is an arrayList but I am confused as to how to search through it.
Any help will be greatly appreciated!
In Jackson you can read whole JSON payload as JsonNode and iterate over all properties check given condition. In case condition is met you can remove given field. To do that you need to implement recursive method. Take a look on below example:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
public class JsonRemoveSomeFieldsApp {
public static void main(String[] args) throws IOException {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
JsonNode root = mapper.readTree(jsonFile);
JsonCleaner jsonCleaner = new JsonCleaner(root, (node) -> node.isNumber() && node.numberValue().intValue() == -1);
JsonNode result = jsonCleaner.removeAll();
// write to file
mapper.writeValue(System.out, result);
}
}
class JsonCleaner {
private final JsonNode root;
private final Predicate<JsonNode> toRemoveCondition;
JsonCleaner(JsonNode node, Predicate<JsonNode> toRemoveCondition) {
this.root = Objects.requireNonNull(node);
this.toRemoveCondition = Objects.requireNonNull(toRemoveCondition);
}
public JsonNode removeAll() {
process(root);
return root;
}
private void process(JsonNode node) {
if (node.isObject()) {
ObjectNode object = (ObjectNode) node;
Iterator<Map.Entry<String, JsonNode>> fields = object.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
JsonNode valueToCheck = field.getValue();
if (valueToCheck.isContainerNode()) {
process(valueToCheck);
} else if (toRemoveCondition.test(valueToCheck)) {
fields.remove();
}
}
} else if (node.isArray()) {
ArrayNode array = (ArrayNode) node;
array.elements().forEachRemaining(this::process);
}
}
}
For below JSON payload:
{
"name": "Head",
"missed": -1,
"covered": -1,
"children": [
{
"name": "project1",
"missed": -1,
"covered": -1,
"children": [
{
"name": "project1",
"missed": 10,
"covered": 11
}
]
},
{
"name": "project1",
"missed": -1,
"covered": 12,
"children": [
{
"name": "project1",
"missed": 10,
"covered": -1
}
]
}
]
}
above code prints:
{
"name" : "Head",
"children" : [ {
"name" : "project1",
"children" : [ {
"name" : "project1",
"missed" : 10,
"covered" : 11
} ]
}, {
"name" : "project1",
"covered" : 12,
"children" : [ {
"name" : "project1",
"missed" : 10
} ]
} ]
}
See also:
Flattening a 3 level nested JSON string in java
There are two main techniques to parse and generate JSON data (as well as many other formats like XML etc): object mapping and event/token/stream-oriented processing. The second way is the best way for many cases, including filtering. Props:
the file/data doesn't require to be loaded entirely into memory, you
can process megs/gigs with no problems
it works much more faster, especially for large files
it's easy to implement any custom type/rule of transformation with this pattern
Both Gson and Jackson supports stream-oriented processing. To illustrate the idea here is just an example using a tiny parser/generator https://github.com/anatolygudkov/green-jelly
import org.green.jelly.AppendableWriter;
import org.green.jelly.JsonBufferedWriter;
import org.green.jelly.JsonEventPump;
import org.green.jelly.JsonNumber;
import org.green.jelly.JsonParser;
import java.io.StringWriter;
public class UpdateMyJson {
private static final String jsonToUpdate = "{\n" +
"\"name\": \"Head\",\n" +
"\"missed\": -1,\n" +
"\"children\": [\n" +
" {\n" +
" \"name\": \"project1\",\n" +
" \"fixes\": 0,\n" +
" \"commits\": -1,\n" +
" },\n" +
" {\n" +
" \"name\": \"project2\",\n" +
" \"fixes\": 20,\n" +
" \"commits\": 5,\n" +
" }\n" +
"]\n" +
"}";
public static void main(String[] args) {
final StringWriter result = new StringWriter(); // you can use FileWriter
final JsonParser parser = new JsonParser();
parser.setListener(new MyJsonUpdater(new AppendableWriter<>(result)));
parser.parseAndEoj(jsonToUpdate); // if you read a file with a buffer,
// to don't load the whole file into memory,
// call parse() several times (part by part) in a loop until EOF
// and then call .eoj()
System.out.println(result);
}
static class MyJsonUpdater extends JsonEventPump {
MyJsonUpdater(final JsonBufferedWriter output) {
super(output);
}
#Override
public boolean onNumberValue(final JsonNumber number) {
if (number.mantissa() == -1 && number.exp() == 0) {
return true; // return immediately
}
return super.onNumberValue(number); // otherwise pump the value to the result JSON
}
}
}

Passing json string as an input to one of the parameters of a POST request body in RESTAssured with Selenium

I am using RESTAssured java library in Selenium for API test automation. I need to pass a json string as a value to one parameter of a POST request body. My request body looks like this:
{
"parameter1": "abc",
"parameter2": "def",
"parameter3": {
"id": "",
"key1": "test123",
"prod1": {
"id": "",
"key3": "test123",
"key4": "12334",
"key5": "3",
"key6": "234334"
},
"prod2": {
"id": "",
"key7": "test234",
"key8": "1",
"key9": true
}
},
"parameter4": false,
"parameter5": "ghi"
}
For parameter3 I need to be pass a string value in json format. The json file is located in my local system and is a huge file, so it would make sense if I can pass the path to the json file.
Is there any way using RestAssured to achieve this?
Use org.json library;
Read json file and get as a String
String content = "";
try {
content = new String(Files.readAllBytes(Paths.get("absolute_path_to_file\\example.json")));
} catch (IOException e) {
e.printStackTrace();
}
Covert the String to JSONObject
JSONObject jsonObject = new JSONObject(content);
Get the new json object that you need to put in the jsonObject
String jsonString = "{\n" +
" \"firstName\": \"John\",\n" +
" \"lastName\" : \"doe\",\n" +
" \"age\" : 26,\n" +
" \"address\" : {\n" +
" \"streetAddress\": \"naist street\",\n" +
" \"city\" : \"Nara\",\n" +
" \"postalCode\" : \"630-0192\"\n" +
" }\n" +
"}";
JSONObject updateObject = new JSONObject(jsonString);
Replace the value of parameter3 with new updateObject
jsonObject.put("parameter3", updateObject);
System.out.println(jsonObject.toString());
If you beautify the printed output;
{
"parameter5": "ghi",
"parameter4": false,
"parameter3": {
"firstName": "John",
"lastName": "doe",
"address": {
"streetAddress": "naist street",
"city": "Nara",
"postalCode": "630-0192"
},
"age": 26
},
"parameter2": "def",
"parameter1": "abc"
}
If you want to update a nested json object like prod1 in parameter3
JSONObject parameter3JsonObject = jsonObject.getJSONObject("parameter3");
parameter3JsonObject.put("prod1", updateObject);

How to customize a json that is created using Gson object in java

I have this JSON object that I've created using GSON:
{
"data": {
"isDeleted": false,
"period": 201601,
"columnMap": {
"1c49eb80-7b53-11e6-bc4b-afbeabb62718": "5000",
"1c49eb80-7b52-11e6-bc4b-afbeabb62718": "hello",
"03a534c0-a7f1-11e6-9cde-493bf5c47f4": "AUS",
"03a534c0-a7fa-11e6-9cde-493bf5c47f4": "123"
}
}
}
But my requirement is for it to look like
{
"data": {
"isDeleted": false,
"period": 201601,
"1c49eb80-7b53-11e6-bc4b-afbeabb62718": "5000",
"1c49eb80-7b52-11e6-bc4b-afbeabb62718": "hello",
"03a534c0-a7f1-11e6-9cde-493bf5c47f4": "AUS",
"03a534c0-a7fa-11e6-9cde-493bf5c47f4": "123"
}
}
}
How do I solve this, because all the values in "columnMap" are generated dynamically.
You need to create instance updateJsonObj of JsonObject to update key and value of columnMap using for each loop. Following code snippet is the solution :
String json = "{ \"data\": {\"isDeleted\": false,\"period\": 201601,"
+ "\"columnMap\": {\"1c49eb80-7b53-11e6-bc4b-afbeabb62718\": \"5000\","
+ "\"1c49eb80-7b52-11e6-bc4b-afbeabb62718\": \"hello\","
+ "\"03a534c0-a7f1-11e6-9cde-493bf5c47f4\": \"AUS\", "
+ "\"03a534c0-a7fa-11e6-9cde-493bf5c47f4\": \"123\"}}}";
Gson gson = new Gson();
JsonObject root = new JsonParser().parse(json).getAsJsonObject();
JsonElement dataElement = root.get("data");
// cobj has value of colummMap of json data
JsonObject cobj = (JsonObject) root.get("data").getAsJsonObject().get("columnMap");
JsonObject updateJsonObj = root;
// remove columnMap node as you wanted !
updateJsonObj.get("data").getAsJsonObject().remove("columnMap");
for (Entry<String, JsonElement> e : cobj.entrySet()) {
//update updateJsonObj root node with key and value of columnMap
updateJsonObj.get("data").getAsJsonObject().addProperty(e.getKey(), e.getValue().getAsString());
}
String updateJson = gson.toJson(updateJsonObj);
System.out.println(updateJson);

Issue with conversion of Map inside a Map into JSON in Java

I have a treemap in Java which has structure like this below:
public static Map<String, Map<String, Integer>> testMap = new TreeMap<String, Map<String, Integer>>();
public String getTestMapInJson(Map<String, Map<String, Integer>> testMap){
ObjectMapper mapper = new ObjectMapper();
String testMapInJson = mapper.writeValueAsString(testMap);
return testMapInJson;
}
Now, when I am trying to convert this map in JSON using Jackson API(snippet above), I am getting data like this and I want to populate this data in HTML tables.
[
{
"key": "1.1.1.1",
"value": {
"string1": 500,
"string2": 400
}
},
{
"key": "2.2.2.2",
"value": {
"string1": 500,
"string2": 400
}
}
]
HTML table output is like:
IP Address Value
1.1.1.1 [object Object]
2.2.2.2 [object Object]
I want it like this:
IP Address Value
1.1.1.1 String1 count1
String2 count2
2.2.2.2 String1 count1
String2 count2
The internal hashmap doesn't get converted exactly into JSON format. I want something so that the internal hashmap too gets converted into similar map like the outer map.
It's feasible for me to make changes on any side (client/server). What should I do?
var resp = [
{
"ip": "1.1.1.1",
"data": {
"string1": 500,
"string2": 400
}
},
{
"ip": "2.2.2.2",
"data": {
"string1": 500,
"string2": 400
}
}
]
To access the Object:
for(var obj : resp) {
$.each(obj, function(key,value){
alert(key + "==" + value);
if(key == "data") {
$.each(value, function(innerKey, innerVal) {
alert(innerKey + "===" + innerVal);
});
}
});
}
To design table:
var table = $("<table>");
//header
var header = $("<tr>").append($("<th>").text("IP Address")).append($("<th>").text("Value"));
for(var obj : resp) {
$.each(obj, function(key,value){
var isFirst = true;
if(key == "data") {
$.each(value, function(innerKey, innerVal) {
var tr;
if(isFirst) {
// ip address and value
tr = $("<tr>").append($("<td>").text(value)).append($("<td>").text(innerKey + " " + innerValue));
isFirst = false;
} else {
// blank and value
tr = $("<tr>").append($("<td>").text("")).append($("<td>").text(innerKey + " " + innerValue));
}
table.append(tr);
});
}
});
}

Java - JSON with undefined Keys

I have to read a JSON without knowing the keys.
My JSON is like this:
{
"hello": {
"bye": {
"a": "1",
"b": "2",
"c": "3"
},
"d": "4",
"e": "5"
},
"hey": {
"qwer": {
"asdf": "1",
"fda": "2",
"bc": "3"
},
"dq": "4"
}
}
just with more data and more elements.
I would like to access them like a map using a path, just with e.g.
get() for hello&hey,
get("hello") for bye,d&e,
get("hello","bye") for a,b&c
...
I hope you understood me, I usually use Gson to parse JSON but passing a corresponding class (like AClass c = new Gson.fromJson(myJson, AClass.class);), but without knowing the JSON structure, I cannot use this approach.
It would be also great if I could easily remove and add entries somewhere.
To do what you need, you can still use Gson, but you need a more "low level" class like the JsonParser.
Here I provide you an example on how you can navigate the JsonElement tree generated by the parser to reach the requested node. It works only on syntaxes like you requested (node1/node2/..) but you can easily adapt also to cases like this: node1/3/node3/4/2 where the number indicates the child.
When you reach the node, you can parse using a Gson if you need, since you can pass to Gson::fromJson a JsonElement also. Here's the code:
package stackoverflow.questions;
import java.util.*;
import com.google.gson.*;
public class Q20883087 {
public static JsonElement get(JsonElement je, String... args){
if(args.length == 0)
return je;
if (je != null && je.isJsonObject()){
String[] newArgs = Arrays.copyOfRange(args, 1, args.length);
return get(je.getAsJsonObject().get(args[0]), newArgs);
}
}
public static void main(String[] args) {
String json ="{ "+
" \"hello\": { "+
" \"bye\": { "+
" \"a\": \"1\", "+
" \"b\": \"2\", "+
" \"c\": \"3\" "+
" }, "+
" \"d\": \"4\", "+
" \"e\": \"5\" "+
" }, "+
" \"hey\": { "+
" \"qwer\": { "+
" \"asdf\": \"1\", "+
" \"fda\": \"2\", "+
" \"bc\": \"3\" "+
" }, "+
" \"dq\": \"4\" "+
" } "+
" } ";
JsonElement jsonElement = new JsonParser().parse(json);
System.out.println(get(jsonElement, "hello","bye"));
System.out.println(get(jsonElement, "hey","qwer"));
}
}
The result is:
{"a":"1","b":"2","c":"3"}
{"asdf":"1","fda":"2","bc":"3"}
Pay attention to the recursive nature of the get method, since Json is recursive by nature, the best way to manage it is using a recursive method.

Categories

Resources