Guia — IExpressionEngine

Uma linguagem de expressão nova (ou funções adicionais na JSONata existente), ativada por engine: (default jsonata).

Extensão leve: uma função JSONata nova

O caso mais comum — não é preciso trocar o motor inteiro, só adicionar $minhaFuncao() ao vocabulário:

using Pipevine.Expressions.Jsonata;

public sealed class AcmeJsonataFunctionProvider : IJsonataFunctionProvider
{
    public IEnumerable<JsonataFunctionBinding> GetFunctions()
    {
        yield return new JsonataFunctionBinding(
            Name: "acmeSlug",
            Implementation: (Func<string, string>)(text => text.ToLowerInvariant().Replace(' ', '-')),
            Signature: "<s:s>"); // 1 argumento string, retorna string
    }
}
services.AddSingleton<IJsonataFunctionProvider, AcmeJsonataFunctionProvider>();

Sem mais nenhuma configuração: toda expressão JSONata compilada pela aplicação passa a reconhecer $acmeSlug(...).

Extensão completa: um motor de expressão novo

using System.Text.Json.Nodes;
using Pipevine.Abstractions.Expressions;

public sealed class AcmeExpressionEngine : IExpressionEngine
{
    // Roda uma vez por expressão, no build. Lançar aqui (sintaxe inválida) derruba o startup —
    // nunca a primeira avaliação.
    public ICompiledExpression Compile(string expression, ExpressionOptions options) =>
        new AcmeCompiledExpression(/* parse de "expression" */);
}

// Imutável e thread-safe: avaliado concorrentemente por muitas requisições.
internal sealed class AcmeCompiledExpression : ICompiledExpression
{
    public JsonNode? Evaluate(JsonNode state)
    {
        // Lance ExpressionTimeoutException se estourar options.Timeout, ou
        // ExpressionEvaluationException para qualquer outra falha em runtime.
    }
}

Registre como a implementação de IExpressionEngine da aplicação (substitui a JSONata):

services.AddSingleton<IExpressionEngine, AcmeExpressionEngine>();

Por que essa abstração existe

JSONata é o motor padrão desde o primeiro momento do framework, mas fica atrás de uma abstração por dois motivos: permite trocar a implementação de JSONata em si sem afetar o resto do framework, e abre espaço para expressões literais ou outras linguagens como extensão — sem que nenhum código que consome ICompiledExpression precise saber qual motor está por trás.

Para onde ir daqui