4

I have a function that allows me to filter out certain JSON files using another JSON object as filter.

See code:

public Map<String, Entry<JsonObject, Long>> loadFilter(Coll<?> coll, JsonObject filter){
    // Create Ret
    Map<String, Entry<JsonObject, Long>> ret = null;

    // Get Directory
    File directory = getDirectory(coll);
    if ( ! directory.isDirectory()) return ret;

    // Find All
    File[] files = directory.listFiles(JsonFileFilter.get());

    // Create Ret
    ret = new LinkedHashMap<String, Entry<JsonObject, Long>>(files.length);

    // Filter rules
    Set<Map.Entry<String, JsonElement>> filterRules = filter.entrySet();

    // For Each Found
    for (File file : files)
    {
        // Get ID
        String id = idFromFile(file);

        // Get Entry
        Entry<JsonObject, Long> entry = loadFile(file);

        // Trying to fix a weird condition causing a NPE error
        if(entry == null) continue;
        if(entry.getKey() == null) continue;

        // Compare the files with the given filter
        Set<Map.Entry<String, JsonElement>> fileEntries = entry.getKey().entrySet();
        if (fileEntries.containsAll(filterRules)) {
            // Add found data to return list
            ret.put(id, entry);
        }
    }

    return ret;
}

Imagine I have the following JSON:

{
    "objects": [
        "object1",
        "object2"
    ],
}

What I'm trying to do is filter out any files where the array objects contains object1. I don't care about object 2, I wish to filter out files that have at least object1 in the objects array.

The code below results in nothing:

JsonObject filter = new JsonObject();
JsonArray array = new JsonArray();
array.add(new JsonPrimitive("object1"));
filter.add("objects", array);
Map<String, Entry<JsonObject, Long>> result = loadFilter(coll, filter); // nothing

Any help is appriciated.

1 Answers1

2

Your code

if (fileEntries.containsAll(filterRules)) {

checks whether the file contains equal elements, so, in case of arrays, it checks whether the arrays are equal, not whether the one contains elements of the other.

There's no native way to do the comparison you need in Gson, so it has to be done in your code.

I would suggest a solution like this:

private static boolean checkJsonPredicate(JsonElement element, JsonElement predicate) {
    if (predicate == null) {
        return true;
    }

    if (element == null || predicate.getClass() != element.getClass()) {
        return false;
    }

    if (predicate.isJsonObject()) {
        return predicate.getAsJsonObject().entrySet().stream()
                .allMatch(e -> checkJsonPredicate(element.getAsJsonObject().get(e.getKey()), e.getValue()));
    }

    if (predicate.isJsonArray()) {
        return StreamSupport.stream(predicate.getAsJsonArray().spliterator(), false)
                .allMatch(element.getAsJsonArray()::contains);
    }

    return predicate.equals(element);
}

I used Stream API to check whether the array in the element JSON contains all the elements from the predicate JSON.

This code handles nested objects (so it would still work even if your array wasn't on the root level). However if the array itself contains objects, the objects would have to be equal.

izstas
  • 5,004
  • 3
  • 42
  • 56