Remove element.field from ArrayNode - java

I have such json ArrayNode and I need to remove from each element for example field "xxx" using ObjectMapper, ArrayNode, JsonNode or ObjectNode. But without Gson and #JsonIgnore etc.
"arrayNode": [
{
"xxx": {},
"yyy": {}
},
{
"xxx": {},
"yyy": {}
}
]

I am not sure whether this problem has been solved or not. But following code snippet shows how to remove a field whose key is xxx from JSON node. And a JsonNode cannot perform insertion or deletion, so you have to cast it to ObjectNode for further manipulation.
Code snippet
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonStr);
rootNode.get("arrayNode").forEach(e -> {
if (e.has("xxx")) {
ObjectNode objNode = (ObjectNode) e;
objNode.remove("xxx");
}
});
System.out.println(rootNode.toString());
Console output
{"arrayNode":[{"yyy":{}},{"yyy":{}}]}

You can use this maven dependency : http://mvnrepository.com/artifact/org.json/json/20160212
It's very simple to understated and use. ex:
JSONObject obj = "YOUR_JSON_STRING";
JSONArray result = obj.getJSONArray("YOUR_STRING_KEY");
for(JSONObject elem : result){
String out = elem.getString("xxx");
}
More you can read at : https://developer.android.com/reference/org/json/JSONArray.html
Good luck

Related

Java jackson parse large JSON to XML with specific xml array format

Help me figure out how to convert Jason node to xml with Jacson library.
My JSON is large (from 10 to 200 mb) and contains many objects. So I won’t be able to convert through the class and use #JacksonXmlProperty(localName = "someName"). It because this json has a lotof dynamic elements. And the problem is that the format of arrays in xml should be:
<test_data>
<data_type>numeric</data_type>
<value>
<Item>0</Item>
<Item>1</Item>
</value>
</test_data>
and jason element looks like this:
{
"test_data": {
"data_type": "numeric",
"value": [
0,
1
]
}
}
if we take this xml and convert it like this:
public static void main(String[] args) throws IOException {
String xmlStr = """
<test_data>
<data_type>numeric</data_type>
<value>
<Item>0</Item>
<Item>1</Item>
</value>
</test_data>
""";
XmlMapper xmlMapper = new XmlMapper();
JsonNode xml = xmlMapper.readTree(xmlStr);
ObjectMapper jsonMapper = new JsonMapper();
System.out.println(jsonMapper.writeValueAsString(xml));
}
OUTPUT is :
{"data_type":"numeric","value":{"Item":["0","1"]}}
visa versa:
String jsonStr = """
{
"test_data" : {
"data_type" : "numeric",
"value" : [ 0, 1 ]
}
}
""";
ObjectMapper jsonMapper = new JsonMapper();
JsonNode node = jsonMapper.readTree(jsonStr.getBytes());
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
String xml = xmlMapper.writeValueAsString(node);
System.out.println(xml);
OUTPUT:
<ObjectNode>
<test_data>
<data_type>numeric</data_type>
<value>0</value>
<value>1</value>
</test_data>
</ObjectNode>
Simply say, I have JsonNode and I need xml String with specific array format, any idea?
Try find a way to create JsonSerializer for JsonNode but failed.
I didn't find a way to do it on XmlMapper side, so I just modified a input json. Replaced all arrays with name "value" by object with name "value" and copied array`s items to new array with name "Item". code example looks like:
private void changeArrayToObject(JsonNode node) {
if (node.isObject()) {
ObjectNode objectNode = (ObjectNode) node;
Iterator<Map.Entry<String, JsonNode>> iter = objectNode.fields();
while (iter.hasNext()) {
Map.Entry<String, JsonNode> entry = iter.next();
if (entry.getKey() == "value" && entry.getValue().isArray()) {
ArrayNode arrayNode = (ArrayNode) entry.getValue();
ObjectNode newObject = new ObjectNode(new JsonNodeFactory(true));
entry.setValue(newObject.set("Item", arrayNode));
if(isArrayValueObjOrNot(arrayNode)) {
for (JsonNode nextNode : arrayNode) {
changeArrayToObject(nextNode);
}
}
} else {
changeArrayToObject(entry.getValue());
}
}
}
}
private Boolean isArrayValueObjOrNot(ArrayNode arrayNode) {
JsonNode firstElement = arrayNode.get(0);
if (firstElement.isObject())
return true;
return false;
}

