json-logic-js icon indicating copy to clipboard operation
json-logic-js copied to clipboard

Render an object

Open CedrikWinter opened this issue 8 years ago • 5 comments

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" })...

CedrikWinter avatar Sep 07 '17 08:09 CedrikWinter

I found out : jsonLogic.add_operation("JSON", JSON) and then :

{
  "if" : [
    true,
    { "JSON.parse" : "{ 'myProp' : 'OK'}" }
  ]
}

CedrikWinter avatar Sep 07 '17 08:09 CedrikWinter

I found out : jsonLogic.add_operation("JSON", JSON) and then :

{
  "if" : [
    true,
    { "JSON.parse" : "{ 'myProp' : 'OK'}" }
  ]
}

CedrikWinter avatar Sep 08 '17 12:09 CedrikWinter

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 :-) !

CedrikWinter avatar Sep 08 '17 13:09 CedrikWinter

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)'

BenjaminConant avatar Aug 09 '18 02:08 BenjaminConant

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 :)

BenjaminConant avatar Aug 09 '18 03:08 BenjaminConant