Add new key value to json node in java - 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);

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 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");

Jackson: understand if source JSON is an array or an object

Parsing JSON in Jackson library would require:
for an object
MapType hashMapType = typeFactory.constructMapType(HashMap.class, String.class, Object.class);
Map<String, Object> receivedMessageObject = objectMapper.readValue(messageBody, hashMapType);
for an array of objects
Map[] receivedMessage = objectMapper.readValue(messageBody, HashMap[].class)
What would be the best way to check whether I have array or object in messageBody, in order to route to the correct parsing? Is it just to directly check for array token in MessageBody?
An option is just to treat everything that might be an array as an array. This is often most convenient if your source JSON has just been auto-transformed from XML or has been created using an XML-first library like Jettison.
It's a sufficiently common use case that there's a Jackson switch for this:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
You can then just deserialize a property into a collection type, regardless of whether it's an array or an object in the source JSON.
If you want to know whether your input is an array or an object, you can simply use the readTree method. A simple example:
ObjectMapper mapper = new ObjectMapper();
String json1 = "{\"key\": \"value\"}";
String json2 = "[\"key1\", \"key2\"]";
JsonNode tree1 = mapper.readTree(json1);
System.out.println(tree1.isArray());
System.out.println(tree1.isObject());
JsonNode tree2 = mapper.readTree(json2);
System.out.println(tree2.isArray());
System.out.println(tree2.isObject());
If you want to be able to deserialize to multiple types, have a look at Polymorphic Deserialization
This is what I did based on the answer from #ryanp :
public class JsonDataHandler {
public List<MyBeanClass> createJsonObjectList(String jsonString) throws JsonMappingException, JsonProcessingException {
ObjectMapper objMapper = new ObjectMapper();
objMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
List<MyBeanClass> jsonObjectList = objMapper.readValue(jsonString, new TypeReference<List<MyBeanClass>>(){});
return jsonObjectList;
}
}

Remove element.field from ArrayNode

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

Reading JSON value fails

I'm trying to understand some legacy production code.
Here's a test that simulates what the production code does:
Map json = new HashMap();
json.put("messageCategory", "Hello World");
ObjectMapper mapper = new ObjectMapper();
String out = mapper.writeValueAsString(json);
System.out.println(out);
final JsonNode node = mapper.valueToTree(out);
Assert.assertEquals("Hello World", node.findValue("messageCategory"));
the output is:
{"messageCategory":"Hello World"}
junit.framework.AssertionFailedError:
Expected :Hello World
Actual :null
The valueToTree methods returns null and I'm not sure why.
It's null because you are serializing a string in your valueToTree() call. So, after the mapping your node contains a single String of this form:
"{\"messageCategory\":\"Hello World\"}"
And so your node contains no property named 'messageCategory'.
Change
final JsonNode node = mapper.valueToTree(out);
to
final JsonNode node = mapper.valueToTree(json);

Categories