0

I have multiple complex (Jackson) JsonNode structures which I need to find a value in. The problem is very similar to: how to parse json array value using json node and Jackson JsonNode Serialization.

So I would like to find a value inside a JsonNode with the following limitation: The context is provided by (Cucumber) tests. e.g. 'Inside "title/configurations/relation" I see the value "a" ' see example below.

The code I've tried to use:

public JsonNode findNodeInPath (String path, JsonNode parent) {
        int firstSlash = path.indexOf("/");
        if (parent == null) {
            return null;
        }
        else {
            String nextNodeName = firstSlash == -1 ? path : path.substring(0, firstSlash);
            if (parent.isObject()) {
                return findNodeInPath(firstSlash == -1 ? path : path.substring(firstSlash + 1), parent.findValue(nextNodeName));
            }
            else { // parent is array
                if (parent.has(path)) {
                    return parent;
                }
                for (JsonNode node : parent) {
                    JsonNode nextNode = node.findValue(nextNodeName);
                    if (nextNode != null) {
                        return findNodeInPath(firstSlash == -1 ? path : path.substring(firstSlash + 1), nextNode);
                    }
                }
            }

            return null; // not found
        }
}

But that doesn't work because when I get to a JsonArray with strings only (like "relation" below) Jackson doesn't let me access the text inside it.

I don't want to create a POJO for each JsonNode because:

  1. The variance between nodes is very big. I can't see any object that will fit even most of the configurations I have.

  2. This is used for testing only. The structures as they are, just sit on the database. I wouldn't like to create objects solely for testing purposes.

An example of a JsonNode I have:

{
  "title": [
    {
      "configuration": [
        {
          "product1": {
            "id": "id1",
            "name": [
              {
                "locale": "en_US",
                "value": "value1"
              }
            ]
          },
          "configured": true,
          "relation": ["a","b","c"],
          "relation2": []
        }
      ]
    }
  ]
}

I apologize for the long question, any help (from any direction) would be greatly appreciated.

Klaymen
  • 73
  • 1
  • 3
  • 9
  • 1
    Jackson has an XPathy thing called JSONPointer that you can use: https://stackoverflow.com/a/31735568/14955 – Thilo Feb 13 '18 at 07:26
  • Actually, using this I still have a problem discerning between a JsonArray of JsonNodes or JsonArrays and a JsonArray with only values. Do you have any pointers for this? – Klaymen Feb 13 '18 at 09:08
  • For example, if I want to check if relation has the value "a" I have to do: root.at("/title").get(0).at("/configuration").get(0).at("/relation").get(0). When the get(i) needs to be checked for all i's since the order of elements in Json isn't preserved. – Klaymen Feb 13 '18 at 09:19
  • 1
    In addition to this, you may want to use `path()` over `get()` since it never returns `null`. Instead you may get `MissingNode`, which can be de-referenced, including traversal. So you can safely chain traversal without null-checks, and only check `isMissingNode()` at the end (or even omit -- accessors give expected values for most things) – StaxMan Feb 13 '18 at 23:52
  • If you want to check the value of "relation" but don't know the path to it, wouldn't you just specify "relation" in the path and it will get all such tags no matter where in the tree? Just like you'd do getElementsByTagName in DOM? – Thilo Feb 15 '18 at 06:26
  • @Thilo my problem is actually the exact opposite, I'm receiving a path in the json, and have to assert whether the value and key in the path match the ones I'm expecting. I probably should've been clearer on that – Klaymen Feb 16 '18 at 10:38
  • The issue with the path is: I can't rely on the ordering of elements and values inside JsonArrays, so I need to check every entryof the array to see if the value is inside somewhere. path() didn't get me far either – Klaymen Feb 16 '18 at 10:45

1 Answers1

0

Extract Array node:

JsonNode node = jsonNode.findPath("relation");

Convert the array node to List of strings:

List<String> relationList = new ObjectMapper().readValue(node.toString(), new TypeReference<List<String>>() {});
firoj_mujawar
  • 301
  • 4
  • 14