Search code examples
javaarraysjsonjacksonfasterxml

Getting a SubArray of Fasterxml Jackson ArrayNode


I need to get a sub-array of an ArrayNode object in fasterxml jackson in Java.

To be more clear,

  1. I have a fasterxml jackson ArrayNode object which contains for example let's say 100 objects.
  2. I have a limit parameter for example let's say 5.
  3. Can do it in a very primitive way as indicated below,

    ArrayNode arrayNodeRecProducts = (ArrayNode) recProducts;
    int arrayNodeSize = arrayNodeRecProducts.size();
    
    if (limit >= 0 && limit < arrayNodeSize) {
        while (arrayNodeRecProducts.has(limit)) {
            arrayNodeRecProducts.remove(limit);
        }
    }
    

The "recProducts" object casted to ArrayNode type is a fasterxml jackson JsonNode and contains an array.

Above works but quite inefficient as the inner while loop runs for "arrayNodeSize - limit" number of times in removing the ArrayNode objects one by one.

Is there a sub-array operation which we can perform on the ArrayNode or the casted JsonNode itself?

Thanks and Regards..


Solution

  • Thanks "henrik" for your answer and you were correct in that Jackson doesn't support such a functionality for ArrayNodes. So what I did was I downloaded the Jackson databind codebase and looked inside the hood why they are not providing such a SubArray functionality for ArrayNodes (Please be informed that I am referring to databind 2.3.2).

    Internally, Jackson is maintaining the ArrayNode in a JsonNode List as below,

    private final List<JsonNode> _children = new ArrayList<JsonNode>();
    

    To my surprise, for some reason I cannot understand, they have not provided a SubArray operation which could be easily accomplished by using the subList method of this contained list. For example as below,

    public List<JsonNode> subArray(int fromIndex, int toIndex) {
        return _children.subList(fromIndex, toIndex);
    }
    

    Above method would have saved me from the trouble I was facing but it is simply not included in the library.

    So what I did in my codebase is to simply hack into this private list in runtime using reflection and invoke the subList operation at runtime as below.

    ArrayNode arrayNodeRecProducts = (ArrayNode) recProducts;
    if (limit >= 0 && limit < arrayNodeRecProducts.size()) {
        Field innerArrayNode = ArrayNode.class.getDeclaredField("_children");
        innerArrayNode.setAccessible(true);
        List<JsonNode> innerArrayNodeChildNodes = (List<JsonNode>) innerArrayNode.get(arrayNodeRecProducts);
        List<JsonNode> limitedChildNodes = innerArrayNodeChildNodes.subList(0, limit);
        innerArrayNode.set(arrayNodeRecProducts, limitedChildNodes);
    }
    

    I know that the above code will not work in all situations but for my situation it is working fine.

    At the same time, I know this is a violation of our well guarded OO principle Encapsulation, but I can live with that for reasons explained in below post.

    Dosen't Reflection API break the very purpose of Data encapsulation?