How to update a certain value inside this jackson JsonNode?

TLDR:
I want to update certain value of a JsonNode key dependsOn and return the result as a JsonNode. Currently I'm converting the value to a String, slicing the characters and then using ObjectMapper to convert the string back to JsonNode
I have a json object like shown below
{
"name": "somename",
"type": "sometype",
"description": "some desc",
"properties": {
"path": "some path",
"dependsOn": [
"ABC:zzz","DEF:sdc","GHI:ere"
],
"checkpoint": "some checkpoint",
"format": "some format",
"output": "some output",
"table": "some table"
}
}
I'm currently parsing the above json data and fetching the dependsOn as JsonNode element (as shown below)
JsonNode components = model.get("properties");
JsonNode dependsOn = components.get("dependsOn");
When I print dependsOn it looks like this "["ABC:zzz","DEF:sdc","GHI:ere"]"
My requirement was to strip everything after : from the dependsOn array
This below code helped me to convert the JsonNode to String and then strip :whatever then convert it back to JsonNode
if (dependsOn != null && !dependsOn.isEmpty()) {
String dependsOnString =
components
.get("dependsOn")
.get(0)
.textValue()
.substring(
0,
(components.get("dependsOn").get(0).textValue().lastIndexOf(":") != -1)
? components.get("dependsOn").get(0).textValue().lastIndexOf(":")
: components.get("dependsOn").get(0).textValue().length());
ObjectMapper mapper = new ObjectMapper();
dependsOn = mapper.readTree("[\"" + dependsOnString + "\"]");
}
Input:
"["ABC:zzz","DEF:sdc","GHI:ere"]"
Output
"["ABC","DEF:sdc","GHI:ere"]"
Above code only strip the first element of the array I can loop and perform the same for rest of the elements though. But I have a couple of questions regarding whatever I'm trying to do
Firstly, am I doing this in a right way or is there a simpler
technique to do this? instead of converting it to string and then
again to JsonNode..
Next, I've only done this to the first element of the array
and I want to loop through and do this for all the elements of the array. Is there a simpler solution to this instead of using a for/while loop?
This should work, without convert to string and parse again to jsonNode
JsonNode prop = node.get("properties");
JsonNode arrayCopy = prop.get("dependsOn").deepCopy();
var array = ((ObjectNode)prop).putArray("dependsOn");
IntStream.range(0, arrayCopy.size())
.forEach(index -> {
String elem = arrayCopy.get(index).asText();
String finalElem = elem.substring(0,elem.contains(":") ? elem.lastIndexOf(':') : elem.length());
array.add(finalElem);
});
Since my usecase suggests my dependsOn value should not be overridden at node level, I had to convert the JsonNode to String and then used the regular expression matcher to replace :xyz with an empty string in each element then convert it back to JsonNode
String pattern = ":[a-zA-Z]+";
String newDependsOn = dependsOn.toString().replaceAll(pattern, "");
ObjectMapper mapper = new ObjectMapper();
dependsOn = mapper.readTree(newDependsOn);
#Gautham's solution did work too but what I think is it was overriding at the root and the old value wasn't available anymore outside the loop
You can iterate the dependsOn after casting it to ArrayNode and set value to it:
ArrayNode array = ((ArrayNode) dependsOn);
List<String> newValues = new ArrayList<>();
for(int i=0;i<array.size();i++) {
newValues.add(array.get(i).asText().split(":")[0]);
}
array.removeAll();
newValues.forEach(array::add);
EDIT: If you don't want your original dependsOn to be updated then use:
JsonNode copy = dependsOn.deepCopy();
// or you could invoke `deepCopy` on the `ArrayNode` as well
Now pass this copy object for slicing operation. So that the original json remains unchanged.

