Check if JSONObject matches JSON boolean expression Check if JSONObject matches JSON boolean expression json json

Check if JSONObject matches JSON boolean expression


Edit: code updated to use org.json.

Below is a working implementation that handles your example.

The function that actually does the work is match, which recursively traverses the filter and applies each node to the supplied object. The function returns true if the given object satisfies the given filter.

Conceptually the match function is mapping each construct in your filter language (AND, OR, EQ, LT etc) into its Java equivalent. E.g. AND maps to Stream.allMatch, NOT maps to the Java ! operator, EQ maps to Object.equals, etc. I.e. match is defining the semantics for your filter language.

I hope the code is self-explanatory - let me know if anything is unclear.

import org.json.*;import java.io.*;import java.nio.file.*;import java.util.List;import java.util.stream.*;import static java.util.stream.Collectors.toList;public class Test {    public static void main(String[] args) throws IOException {        final List<JSONObject> jsObjs =                stream(readJsonArray("users.json"))                        .map(JSONObject.class::cast)                        .collect(toList());        final JSONObject jsFilter = readJsonObject("filter.json");        final List<JSONObject> matches = applyFilter(jsObjs, jsFilter);        System.out.println(matches);    }    private static List<JSONObject> applyFilter(List<JSONObject> jsObjs, JSONObject jsFilter) {        return jsObjs.stream()                .filter(jsObj -> match(jsObj, jsFilter))                .collect(toList());    }    private static boolean match(JSONObject jsObj, JSONObject jsFilter) {        final String name = getSingleKey(jsFilter);        final Object value = jsFilter.get(name);        switch (name) {            case "AND":                return stream((JSONArray)value)                        .map(JSONObject.class::cast)                        .allMatch(jse -> match(jsObj, jse));            case "OR":                return stream((JSONArray)value)                        .map(JSONObject.class::cast)                        .anyMatch(jse -> match(jsObj, jse));            case "NOT":                return !match(jsObj, (JSONObject)((JSONArray)value).get(0));            default:                final JSONObject jsOp = (JSONObject)value;                final String operator = getSingleKey(jsOp);                final Object operand = jsOp.get(operator);                switch (operator) {                    case "eq": return jsObj.get(name).equals(operand);                    case "lt": return (Integer)jsObj.get(name) < (Integer)operand;                    case "gt": return (Integer)jsObj.get(name) > (Integer)operand;                    default: throw new IllegalArgumentException("Unexpected operator: " + operator);                }        }    }    private static JSONObject readJsonObject(String fileName) throws IOException {        try (Reader reader = Files.newBufferedReader(Paths.get(fileName))) {            return new JSONObject(new JSONTokener(reader));        }    }    private static JSONArray readJsonArray(String fileName) throws IOException {        try (Reader reader = Files.newBufferedReader(Paths.get(fileName))) {            return new JSONArray(new JSONTokener(reader));        }    }    private static Stream<Object> stream(JSONArray jsa) {        return StreamSupport.stream(jsa.spliterator(), false);    }    private static String getSingleKey(JSONObject jso) {        if (jso.length() != 1) {            throw new IllegalArgumentException("Expected single entry");        } else {            return jso.keySet().iterator().next();        }    }}