在JSON中将逻辑表示为数据
出于商业原因,我们需要将一些条件逻辑外部化为外部文件:最好是JSON。
一个简单的过滤scheme可以通过添加一个节点来处理,如下所示:
"filter": [ { "criteria": "status", "value": "open", "condition": "==" } ]
数组中的其他值可以处理多个条件。
"filter": [ { "criteria": "status", "value": "open", "condition": "==" }, { "criteria": "condition2", "value": "value2", "condition": "==" } ]
然而,当我们处理涉及AND或OR的复杂条件时,会有点混淆。
问题:在JSON中表示这种逻辑是否有一种标准化(甚至被广泛接受)的格式? 如果这取决于你,你会怎么做?
注:第一个答案已经成为一个可编辑的维基,所以任何人都可以改善它。
如果你必须使用标准的JSON实现这个,我会推荐类似于Lisp的“Sexpression式”。 条件可以是普通对象,也可以是第一个条目是join它们的逻辑操作的数组。
例如:
["AND", {"var1" : "value1"}, ["OR", { "var2" : "value2" }, { "var3" : "value3" } ] ]
将代表var1 == value1 AND (var2 == value2 OR var3 == value3)
。
如果你喜欢简洁而不是一致性,你也可以允许一个对象具有多个属性,这些属性将隐含地被AND连接。 例如, { "a": "b", "c": "d" }
等效于["AND", { "a": "b" }, { "c": "d" }]
。 但是有些情况下(比如这个例子),前面的语法不能忠实地表示书写的状态; 你需要额外的技巧来翻译条件或使用虚拟属性名称。 后者的语法应该始终工作。
我需要一个格式,将:
- 支持比较平等以外的比较。
- 让variables出现在任何位置,而不仅仅是比较文字。
- 一致,简洁,安全和可扩展。
所以我build立了一个叫做JsonLogic的格式。 规则是JSON对象,操作符位于键位置,值位置中有一个或一个参数数组。 (受Amazon CloudFormationfunction的启发)任何参数都可以是另一个规则,因此您可以构build任意深度的逻辑。
我还为它编写了两个parsing器: JsonLogic for JavaScript和JsonLogic for PHP 。
cHao的例子会写成
{ "and", [ {"==", [ {"var" : "var1"}, "value1" ]}, { "or", [ {"==", [ {"var" : "var2"}, "value2" ]}, {"==", [ {"var" : "var3"}, "value3" ]} ]} ]}
var
这里是运算符获得“数据”对象的属性,与“规则”对象一起传递给parsing器,例如:
jsonLogic( {"==", [{"var":"filling"}, "apple"]} // rule, is this pie apple? {"filling":"apple", "temperature":100} // data, a pie I'm inspecting ); // true
有更多的可能的运算符(大于,不等于,数组,三元等),这两个parsing器都可以在GitHub上(unit testing和文档)。
我有一个类似的需求(在JavaScript中build立一个SQL where子句)。 我创build了以下javascript函数:
function parseQuery(queryOperation){ var query=""; if (queryOperation.operator == 'and') query = "(" + parseQuery(queryOperation.leftOp) + ") AND (" + parseQuery(queryOperation.rightOp) + ")"; if (queryOperation.operator == 'or') query = "(" + parseQuery(queryOperation.leftOp) + ") OR (" + parseQuery(queryOperation.rightOp) + ")"; if (queryOperation.operator == '=') query = "(" + queryOperation.leftOp +" = "+ queryOperation.rightOp + ")"; return query; }
我创build我的queryOperation像这样:
var queryObject = { operator: 'and', leftOp: { leftOp: 'tradedate', operator: '=', rightOp: new Date() }, rightOp: { operator: 'or', leftOp: { leftOp: 'systemid', operator: '=', rightOp: 9 }, rightOp: { leftOp: 'systemid', operator: '=', rightOp:10 } } };
当我通过我的queryOperation ParseQuery它返回((tradedate = Thu Jul 24 17:30:37 EDT 2014))AND(((systemid = 9))OR((systemid = 10)))
我需要添加一些types转换和其他操作符,但基本结构起作用。
顺便提一下, IBM DB2支持以JSON编码的逻辑语句 。
布尔操作看起来像cHao的解决scheme和Amazon CloudFormation之间的交叉:
{"$and":[{"age":5},{"name":"Joe"}]}
比较操作对于我来说就像音译SQL一样。 (而不是Amazon或Russellg或cHao的抽象语法树)。
{"age":{"$lt":3}}
请检查(JSL)[ https://www.npmjs.com/package/lib-jsl ]。 它似乎符合给出的描述。
这是一个示例:
var JSL = require('lib-jsl'); var bugs = [ [{ bug : { desc: 'this is bug1 open', status : 'open' } }], [{ bug : { desc: 'this is bug2 resolved', status : 'resolved' } }], [{ bug : { desc: 'this is bug3 closed' , status : 'closed' } }], [{ bug : { desc: 'this is bug4 open', status : 'open' } }], [{ bug : { desc: 'this is bug5 resolved', status : 'resolved' } }], [{ bug : { desc: 'this is bug6 open', status : 'open' } }], [ { workInProgress : '$bug'}, { bug : '$bug'}, { $or : [ { $bind : [ '$bug', { status : 'open'} ] }, { $bind : [ '$bug', { status : 'resolved'} ] } ] } ] ]; var query = [{workInProgress : '$wip'}] var transform = '$wip' var jsl = new JSL ({ rules : bugs, query : query, transform : transform }); var retval = jsl.run(); console.log(JSON.stringify(retval, null,2));
答复是:
[ { "desc": "this is bug1 open", "status": "open" }, { "desc": "this is bug2 resolved", "status": "resolved" }, { "desc": "this is bug4 open", "status": "open" }, { "desc": "this is bug5 resolved", "status": "resolved" }, { "desc": "this is bug6 open", "status": "open" } ]
主要工作由规则workInProgress中定义的查询完成:
[ { workInProgress : '$bug'}, { bug : '$bug'}, { $or : [ { $bind : [ '$bug', { status : 'open'} ] }, { $bind : [ '$bug', { status : 'resolved'} ] } ] } ]
这条规则可以被理解为:
为了满足workInProgress的查询,我们定义一个variables{workInProgress : '$bug'}
,然后我们使用规则的下一部分{bug : '$bug'}
来匹配数据库中的所有错误。 这部分匹配所有的错误,因为对象的形状(它是键:'错误')匹配数据库中的错误logging。 该规则进一步要求$ bugvariables在包含相关状态值(打开和closures)的模式下绑定(编辑)。 只有那些在$ bug中的状态值满足规则主体的所有部分的buglogging才有资格获得结果。
最终使用变换规范transform : '$wip'
结果: transform : '$wip'
,它实际上要求查询的$ wipvariables中返回的所有值的数组。
我的同事提出了这个可能的解决办法
“所有OR条件将是一个数组而AND条件将是对象,
例如,OR可以匹配数组中的任何对象:
[ { "var1":"value1" }, { "var2":"value2" }, { "var3":"value3" } ]
将是
{ "var1":"val1", "var2":"val2", "var3":"val3" }