7

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!!

2

4 Answers 4

14

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.

Sign up to request clarification or add additional context in comments.

3 Comments

setJsonPointerValue(node, parentPointer, parentNode); // recursively reconstruct hierarchy
Dude, you saved my sprint
Haha nice, glad it is still helpful for someone 6 years later 🙂
2

To create a new node try put(path, key, object) on the WriteContext interface implemented by the result of JsonPath.parse(jsonString).

Comments

1

You can do it as follows:

JsonPath.parse(jsonString).set(JsonPath.compile("$.user.name"), "John");

6 Comments

I'm supposing that the beginning json is empty like this {} I still get this error class com.jayway.jsonpath.PathNotFoundException : Missing property in path $['user']
Can you try with following: private static Configuration conf = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL).addOptions(Option.SUPPRESS_EXCEPTIONS); JsonPath.using(conf).parse(...
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") I tried your solution but jsonString doesn't change. Could you please help? I'm using Kotlin language. Thank you
It seems that it didn't work. Could you plz help? Thank you
@NumeroUno Even with configuration, this works for a single key, but not for nested path; i.e. <"{}", "$.key", "value"> works, but <"{}", "$.nested.key", "value"> doesn't.
|
0

Put will help you to add or update the key:

JsonPath.parse(jsonString).put("user", "name", "John");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.