Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Fluid.Tests/MiscFiltersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<TestEnum>();

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")]
Expand Down Expand Up @@ -1029,5 +1084,12 @@ public DictionaryWithoutIndexableTestObjects(object value) : base(value)

}
}

private enum TestEnum
{
Value1 = 0,
Value2 = 1,
Value3 = 2
}
}
}
6 changes: 2 additions & 4 deletions Fluid/Filters/MiscFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -792,10 +792,8 @@ private static async ValueTask WriteJson(Utf8JsonWriter writer, FluidValue input
public static async ValueTask<FluidValue> 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);
}
Expand Down
7 changes: 7 additions & 0 deletions Fluid/TemplateContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Fluid.Values;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text.Json;

namespace Fluid
{
Expand Down Expand Up @@ -59,6 +60,7 @@ public TemplateContext(TemplateOptions options, StringComparer modelNamesCompare
Now = options.Now;
MaxSteps = options.MaxSteps;
ModelNamesComparer = modelNamesComparer;
JsonWriterOptions = options.JsonWriterOptions;
}

/// <summary>
Expand Down Expand Up @@ -115,6 +117,11 @@ public TemplateContext(object model, bool allowModelMembers = true, StringCompar
/// </summary>
public TimeZoneInfo TimeZone { get; set; } = TemplateOptions.Default.TimeZone;

/// <summary>
/// Gets or sets the <see cref="JsonWriterOptions"/> used by the <c>json</c> filter.
/// </summary>
public JsonWriterOptions JsonWriterOptions { get; set; } = new JsonWriterOptions();
Copy link
Owner

@sebastienros sebastienros Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot The initialization is unnecessary because the value is set in the constructor from the TemplateOptions


/// <summary>
/// Increments the number of statements the current template is processing.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions Fluid/TemplateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.Extensions.FileProviders;
using System.Globalization;
using System.Text.Encodings.Web;
using System.Text.Json;

namespace Fluid
{
Expand Down Expand Up @@ -105,6 +106,11 @@ public class TemplateOptions
/// </summary>
public JavaScriptEncoder JavaScriptEncoder { get; set; } = DefaultJavaScriptEncoder;

/// <summary>
/// Gets or sets the <see cref="JsonWriterOptions"/> used by the <c>json</c> filter.
/// </summary>
public JsonWriterOptions JsonWriterOptions { get; set; } = new JsonWriterOptions();

/// <summary>
/// Gets or sets the default trimming rules.
/// </summary>
Expand Down