java - 检查 JSONObject 是否匹配 JSON boolean 表达式

标签 java json validation optimization

为了正确解释这个问题,我必须从一个例子开始,假设我有一个这样的用户列表

[
    { "name": "John",  "surname": "Doe",   "age": 22 },
    { "name": "Syrus", "surname": "Black", "age": 20 }
]
我还有一个 JSONObject表示必须像这样匹配的条件:
{
    "OR":[
        { "name": { "eq": "John"} },
        { "AND":[
            { "name": { "eq": "Syrus"} },
            { "age": { "gt": 18 } }
        ] }
    ]
}
应该翻译成:
name = "John" OR (name = "Syrus" AND age > 18)
现在我必须制作给出 JSONObject 的代码条件和用户列表检查每个用户是否符合条件。
目前这就是我所做的:
import java.util.Set;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import com.query.Queryable;
import org.json.JSONArray;
import org.json.JSONObject;

public class QueryableTreeMap<K,V extends JSONObject> extends TreeMap<K,V> implements Queryable<JSONObject,JSONObject> {

    private static final long serialVersionUID = 2586026774025401270L;

    private static boolean test(Set<Map.Entry<String, Object>> condition, JSONObject value){
        boolean isValid = true;
        Iterator<Map.Entry<String, Object>> iter = condition.iterator();
        while(iter.hasNext()){
            Map.Entry<String, Object> subcond = iter.next();
            if(subcond.getKey().equals("OR")){
                //isValid = isValid || test((Set<Map.Entry<String, Object>>) subcond.getValue(), value);
            } else if(subcond.getKey().equals("AND")){
                //isValid = isValid && test((Set<Map.Entry<String, Object>>) subcond.getValue(), value);
            } else if(subcond.getKey().equals("NOT")){
            
            } else {
                
            }
        }
        return isValid;
    }

    @Override
    public JSONObject query(JSONObject query) {
        // the set containing the conditions
        Set<Map.Entry<String, Object>> entries = query.toMap().entrySet();
        // the JSONArray with containing the records that match the condition
        JSONArray array = new JSONArray();
        // for each JSONObject inside this structure
        this.forEach(new BiConsumer<K,JSONObject>(){

            @Override
            public void accept(K key, JSONObject value) {
                // testing if the current record matches the condition
                if(test(entries, value)) array.put(value);
            }
            
        });
        // returns a JSONObject containing a JSONArray that contains the records that match the condition
        return new JSONObject(array);
    }
}
我目前被困在测试方法中,该方法应该实际上测试给定的对象是否与给定的条件匹配。
我不介意更改 JSON 条件的格式,只要它是 JSONObject .
目前,我提出了一个部分解决方案,该解决方案构建了一个名为 Condition 的对象,该对象表示 JSONObject 中的 boolean 表达式(效率不高,但仍然是一个可能的解决方案)这显然目前不起作用,我需要帮助解决我的问题现在应该做
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Condition {

    private Condition left, right;
    private String boolExpr, key, value;
    private boolean not;
    private int operationType; // greater then, less then, equal to, greater or equal of, less or equal of

    public Condition(Map<String, Object> map) {
        Set<Map.Entry<String, Object>> set = map.entrySet();
        Iterator<Map.Entry<String, Object>> iterator = set.iterator();
        while(iterator.hasNext()){
            Object entry = iterator.next();
            System.out.println(entry);
            System.out.println(entry.getClass());
            if(entry instanceof Map.Entry) {
                String key = (String) ((Map.Entry) entry).getKey();
                switch(key){
                    case "AND":
                    case "OR":
                        this.boolExpr = key;
                        break;
                    case "eq":
                    case "gt":
                    case "lt":
                    case "gte":
                    case "lte":
                        this.operationType = getOperationTypeFromString(key);
                        break;
                    case "NOT":
                        this.not = true;
                        break;
                    default:
                        this.key = key;
                        break;
                }
            }
        }
    }

    public int getOperationTypeFromString(String operation) {
        switch(operation){
            case "eq":
                return 0;
            case "gt":
                return 1;
            case "lt":
                return 2;
            case "gte":
                return 3;
            case "lte":
                return 4;
            default:
                return 0;
        }
    }

}
我宁愿不使用 Condition 类,而只使用 JSONObject。
我正在使用 org.json JSON-Java解析器。

最佳答案

编辑:代码更新为使用 org.json。
下面是处理您的示例的工作实现。
实际完成工作的函数是 match ,它递归地遍历过滤器并将每个节点应用于提供的对象。如果给定的对象满足给定的过滤器,则该函数返回 true。
从概念上讲 match函数将过滤器语言中的每个构造( ANDOREQLT 等)映射到它的 Java 等价物。例如。 AND映射到 Stream.allMatch , NOT映射到 Java !运算符(operator),EQ映射到 Object.equals等,即match正在定义过滤器语言的语义。
我希望代码是不言自明的 - 如果有任何不清楚的地方,请告诉我。

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();
        }
    }
}

关于java - 检查 JSONObject 是否匹配 JSON boolean 表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66781122/

相关文章:

java - Tomcat 上的 j_security_check

java - 导入gradle依赖

java - 正则表达式帮助 : Excluding characters

javascript - 如何手动显示来自 JavaScript 函数的 HTML5 验证消息?

java - 用于验证 Linux 文件夹路径的正则表达式模式

java - 将具有列表作为属性的 Java 对象写入和读取到 CSV

javascript - PHP JSON 无法回显值,总是返回数组?

java - 用于向 Cassandra 写入 JSON 响应的示例 Java 代码?

c++ - 如何在安装了 WSL 的 Visual Studio Code 中修复 "g++: error: helloworld.cpp: No such file or directory"?

json - 确保传入的 JSON 响应符合模式?