Handlebars.Net icon indicating copy to clipboard operation
Handlebars.Net copied to clipboard

[Question] How to use functions in loops (#each and so on) when values comes from JSON?

Open paciox opened this issue 1 year ago • 0 comments

I have this test json: { "partyEvent": { "partyName": "Sweet Treats Fiesta", "day": "Saturday", "hour": "5:00 PM", "date": "2024-09-28", "address": { "street": "123 Party Lane", "city": "Funville", "state": "CA", "zip": "90210" }, "theme": "Desserts Extravaganza", "host": { "name": "Jane Doe", "contact": "[email protected]" }, "participants": [ { "name": "Alice Smith", "role": "Guest", "RSVP": true }, { "name": "John Johnson", "role": "Guest", "RSVP": false }, { "name": "Emily Davis", "role": "Caterer", "RSVP": true } ], "menu": { "items": [ { "id": "0001", "type": "donut", "name": "Cake", "ppu": 0.55, "batters": { "batter": [ { "id": "1001", "type": "Regular" }, { "id": "1002", "type": "Chocolate" }, { "id": "1003", "type": "Blueberry" }, { "id": "1004", "type": "Devil's Food" } ] }, "topping": [ { "id": "5001", "type": "None" }, { "id": "5002", "type": "Glazed" }, { "id": "5005", "type": "Sugar" }, { "id": "5007", "type": "Powdered Sugar" }, { "id": "5006", "type": "Chocolate with Sprinkles" }, { "id": "5003", "type": "Chocolate" }, { "id": "5004", "type": "Maple" } ] }, { "id": "0002", "type": "donut", "name": "Raised", "ppu": 0.55, "batters": { "batter": [ { "id": "1001", "type": "Regular" } ] }, "topping": [ { "id": "5001", "type": "None" }, { "id": "5002", "type": "Glazed" }, { "id": "5005", "type": "Sugar" }, { "id": "5003", "type": "Chocolate" }, { "id": "5004", "type": "Maple" } ] }, { "id": "0003", "type": "donut", "name": "Old Fashioned", "ppu": 0.55, "batters": { "batter": [ { "id": "1001", "type": "Regular" }, { "id": "1002", "type": "Chocolate" } ] }, "topping": [ { "id": "5001", "type": "None" }, { "id": "5002", "type": "Glazed" }, { "id": "5003", "type": "Chocolate" }, { "id": "5004", "type": "Maple" } ] } ] } } }

And I want to convert it to this one:

{ "eventoFesta": { "nomeFesta": "Sweet Treats Fiesta", "giorno": "Saturday", "ora": "5:00 PM", "data": "2024-09-28", "indirizzo": { "via": "123 Party Lane", "città": "Funville", "stato": "CA", "cap": "90210" }, "tema": "Desserts Extravaganza", "ospite": { "nome": "Jane Doe", "contatto": "[email protected]" }, "personeInvitate": { "partecipanti": [ { "nome": "Alice Smith", "ruolo": "Guest", "RSVP": true }, { "nome": "John Johnson", "ruolo": "Guest", "RSVP": false }, { "nome": "Emily Davis", "ruolo": "Caterer", "RSVP": true } ] } } }

through this template that it will simply map the first one through the second one: var template = """ { "eventoFesta": { "nomeFesta": "{{jsonpath '$.partyEvent.partyName'}}", "giorno": "{{jsonpath '$.partyEvent.day'}}", "ora": "{{jsonpath '$.partyEvent.hour'}}", "data": "{{jsonpath '$.partyEvent.date'}}", "indirizzo": { "via": "{{jsonpath '$.partyEvent.address.street'}}", "città": "{{jsonpath '$.partyEvent.address.city'}}", "stato": "{{jsonpath '$.partyEvent.address.state'}}", "cap": "{{jsonpath '$.partyEvent.address.zip'}}" }, "tema": "{{jsonpath '$.partyEvent.theme'}}", "ospite": { "nome": "{{jsonpath '$.partyEvent.host.name'}}", "contatto": "{{jsonpath '$.partyEvent.host.contact'}}" }, "personeInvitate": { "partecipanti": [ {{#each (jsonpath '$.partyEvent.participants')}} { "nome": "{{jsonpath '$.name'}}", "ruolo": "{{jsonpath '$.role'}}", "RSVP": {{jsonpath '$.RSVP'}} }{{#unless @last}},{{/unless}} {{/each}} ] } } } """;

Since we are dealing with JSON, I wrote an helper, which is "jsonpath":

` using HandlebarsDotNet; using Json.Path; using System.Text.Json.Nodes;

namespace _01_BaseSKStuff.BaseUtils.JsonUtils { internal static class PathEvaluator { internal static HandlebarsHelper PathTrasversalHelper => (writer, context, parameters) => { if (parameters.Length == 0) { writer.Write(null); return; }

        var data = JsonNode.Parse(context.Value.ToString());
        var paramValue = parameters[0].ToString();

        if (data is null || paramValue is null)
        {
            writer.Write(null);
            return;
        }

        var value = EvaluatePath(data, paramValue);

        writer.Write(value);
    };

    public static string? EvaluatePath(JsonNode jsonNode, string rulePath)
    {
        var jsonPath = JsonPath.Parse(rulePath);
        var pathResult = jsonPath.Evaluate(jsonNode);

        if (pathResult is null || pathResult.Matches.Count != 1) return null;

        return pathResult.Matches.Select(x => x.Value?.ToString()).First();
    }
}

} `

This just takes the first element of a json path, traversing it with the syntax "$.thisproperty.thischildproperty"

It actually maps everything good except for the #each helper that does not seem to like what it returns, which is a JsonArray[].

How to make this work?

paciox avatar Sep 21 '24 18:09 paciox