Mapping Json Array to POJO using Jackson

I have a JSON array of the form:
[
[
1232324343,
"A",
"B",
3333,
"E"
],
[
12345424343,
"N",
"M",
3133,
"R"
]
]
I want to map each element of the parent array to a POJO using the Jackson library. I tried this:
ABC abc = new ABC();
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(data).get("results");
if (jsonNode.isArray()) {
for (JsonNode node : jsonNode) {
String nodeContent = mapper.writeValueAsString(node);
abc = mapper.readValue(nodeContent,ABC.class);
System.out.println("Data: " + abc.getA());
}
}
where ABC is my POJO class and abc is the object but I get the following exception:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.demo.json.model.ABC
EDIT:
My POJO looks like this:
class ABC{
long time;
String a;
String b;
int status;
String c;
}
Can someone suggest a solution for this?
EDIT 2: After consulting a lot of answers on StackOverflow and other forums, I came across one solution. I mapped the returned value of readValue() method into an array of POJO objects.
ABC[] abc = mapper.readValue(nodeContent, ABC[].class);
But now I am getting a separate exception
Can not construct instance of ABC: no long/Long-argument constructor/factory method to deserialize from Number value (1552572583232)
I have tried the following but nothing worked:
1. Forcing Jackson to use ints for long values using
mapper.configure(DeserializationFeature.USE_LONG_FOR_INTS, true);
2. Using wrapper class Long instead of long in the POJO
Can anyone help me with this?
You can use ARRAY shape for this object. You can do that using JsonFormat annotation:
#JsonFormat(shape = Shape.ARRAY)
class ABC {
And deserialise it:
ABC[] abcs = mapper.readValue(json, ABC[].class);
EDIT after changes in question.
You example code could look like this:
JsonNode jsonNode = mapper.readTree(json);
if (jsonNode.isArray()) {
for (JsonNode node : jsonNode) {
String nodeContent = mapper.writeValueAsString(node);
ABC abc = mapper.readValue(nodeContent, ABC.class);
System.out.println("Data: " + abc.getA());
}
}
We can use convertValue method and skip serializing process:
JsonNode jsonNode = mapper.readTree(json);
if (jsonNode.isArray()) {
for (JsonNode node : jsonNode) {
ABC abc = mapper.convertValue(node, ABC.class);
System.out.println("Data: " + abc.getA());
}
}
Or even:
JsonNode jsonNode = mapper.readTree(json);
ABC[] abc = mapper.convertValue(jsonNode, ABC[].class);
System.out.println(Arrays.toString(abc));
Your json does not map to the pojo that you have defined. For the pojo that you have defined, the json should be of the form below.
{
"time:1232324343,
"a":"A",
"b":"B",
"status":3333,
"c":"E"
}

How to add new node to Json using JsonPath?

I'm working with JSON and facing some problems.
I want to insert/update a path in a JSON object. In the case that the path doesn't exist, it will be created then I insert a new value. In case that it exits, it will be updated by a new value
For example, I want to add new path like this:
val doc = JsonPath.parse(jsonString)
doc.add("$.user.name", "John")
but I always get this error, because the path doesn't exist:
class com.jayway.jsonpath.PathNotFoundException : Missing property in path $['user']
Therefore I want to create a new path if it does not exist.
This is my code, but jsonString doesn't change:
var jsonString = "{}" val conf = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL).addOptions(Option.SUPPRESS_EXCEPTIONS)
JsonPath.using(conf).parse(jsonString).set(JsonPath.compile("$.user.name"), "John")
Log.d("TAG", "new json = $jsonString")
Please give me your advice. Thank you very much!!
I tried three different JSON libraries with support of JsonPath/JsonPointer (Jackson, JsonPath and JSON-P) and none of them is able to reconstruct JSON object hierarchy in case of missing parent nodes. So I came up with my own solution for adding new values to JSON object using Jackson/JsonPointer as it allows to navigate through JsonPointer parts.
private static final ObjectMapper mapper = new ObjectMapper();
public void setJsonPointerValue(ObjectNode node, JsonPointer pointer, JsonNode value) {
JsonPointer parentPointer = pointer.head();
JsonNode parentNode = node.at(parentPointer);
String fieldName = pointer.last().toString().substring(1);
if (parentNode.isMissingNode() || parentNode.isNull()) {
parentNode = StringUtils.isNumeric(fieldName) ? mapper.createArrayNode() : mapper.createObjectNode();
setJsonPointerValue(parentPointer, parentNode); // recursively reconstruct hierarchy
}
if (parentNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) parentNode;
int index = Integer.valueOf(fieldName);
// expand array in case index is greater than array size (like JavaScript does)
for (int i = arrayNode.size(); i <= index; i++) {
arrayNode.addNull();
}
arrayNode.set(index, value);
} else if (parentNode.isObject()) {
((ObjectNode) parentNode).set(fieldName, value);
} else {
throw new IllegalArgumentException("`" + fieldName + "` can't be set for parent node `"
+ parentPointer + "` because parent is not a container but " + parentNode.getNodeType().name());
}
}
Usage:
ObjectNode rootNode = mapper.createObjectNode();
setJsonPointerValue(rootNode, JsonPointer.compile("/root/array/0/name"), new TextNode("John"));
setJsonPointerValue(rootNode, JsonPointer.compile("/root/array/0/age"), new IntNode(17));
setJsonPointerValue(rootNode, JsonPointer.compile("/root/array/4"), new IntNode(12));
setJsonPointerValue(rootNode, JsonPointer.compile("/root/object/num"), new IntNode(81));
setJsonPointerValue(rootNode, JsonPointer.compile("/root/object/str"), new TextNode("text"));
setJsonPointerValue(rootNode, JsonPointer.compile("/descr"), new TextNode("description"));
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode));
This generates and prints the following JSON object:
{
"root" : {
"array" : [ {
"name" : "John",
"age" : 17
}, null, null, null, 12 ],
"object" : {
"num" : 81,
"str" : "text"
}
},
"descr" : "description"
}
For sure, this doesn't cover all corner cases but works in most of the cases. Hope this helps someone else.
To create a new node try put(path, key, object) on the WriteContext interface implemented by the result of JsonPath.parse(jsonString).
You can do it as follows:
JsonPath.parse(jsonString).set(JsonPath.compile("$.user.name"), "John");

Add new key value to json node in java

I have JSON file :
{
"id":1,
"name":"abc",
"addressDetails":
{
"City":"NewYork"
}
}
I wanted to add one more key-value ("pinCode" : "414141") to the node 'addressDetails'.
I tried using :
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(new File("D://test.json"));
ObjectNode node = mapper.createObjectNode();
node.with("addressDetails").put("pinCode", "414141");
But it's not getting added, Is there any way I can do this ?
you can try
you should modify the file path
ObjectMapper mapper = new ObjectMapper();
ObjectNode nodes = mapper.readValue(new File("D:\\test.txt"),
ObjectNode.class);
nodes.with("addressDetails").put("pinCode", "414141");
mapper.writer().writeValue(new File("D:\\test.txt"), nodes);
The problem with your code is that you add your value to a new JsonObject (node), and not the one (root) that you've read in. So basically, you'll have to add it to root. But to be able to do that, you'll have to cast it to an ObjectNode, as JsonNode does not provide any methods to add to the node.
So, try something like the following:
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(new File("D://test.json"));
ObjectNode node = (ObjectNode) root;
node.with("addressDetails").put("pinCode", "414141");
mapper.writer().writeValue(new File("D:\\test.txt"), node);

Categories