Home JsonLogic Basics (Legacy)
JsonLogic Basics (Legacy)
Cancel

JsonLogic Basics (Legacy)

As of v5.2.0, this approach is deemed legacy and will likely be removed from v6. Benchmarks show this approach to be twice as slow and consume twice the memory.

Using the object model

The library also defines an object model for rules, starting with the Rule base class. This type is fully serializeable, so if you have rules in a text format, just deserialize them to get a Rule instance.

1
var rule = JsonSerializer.Deserialize<Rule>("{\"<\" : [1, 2]}");

Once you have a rule instance, you can apply it using the .Apply() method, which takes a JsonNode? for the data. (JSON null and .Net null are unified for this model.) Sometimes, you may not have a data instance; rather you just want the rule to run. In these cases you can call .Apply() passing a null or by using the .Apply() extension method which takes no parameters.

1
2
var data = JsonNode.Parse("{\"foo\": \"bar\"}");
var result = rule.Apply(data);

In addition to reading and deserializing rules, you can define them inline using the JsonLogic static class. This class defines methods for all of the built-in rules.

Creating the “less than” rule with a variable lookup from above:

1
var rule = JsonLogic.LessThan(JsonLogic.Variable("foo.bar"), 2);

The 2 here is actually implicitly cast to a LiteralRule which is a stand-in for discrete JSON elements. It can hold any JSON value, and there are implicit casts for numeric, string*, and boolean types, as well as JsonElement. For arrays and objects, you can either build nodes inline

1
new JsonArray { 1, false, "string" };

or via JsonNode.Parse().

* JSON null literals need to either be cast to string, use JsonNull.Node from Json.More.Net, or use the provided LiteralRule.Null. All of these result in the same semantic value.

Creating new operators

JSON Logic also supports adding custom operations.

In C#, your operators will need to derive from the Rule abstract class. There is only a single method to implement, Apply(), and you’ll need to add an Operator attribute. The logic in the rule doesn’t need to be complex, but there are a couple things to be aware of:

  • The arguments for your rule must correspond to the parameters of the constructor.
  • You’re working with JsonNodes, so you’ll need to detect compatible value types. There are a few extension methods that you can use, like .Numberify(), that try to “fuzzy-cast” to an appropriate value.
  • If you encounter invalid input, throw a JsonLogicException with an appropriate message.

Apply() takes two parameters, both of which are data for variables to act upon.

  • data represents the external data.
  • contextData represents data that’s passed to it by other rules.

Several rules (all, none, and some) can pass data to their children. var will prioritize contextData when attempting to resolve the path. If contextData is (JSON) null or doesn’t have data at the indicated path, the path will be resolved against data.

It’s definitely recommended to go through the code for the built-in ruleset for examples.

Once your rule is defined, it needs to be registered using the RuleRegistry.Register<T>() method. This will allow the rule to be automatically deserialized.

Lastly, you’ll need to create a JSON converter for your rule. Due to the dynamic nature of how rules are serialized, your converter MUST implement IWeaklyTypedJsonConverter which is defined by Json.More.Net. The library also defines a WeaklyTypeJsonConverter<T> abstract class that you can use as a base.

Also see the AOT section below for AOT-compatibility requirements.

Overriding existing operators

While this library allows you to inherit from, and therefore override, the default behavior of a Rule, you need to be aware of the implications.

The rules in this library implement the Json Logic Specification. If you override this behavior, then you are no longer implementing that specification, and you lose interoperability with other implementations. If you want custom behavior and have this custom behavior common across implementations, you’ll need to also override the behavior in every implementation and application you use.

Ahead of Time (AOT) compatibility

JsonLogic v5 includes updates to support Native AOT applications. In order to take advantage of this, there are a few things you’ll need to do.

First, on your JsonSerializerContext, add the following attributes:

1
[JsonSerializable(typeof(Rule))]

It’s recommended that you create a single JsonSerializerOptions object (or a few if you need different configurations) and reuse it rather than creating them ad-hoc. When you create one, you’ll need to configure its TypeInfoResolverChain with your serializer context:

1
2
3
4
var serializerOptions = new()
{
    TypeInfoResolverChain = { MySerializerContext.Default }
};

If you don’t have any custom rules, you’re done. Congratulations.

If you do have custom rules, you’ll want to add [JsonSerializable] attributes for those as well.

For AOT-compatibility, you’ll need to register your rules using the RuleRegistry.AddRule<T>(JsonSerializerContext) method overload, passing in your serializer context, to provide the library access to the JsonTypeInfo for your rule type.

Contents