diff --git a/Fluid.Tests/MiscFiltersTests.cs b/Fluid.Tests/MiscFiltersTests.cs index 9077821a..ab29d0c0 100644 --- a/Fluid.Tests/MiscFiltersTests.cs +++ b/Fluid.Tests/MiscFiltersTests.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Text.Encodings.Web; +using System.Text.Json; using System.Threading.Tasks; using TimeZoneConverter; using Xunit; @@ -823,6 +824,60 @@ public async Task JsonShouldUseJsonSerializerOption() Assert.Equal(expected, result.ToStringValue()); } + [Fact] + public async Task JsonShouldUseJsonWriterOptionsFromTemplateOptions() + { + var options = new TemplateOptions + { + JsonWriterOptions = new JsonWriterOptions + { + Indented = true + } + }; + + var input = FluidValue.Create(new { name = "test", value = 123 }, options); + options.MemberAccessStrategy.Register(input.ToObjectValue().GetType()); + var context = new TemplateContext(options); + var result = await MiscFilters.Json(input, new FilterArguments(), context); + + // Indented JSON should have newlines + Assert.Contains("\n", result.ToStringValue()); + } + + [Fact] + public async Task JsonShouldUseJsonWriterOptionsFromTemplateContext() + { + var options = new TemplateOptions(); + var context = new TemplateContext(options) + { + JsonWriterOptions = new JsonWriterOptions + { + Indented = true + } + }; + + var input = FluidValue.Create(new { name = "test", value = 123 }, options); + options.MemberAccessStrategy.Register(input.ToObjectValue().GetType()); + var result = await MiscFilters.Json(input, new FilterArguments(), context); + + // Indented JSON should have newlines + Assert.Contains("\n", result.ToStringValue()); + } + + [Fact] + public async Task JsonShouldSerializeEnumsAsNumbers() + { + var options = new TemplateOptions(); + options.MemberAccessStrategy.Register(); + + var input = FluidValue.Create(TestEnum.Value2, options); + var context = new TemplateContext(options); + var result = await MiscFilters.Json(input, new FilterArguments(), context); + + // Enum should be serialized as number (1 for Value2) + Assert.Equal("1", result.ToStringValue()); + } + [Theory] [InlineData("", "", "", "0")] [InlineData(123456, "", "", "123456")] @@ -1029,5 +1084,12 @@ public DictionaryWithoutIndexableTestObjects(object value) : base(value) } } + + private enum TestEnum + { + Value1 = 0, + Value2 = 1, + Value3 = 2 + } } } diff --git a/Fluid/Filters/MiscFilters.cs b/Fluid/Filters/MiscFilters.cs index 200bb0f6..c4d3afb3 100644 --- a/Fluid/Filters/MiscFilters.cs +++ b/Fluid/Filters/MiscFilters.cs @@ -792,10 +792,8 @@ private static async ValueTask WriteJson(Utf8JsonWriter writer, FluidValue input public static async ValueTask Json(FluidValue input, FilterArguments arguments, TemplateContext context) { using var ms = new MemoryStream(); - await using (var writer = new Utf8JsonWriter(ms, new JsonWriterOptions - { - Indented = arguments.At(0).ToBooleanValue() - })) + + await using (var writer = new Utf8JsonWriter(ms, context.JsonWriterOptions)) { await WriteJson(writer, input, context); } diff --git a/Fluid/TemplateContext.cs b/Fluid/TemplateContext.cs index dd7d0e32..44084fa0 100644 --- a/Fluid/TemplateContext.cs +++ b/Fluid/TemplateContext.cs @@ -1,6 +1,7 @@ using Fluid.Values; using System.Globalization; using System.Runtime.CompilerServices; +using System.Text.Json; namespace Fluid { @@ -59,6 +60,7 @@ public TemplateContext(TemplateOptions options, StringComparer modelNamesCompare Now = options.Now; MaxSteps = options.MaxSteps; ModelNamesComparer = modelNamesComparer; + JsonWriterOptions = options.JsonWriterOptions; } /// @@ -115,6 +117,11 @@ public TemplateContext(object model, bool allowModelMembers = true, StringCompar /// public TimeZoneInfo TimeZone { get; set; } = TemplateOptions.Default.TimeZone; + /// + /// Gets or sets the used by the json filter. + /// + public JsonWriterOptions JsonWriterOptions { get; set; } = TemplateOptions.Default.JsonWriterOptions; + /// /// Increments the number of statements the current template is processing. /// diff --git a/Fluid/TemplateOptions.cs b/Fluid/TemplateOptions.cs index 74d2dfa0..b5a87be8 100644 --- a/Fluid/TemplateOptions.cs +++ b/Fluid/TemplateOptions.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.FileProviders; using System.Globalization; using System.Text.Encodings.Web; +using System.Text.Json; namespace Fluid { @@ -105,6 +106,11 @@ public class TemplateOptions /// public JavaScriptEncoder JavaScriptEncoder { get; set; } = DefaultJavaScriptEncoder; + /// + /// Gets or sets the used by the json filter. + /// + public JsonWriterOptions JsonWriterOptions { get; set; } = new JsonWriterOptions(); + /// /// Gets or sets the default trimming rules. /// diff --git a/README.md b/README.md index 379d2e17..089419df 100644 --- a/README.md +++ b/README.md @@ -366,6 +366,48 @@ var context = new TemplateContext(options); "你好,这是一条短信" ``` +### Customizing JSON output + +The `json` filter uses `System.Text.Json.JsonWriterOptions` to control the JSON output format. You can customize these options through `TemplateOptions.JsonWriterOptions` or `TemplateContext.JsonWriterOptions`. + +#### Example: Indented JSON output + +```csharp +var options = new TemplateOptions +{ + JsonWriterOptions = new JsonWriterOptions + { + Indented = true + } +}; + +var context = new TemplateContext(options); +context.SetValue("data", new { name = "John", age = 30 }); +``` + +```Liquid +{{ data | json }} +``` + +#### Result +```json +{ + "name": "John", + "age": 30 +} +``` + +You can also set `JsonWriterOptions` per context: + +```csharp +var context = new TemplateContext(); +context.JsonWriterOptions = new JsonWriterOptions +{ + Indented = true, + // Other options like MaxDepth, SkipValidation, etc. +}; +``` +
## Localization