diff --git a/source/Handlebars.Extension.Test/Handlebars.Extension.Test.csproj b/source/Handlebars.Extension.Test/Handlebars.Extension.Test.csproj index d885830..11d6390 100644 --- a/source/Handlebars.Extension.Test/Handlebars.Extension.Test.csproj +++ b/source/Handlebars.Extension.Test/Handlebars.Extension.Test.csproj @@ -1,7 +1,7 @@ - netcoreapp2.1;netcoreapp3.1 + netcoreapp3.1;net6.0 $(TargetFrameworks);net472 1956A22F-7B26-4747-8125-7EAC0B94665D HandlebarsDotNet.Extension.Test @@ -10,7 +10,7 @@ false true - + 0618;1701 @@ -27,15 +27,15 @@ $(DefineConstants);netFramework - + $(DefineConstants);netcoreapp;netstandard - + $(DefineConstants);netcoreapp;netstandard - + all @@ -45,23 +45,22 @@ - + - - + - + - + diff --git a/source/Handlebars.Extension.Test/JsonTests.cs b/source/Handlebars.Extension.Test/JsonTests.cs index 77408c0..e7b5a4f 100644 --- a/source/Handlebars.Extension.Test/JsonTests.cs +++ b/source/Handlebars.Extension.Test/JsonTests.cs @@ -13,14 +13,17 @@ namespace HandlebarsDotNet.Extension.Test { public class JsonTests { + public delegate object JsonModelFactory(string json); + public class EnvGenerator : IEnumerable { - private readonly List _data = new List + private readonly List<(IHandlebars, JsonModelFactory)> _data = new List<(IHandlebars, JsonModelFactory)> { - Handlebars.Create(new HandlebarsConfiguration().UseJson()) + (Handlebars.Create(new HandlebarsConfiguration().UseJson()), json => JsonDocument.Parse(json)), + (Handlebars.Create(new HandlebarsConfiguration().UseJson()), json => System.Text.Json.Nodes.JsonNode.Parse(json)), }; - public IEnumerator GetEnumerator() => _data.Select(o => new object[] { o }).GetEnumerator(); + public IEnumerator GetEnumerator() => _data.Select(item => new object[] { item.Item1, item.Item2 }).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } @@ -51,9 +54,9 @@ public void ValueTypes(string value) [Theory] [ClassData(typeof(EnvGenerator))] - public void JsonTestIfTruthy(IHandlebars handlebars) + public void JsonTestIfTruthy(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("{\"truthy\":true}"); + var model = jsonModelFactory("{\"truthy\":true}"); var source = "{{#if truthy}}{{truthy}}{{/if}}"; @@ -66,9 +69,9 @@ public void JsonTestIfTruthy(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void JsonTestIfFalsy(IHandlebars handlebars) + public void JsonTestIfFalsy(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("{\"falsy\":false}"); + var model = jsonModelFactory("{\"falsy\":false}"); var source = "{{#if (not falsy)}}{{falsy}}{{/if}}"; @@ -82,9 +85,9 @@ public void JsonTestIfFalsy(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void JsonTestIfFalsyMissingField(IHandlebars handlebars) + public void JsonTestIfFalsyMissingField(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("{\"myfield\":\"test1\"}"); + var model = jsonModelFactory("{\"myfield\":\"test1\"}"); var source = "{{myfield}}{{#if mymissingfield}}{{mymissingfield}}{{/if}}"; @@ -97,9 +100,9 @@ public void JsonTestIfFalsyMissingField(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void JsonTestIfFalsyValue(IHandlebars handlebars) + public void JsonTestIfFalsyValue(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("{\"myfield\":\"test1\",\"falsy\":null}"); + var model = jsonModelFactory("{\"myfield\":\"test1\",\"falsy\":null}"); var source = "{{myfield}}{{#if falsy}}{{falsy}}{{/if}}"; @@ -112,9 +115,9 @@ public void JsonTestIfFalsyValue(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void ArrayIterator(IHandlebars handlebars) + public void ArrayIterator(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); + var model = jsonModelFactory("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); var source = "{{#each this}}{{Key}}{{Value}}{{/each}}"; @@ -127,9 +130,9 @@ public void ArrayIterator(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void ArrayIteratorProperties(IHandlebars handlebars) + public void ArrayIteratorProperties(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); + var model = jsonModelFactory("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); var source = "{{#each this}}{{@index}}-{{@first}}-{{@last}}-{{@value.Key}}-{{@value.Value}};{{/each}}"; @@ -142,9 +145,9 @@ public void ArrayIteratorProperties(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void ArrayIndexProperties(IHandlebars handlebars) + public void ArrayIndexProperties(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("[\"Index0\", \"Index1\"]"); + var model = jsonModelFactory("[\"Index0\", \"Index1\"]"); var source = "{{@root.1}}"; @@ -157,9 +160,9 @@ public void ArrayIndexProperties(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void ArrayIndexPropertiesNested(IHandlebars handlebars) + public void ArrayIndexPropertiesNested(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("[{}, {\"Items\": [\"Index0\", \"Index1\"]}]"); + var model = jsonModelFactory("[{}, {\"Items\": [\"Index0\", \"Index1\"]}]"); var source = "{{@root.1.Items.1}}"; @@ -172,9 +175,9 @@ public void ArrayIndexPropertiesNested(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void ArrayCount(IHandlebars handlebars) + public void ArrayCount(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); + var model = jsonModelFactory("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); var source = "{{this.Count}} = {{this.Length}}"; @@ -187,9 +190,9 @@ public void ArrayCount(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void ArrayListProperties(IHandlebars handlebars) + public void ArrayListProperties(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { - var model = JsonDocument.Parse("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); + var model = jsonModelFactory("[{\"Key\": \"Key1\", \"Value\": \"Val1\"},{\"Key\": \"Key2\", \"Value\": \"Val2\"}]"); var source = "{{listProperties this}}"; @@ -203,8 +206,9 @@ public void ArrayListProperties(IHandlebars handlebars) [Theory] [ClassData(typeof(EnvGenerator))] - public void ObjectIterator(IHandlebars handlebars){ - var model = JsonDocument.Parse("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); + public void ObjectIterator(IHandlebars handlebars, JsonModelFactory jsonModelFactory) + { + var model = jsonModelFactory("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); var source = "{{#each this}}{{@key}}{{@value}}{{/each}}"; @@ -217,8 +221,9 @@ public void ObjectIterator(IHandlebars handlebars){ [Theory] [ClassData(typeof(EnvGenerator))] - public void ObjectIteratorProperties(IHandlebars handlebars){ - var model = JsonDocument.Parse("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); + public void ObjectIteratorProperties(IHandlebars handlebars, JsonModelFactory jsonModelFactory) + { + var model = jsonModelFactory("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); var source = "{{#each this}}{{@index}}-{{@first}}-{{@last}}-{{@key}}-{{@value}};{{/each}}"; @@ -231,8 +236,9 @@ public void ObjectIteratorProperties(IHandlebars handlebars){ [Theory] [ClassData(typeof(EnvGenerator))] - public void ObjectListProperties(IHandlebars handlebars){ - var model = JsonDocument.Parse("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); + public void ObjectListProperties(IHandlebars handlebars, JsonModelFactory jsonModelFactory) + { + var model = jsonModelFactory("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); var source = "{{ListProperties this}}"; handlebars.RegisterHelper(new ListPropertiesHelper()); @@ -246,8 +252,9 @@ public void ObjectListProperties(IHandlebars handlebars){ [Theory] [ClassData(typeof(EnvGenerator))] - public void ObjectIteratorPropertiesWithLast(IHandlebars handlebars){ - var model = JsonDocument.Parse("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); + public void ObjectIteratorPropertiesWithLast(IHandlebars handlebars, JsonModelFactory jsonModelFactory) + { + var model = jsonModelFactory("{\"Key1\": \"Val1\", \"Key2\": \"Val2\"}"); var source = "{{#each this}}{{@index}}-{{@first}}-{{@last}}-{{@key}}-{{@value}};{{/each}}"; @@ -261,7 +268,7 @@ public void ObjectIteratorPropertiesWithLast(IHandlebars handlebars){ [Theory] [ClassData(typeof(EnvGenerator))] - public void WithParentIndexJsonNet(IHandlebars handlebars) + public void WithParentIndexJsonNet(IHandlebars handlebars, JsonModelFactory jsonModelFactory) { var source = @" {{#each level1}} @@ -328,9 +335,9 @@ public void WithParentIndexJsonNet(IHandlebars handlebars) } }; - var json = JsonDocument.Parse(JsonSerializer.Serialize(data)); + var model = jsonModelFactory(JsonSerializer.Serialize(data)); - var result = template(json); + var result = template(model); const string expected = @" id=0 diff --git a/source/Handlebars.Extension.sln b/source/Handlebars.Extension.sln index 48eaecf..8ac4c3b 100644 --- a/source/Handlebars.Extension.sln +++ b/source/Handlebars.Extension.sln @@ -1,19 +1,18 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26403.3 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34714.143 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E9AC0BCD-C060-4634-BBBB-636167C809B4}" ProjectSection(SolutionItems) = preProject - ..\README.md = ..\README.md Directory.Build.props = Directory.Build.props + ..\README.md = ..\README.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Handlebars.Extension", "Handlebars.Extension\Handlebars.Extension.csproj", "{9822C7B8-7E51-42BC-9A49-72A10491B202}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Handlebars.Extension", "Handlebars.Extension\Handlebars.Extension.csproj", "{25080858-B620-4985-8AEF-E135324081B3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Handlebars.Extension.Test", "Handlebars.Extension.Test\Handlebars.Extension.Test.csproj", "{6BA232A6-8C4D-4C7D-BD75-1844FE9774AF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Handlebars.Extension.Test", "Handlebars.Extension.Test\Handlebars.Extension.Test.csproj", "{1956A22F-7B26-4747-8125-7EAC0B94665D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Handlebars.Extension.Benchmark", "Handlebars.Extension.Benchmark\Handlebars.Extension.Benchmark.csproj", "{B335E9C5-7DD3-416D-89CC-8D48D89DB628}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Handlebars.Extension.Benchmark", "Handlebars.Extension.Benchmark\Handlebars.Extension.Benchmark.csproj", "{95ECC7A5-0A42-4DAF-A546-20522A3F3CF5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,22 +20,25 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9822C7B8-7E51-42BC-9A49-72A10491B202}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9822C7B8-7E51-42BC-9A49-72A10491B202}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9822C7B8-7E51-42BC-9A49-72A10491B202}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9822C7B8-7E51-42BC-9A49-72A10491B202}.Release|Any CPU.Build.0 = Release|Any CPU - {6BA232A6-8C4D-4C7D-BD75-1844FE9774AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BA232A6-8C4D-4C7D-BD75-1844FE9774AF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BA232A6-8C4D-4C7D-BD75-1844FE9774AF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BA232A6-8C4D-4C7D-BD75-1844FE9774AF}.Release|Any CPU.Build.0 = Release|Any CPU - {B335E9C5-7DD3-416D-89CC-8D48D89DB628}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B335E9C5-7DD3-416D-89CC-8D48D89DB628}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B335E9C5-7DD3-416D-89CC-8D48D89DB628}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B335E9C5-7DD3-416D-89CC-8D48D89DB628}.Release|Any CPU.Build.0 = Release|Any CPU + {25080858-B620-4985-8AEF-E135324081B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25080858-B620-4985-8AEF-E135324081B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25080858-B620-4985-8AEF-E135324081B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25080858-B620-4985-8AEF-E135324081B3}.Release|Any CPU.Build.0 = Release|Any CPU + {1956A22F-7B26-4747-8125-7EAC0B94665D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1956A22F-7B26-4747-8125-7EAC0B94665D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1956A22F-7B26-4747-8125-7EAC0B94665D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1956A22F-7B26-4747-8125-7EAC0B94665D}.Release|Any CPU.Build.0 = Release|Any CPU + {95ECC7A5-0A42-4DAF-A546-20522A3F3CF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95ECC7A5-0A42-4DAF-A546-20522A3F3CF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95ECC7A5-0A42-4DAF-A546-20522A3F3CF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95ECC7A5-0A42-4DAF-A546-20522A3F3CF5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A684E42D-246D-4CF7-B79C-34C49A10B35F} + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.TextStylePolicy = $1 diff --git a/source/Handlebars.Extension/CountMemberAliasProvider.JsonNode.cs b/source/Handlebars.Extension/CountMemberAliasProvider.JsonNode.cs new file mode 100644 index 0000000..5101ed0 --- /dev/null +++ b/source/Handlebars.Extension/CountMemberAliasProvider.JsonNode.cs @@ -0,0 +1,26 @@ +using System; +using System.Text.Json.Nodes; +using HandlebarsDotNet.PathStructure; + +namespace HandlebarsDotNet.Extension.Json +{ + public partial class CountMemberAliasProvider : IMemberAliasProvider + { + public bool TryGetMemberByAlias(JsonNode instance, Type targetType, ChainSegment memberAlias, out object? value) + { + if (!EqualsIgnoreCase("count", memberAlias) && !EqualsIgnoreCase("length", memberAlias)) + { + value = null; + return false; + } + if (!(instance is JsonArray jsonArray)) + { + value = null; + return false; + } + + value = jsonArray.Count; + return true; + } + } +} diff --git a/source/Handlebars.Extension/CountMemberAliasProvider.cs b/source/Handlebars.Extension/CountMemberAliasProvider.cs index 66b44b7..c91f52a 100644 --- a/source/Handlebars.Extension/CountMemberAliasProvider.cs +++ b/source/Handlebars.Extension/CountMemberAliasProvider.cs @@ -4,7 +4,7 @@ namespace HandlebarsDotNet.Extension.Json { - public class CountMemberAliasProvider : IMemberAliasProvider + public partial class CountMemberAliasProvider : IMemberAliasProvider { public bool TryGetMemberByAlias(JsonElement instance, Type targetType, ChainSegment memberAlias, out object? value) { @@ -22,11 +22,11 @@ public bool TryGetMemberByAlias(JsonElement instance, Type targetType, ChainSegm value = instance.GetArrayLength(); return true; + } - static bool EqualsIgnoreCase(string a, ChainSegment b) - { - return string.Equals(a, b.TrimmedValue, StringComparison.OrdinalIgnoreCase); - } + private static bool EqualsIgnoreCase(string a, ChainSegment b) + { + return string.Equals(a, b.TrimmedValue, StringComparison.OrdinalIgnoreCase); } } } \ No newline at end of file diff --git a/source/Handlebars.Extension/Handlebars.Extension.csproj b/source/Handlebars.Extension/Handlebars.Extension.csproj index 2a86a4f..2be0336 100644 --- a/source/Handlebars.Extension/Handlebars.Extension.csproj +++ b/source/Handlebars.Extension/Handlebars.Extension.csproj @@ -3,14 +3,14 @@ HandlebarsDotNet.Extension.Json 25080858-B620-4985-8AEF-E135324081B3 - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net6.0 $(TargetFrameworks);net472 1.0.0 HandlebarsDotNet.Extension.Json enable true - + $(DefineConstants);netstandard @@ -28,17 +28,17 @@ https://github.com/Handlebars-Net/Handlebars.Net.Extension.Json/releases/tag/$(Version) true - + - + - + - + - + \ No newline at end of file diff --git a/source/Handlebars.Extension/JsonFeature.cs b/source/Handlebars.Extension/JsonFeature.cs index a862c6e..1fb195f 100644 --- a/source/Handlebars.Extension/JsonFeature.cs +++ b/source/Handlebars.Extension/JsonFeature.cs @@ -3,25 +3,29 @@ namespace HandlebarsDotNet.Extension.Json { /// - /// + /// /// public static class JsonFeatureExtensions { private static readonly JsonDocumentObjectDescriptor JsonDocumentObjectDescriptor = new JsonDocumentObjectDescriptor(); private static readonly JsonElementObjectDescriptor JsonElementObjectDescriptor = new JsonElementObjectDescriptor(); + private static readonly JsonNodeObjectDescriptor JsonNodeObjectDescriptor = new JsonNodeObjectDescriptor(); + /// - /// Adds s required to support System.Text.Json. + /// Adds s required to support System.Text.Json. /// /// /// public static HandlebarsConfiguration UseJson(this HandlebarsConfiguration configuration) { var providers = configuration.ObjectDescriptorProviders; - + providers.Add(JsonDocumentObjectDescriptor); providers.Add(JsonElementObjectDescriptor); - + + providers.Add(JsonNodeObjectDescriptor); + return configuration; } } diff --git a/source/Handlebars.Extension/JsonNodeIterator.cs b/source/Handlebars.Extension/JsonNodeIterator.cs new file mode 100644 index 0000000..6e45127 --- /dev/null +++ b/source/Handlebars.Extension/JsonNodeIterator.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text.Json.Nodes; +using HandlebarsDotNet.Collections; +using HandlebarsDotNet.Compiler; +using HandlebarsDotNet.Iterators; +using HandlebarsDotNet.PathStructure; +using HandlebarsDotNet.Runtime; +using HandlebarsDotNet.ValueProviders; + +namespace HandlebarsDotNet.Extension.Json +{ + public class JsonNodeIterator : IIterator + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Iterate( + in EncodedTextWriter writer, + BindingContext context, + ChainSegment[] blockParamsVariables, + object input, + TemplateDelegate template, + TemplateDelegate ifEmpty + ) + { + Iterate(writer, context, blockParamsVariables, (JsonNode)input, template, ifEmpty); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Iterate( + in EncodedTextWriter writer, + BindingContext context, + ChainSegment[] blockParamsVariables, + JsonNode input, + TemplateDelegate template, + TemplateDelegate ifEmpty + ) + { + switch (input) + { + case JsonObject jsonObject: + IterateObject(writer, context, blockParamsVariables, jsonObject, template, ifEmpty); + break; + case JsonArray jsonArray: + IterateArray(writer, context, blockParamsVariables, jsonArray, template, ifEmpty); + break; + + default: + Throw.ArgumentOutOfRangeException(); + break; + } + } + + private static void IterateObject( + in EncodedTextWriter writer, + BindingContext context, + ChainSegment[] blockParamsVariables, + JsonObject target, + TemplateDelegate template, + TemplateDelegate ifEmpty + ) + { + using var innerContext = context.CreateFrame(); + var iterator = new ObjectIteratorValues(innerContext); + var blockParamsValues = new BlockParamsValues(innerContext, blockParamsVariables); + + blockParamsValues.CreateProperty(0, out var _0); + blockParamsValues.CreateProperty(1, out var _1); + + var enumerator = ExtendedEnumerator>.Create(target.GetEnumerator()); + + iterator.First = BoxedValues.True; + iterator.Last = BoxedValues.False; + + int index = 0; + while (enumerator.MoveNext()) + { + var current = enumerator.Current; + + var currentValue = current.Value; + iterator.Key = currentValue.Key; + + if (index == 1) iterator.First = BoxedValues.False; + if (current.IsLast) iterator.Last = BoxedValues.True; + + iterator.Index = BoxedValues.Int(index); + + object? resolvedValue = currentValue.Value; + + blockParamsValues[_0] = resolvedValue; + blockParamsValues[_1] = currentValue.Key; + + iterator.Value = resolvedValue; + innerContext.Value = resolvedValue; + + template(writer, innerContext); + + ++index; + } + + if (index == 0) + { + innerContext.Value = context.Value; + ifEmpty(writer, innerContext); + } + } + + private static void IterateArray( + in EncodedTextWriter writer, + BindingContext context, + ChainSegment[] blockParamsVariables, + JsonArray target, + TemplateDelegate template, + TemplateDelegate ifEmpty + ) + { + using var innerContext = context.CreateFrame(); + var iterator = new IteratorValues(innerContext); + var blockParamsValues = new BlockParamsValues(innerContext, blockParamsVariables); + + blockParamsValues.CreateProperty(0, out var _0); + blockParamsValues.CreateProperty(1, out var _1); + + iterator.First = BoxedValues.True; + iterator.Last = BoxedValues.False; + + var count = target.Count; + var enumerator = target.GetEnumerator(); + + var index = 0; + var lastIndex = count - 1; + while (enumerator.MoveNext()) + { + var value = enumerator.Current; + var objectIndex = BoxedValues.Int(index); + + if (index == 1) iterator.First = BoxedValues.False; + if (index == lastIndex) iterator.Last = BoxedValues.True; + + iterator.Index = objectIndex; + + object? resolvedValue = value; + + blockParamsValues[_0] = resolvedValue; + blockParamsValues[_1] = objectIndex; + + iterator.Value = resolvedValue; + innerContext.Value = resolvedValue; + + template(writer, innerContext); + + ++index; + } + + if (index == 0) + { + innerContext.Value = context.Value; + ifEmpty(writer, innerContext); + } + } + + private static class Throw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(); + } + } +} diff --git a/source/Handlebars.Extension/JsonNodeMemberAccessor.cs b/source/Handlebars.Extension/JsonNodeMemberAccessor.cs new file mode 100644 index 0000000..53abe8b --- /dev/null +++ b/source/Handlebars.Extension/JsonNodeMemberAccessor.cs @@ -0,0 +1,43 @@ +using System.Text.Json.Nodes; +using HandlebarsDotNet.MemberAccessors; +using HandlebarsDotNet.PathStructure; + +namespace HandlebarsDotNet.Extension.Json +{ + internal class JsonNodeMemberAccessor : IMemberAccessor + { + private readonly CountMemberAliasProvider _aliasProvider = new CountMemberAliasProvider(); + + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) + { + var element = (JsonNode)instance; + + if (element is JsonObject jsonObject && jsonObject.TryGetPropertyValue(memberName.TrimmedValue, out var property)) + { + value = Utils.ExtractProperty(property); + return true; + } + + if (element is JsonArray jsonArray && int.TryParse(memberName, out var index)) + { + if (index >= jsonArray.Count) + { + value = null; + return false; + } + + var indexedElement = jsonArray[index]; + value = Utils.ExtractProperty(indexedElement); + return true; + } + + if (_aliasProvider.TryGetMemberByAlias(element, typeof(JsonNode), memberName, out value)) + { + return true; + } + + value = null; + return false; + } + } +} diff --git a/source/Handlebars.Extension/JsonNodeObjectDescriptor.cs b/source/Handlebars.Extension/JsonNodeObjectDescriptor.cs new file mode 100644 index 0000000..77cb6aa --- /dev/null +++ b/source/Handlebars.Extension/JsonNodeObjectDescriptor.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections; +using System.Text.Json.Nodes; +using HandlebarsDotNet.ObjectDescriptors; + +namespace HandlebarsDotNet.Extension.Json +{ + internal class JsonNodeObjectDescriptor : IObjectDescriptorProvider + { + private static readonly Type Type = typeof(JsonNode); + + private readonly ObjectDescriptor _descriptor = new ObjectDescriptor( + Type, + JsonDocumentMemberAccessor, + (descriptor, instance) => GetEnumerator(instance), + self => new JsonNodeIterator() + ); + + private static readonly JsonNodeMemberAccessor JsonDocumentMemberAccessor = new JsonNodeMemberAccessor(); + + public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + { + if (!Type.IsAssignableFrom(type)) + { + value = ObjectDescriptor.Empty; + return false; + } + + value = _descriptor; + return true; + } + + private static IEnumerable GetEnumerator(object instance) + { + var jsonNode = (JsonNode)instance; + + return Utils.GetEnumerator(jsonNode); + } + } +} diff --git a/source/Handlebars.Extension/Utils.JsonNode.cs b/source/Handlebars.Extension/Utils.JsonNode.cs new file mode 100644 index 0000000..246263e --- /dev/null +++ b/source/Handlebars.Extension/Utils.JsonNode.cs @@ -0,0 +1,44 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace HandlebarsDotNet.Extension.Json +{ + internal static partial class Utils + { + public static IEnumerable GetEnumerator(JsonNode jsonNode) + { + return jsonNode switch + { + JsonObject jsonObject => EnumerateObject(jsonObject), + JsonArray _ => ArrayProperties, + _ => Throw.ArgumentOutOfRangeException() + }; + + static IEnumerable EnumerateObject(JsonObject jsonObject) + { + foreach (var property in jsonObject) + { + yield return property.Key; + } + } + } + + public static object? ExtractProperty(JsonNode? property) + { + if (property == null) + { + return null; + } + + var type = property.GetType(); + if (type == typeof(JsonObject) || type == typeof(JsonArray)) + { + return property; + } + + return ExtractProperty(property.GetValue()); + } + } +} diff --git a/source/Handlebars.Extension/Utils.cs b/source/Handlebars.Extension/Utils.cs index 230fb09..e84a7e0 100644 --- a/source/Handlebars.Extension/Utils.cs +++ b/source/Handlebars.Extension/Utils.cs @@ -7,7 +7,7 @@ namespace HandlebarsDotNet.Extension.Json { - internal static class Utils + internal static partial class Utils { private static readonly string[] ArrayProperties = { "length" };