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
\ 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" };