json-logic-js
json-logic-js copied to clipboard
Render an object
Hi,
Is it possible that statement if render an object ? For example :
{
if : [
test, // true or false
{ myProp : "OK" }
]
}
but json-logic try to apply function myProp.
I've tried Object.defineProperty( {}, "myProp", { value : "OK" })...
I found out : jsonLogic.add_operation("JSON", JSON) and then :
{
"if" : [
true,
{ "JSON.parse" : "{ 'myProp' : 'OK'}" }
]
}
I found out : jsonLogic.add_operation("JSON", JSON) and then :
{
"if" : [
true,
{ "JSON.parse" : "{ 'myProp' : 'OK'}" }
]
}
I think it's because apply method tries to "recurse" on every member without testing if a "member" is a json-logic object.
Why not something like
jsonLogic.is_logic = function(logic) {
var op = jsonLogic.get_operator(logic)
return op && op in operations || op == "if")
}
And apply method starts with
jsonLogic.apply = function(logic, data) {
// Does this array contain logic - and now, we test it ;-) ? Only one way to find out.
if(Array.isArray(logic)) {
return logic.map(function(l) {
// return jsonLogic.apply(l, data);
return logic.is_logic(l) ? jsonLogic.apply(l, data) : l;
});
}
// rest
}
No ? It works perfectly for me :-) !
Also running into this issue. "if" should definitely be able to return an object if the condition is true. The error I get when trying to return an object as a conditional is. Interestingly "if" has no problem returning an object when the condition is false.
Error: Unrecognized operation yay
at Object.jsonLogic.apply (/Users/benconant/MyFin/rabbit/node_modules/json-logic-js/logic.js:368:11)
at Object.jsonLogic.apply (/Users/benconant/MyFin/rabbit/node_modules/json-logic-js/logic.js:251:28)
at Function.evaluateDecision (/Users/benconant/MyFin/rabbit/index.js:8:31)
at Object.<anonymous> (/Users/benconant/MyFin/rabbit/index.js:18:26)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10) 'Error: Unrecognized operation yay\n at Object.jsonLogic.apply (/Users/benconant/MyFin/rabbit/node_modules/json-logic-js/logic.js:368:11)\n at Object.jsonLogic.apply (/Users/benconant/MyFin/rabbit/node_modules/json-logic-js/logic.js:251:28)\n at Function.evaluateDecision (/Users/benconant/MyFin/rabbit/index.js:8:31)\n at Object.<anonymous> (/Users/benconant/MyFin/rabbit/index.js:18:26)\n at Module._compile (module.js:652:30)\n at Object.Module._extensions..js (module.js:663:10)\n at Module.load (module.js:565:32)\n at tryModuleLoad (module.js:505:12)\n at Function.Module._load (module.js:497:3)\n at Function.Module.runMain (module.js:693:10)'
So I have a workaround that does not require you to mess with the internals / fork.
@CedrikWinter identified the problem. json_logic.apply attempts to recursively call itself on all objects in an array. The problem is that when json_logic.apply gets down to a single object it checks to see if an object is logic using the json_logic.is_logic function. This function treats any object with a single key as logic.
jsonLogic.is_logic = function(logic) {
return (
typeof logic === "object" && // An object
logic !== null && // but not null
! Array.isArray(logic) && // and not an array
Object.keys(logic).length === 1 // with exactly one key
);
};
so { "Iamnotlogicatall" : true } is consideted logic. Which I assume it should not be.
All I am going to do is enforce that my outcomes that are objects always have at least two keys. Though it would be nice if this could be fixed! So all my object outcomes will look like
"logic": { "if": [
{
"and": [
{"==" : [ {"var" : "productId" }, "1" ] },
{"==" : [ {"var" : "existingCustomer" }, true ] }
]
},
{ "jsonLogicOutcome": true, "yay" : 1 },
"defaultOutcome"
] },
I then just delete the jsonLogicOutcome in the part of the code responsible for interfacing with json-logic-js's apply function. This is the part of my code that does that:
class Rabbit {
static evaluateDecision (options) {
const { input, decision } = options
const outcome = jsonLogic.apply(decision.logic, input)
if (outcome && outcome !== "defaultOutcome") {
if (_.isObject(outcome)) delete outcome.jsonLogicOutcome
return outcome
}
return decision.defaultOutcome
}
}
I don't love this. But, I like basically everything else about the module! So ... there you go. Perhaps a mention could be added to the docs to prevent people from going down Rabbit holes :)