Skip to content

Commit 87c37c4

Browse files
Add JsonWriterOptions to TemplateOptions and TemplateContext (#846)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: sebastienros <[email protected]> Co-authored-by: Sébastien Ros <[email protected]>
1 parent d8b6681 commit 87c37c4

File tree

5 files changed

+119
-4
lines changed

5 files changed

+119
-4
lines changed

Fluid.Tests/MiscFiltersTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Globalization;
66
using System.Linq;
77
using System.Text.Encodings.Web;
8+
using System.Text.Json;
89
using System.Threading.Tasks;
910
using TimeZoneConverter;
1011
using Xunit;
@@ -823,6 +824,60 @@ public async Task JsonShouldUseJsonSerializerOption()
823824
Assert.Equal(expected, result.ToStringValue());
824825
}
825826

827+
[Fact]
828+
public async Task JsonShouldUseJsonWriterOptionsFromTemplateOptions()
829+
{
830+
var options = new TemplateOptions
831+
{
832+
JsonWriterOptions = new JsonWriterOptions
833+
{
834+
Indented = true
835+
}
836+
};
837+
838+
var input = FluidValue.Create(new { name = "test", value = 123 }, options);
839+
options.MemberAccessStrategy.Register(input.ToObjectValue().GetType());
840+
var context = new TemplateContext(options);
841+
var result = await MiscFilters.Json(input, new FilterArguments(), context);
842+
843+
// Indented JSON should have newlines
844+
Assert.Contains("\n", result.ToStringValue());
845+
}
846+
847+
[Fact]
848+
public async Task JsonShouldUseJsonWriterOptionsFromTemplateContext()
849+
{
850+
var options = new TemplateOptions();
851+
var context = new TemplateContext(options)
852+
{
853+
JsonWriterOptions = new JsonWriterOptions
854+
{
855+
Indented = true
856+
}
857+
};
858+
859+
var input = FluidValue.Create(new { name = "test", value = 123 }, options);
860+
options.MemberAccessStrategy.Register(input.ToObjectValue().GetType());
861+
var result = await MiscFilters.Json(input, new FilterArguments(), context);
862+
863+
// Indented JSON should have newlines
864+
Assert.Contains("\n", result.ToStringValue());
865+
}
866+
867+
[Fact]
868+
public async Task JsonShouldSerializeEnumsAsNumbers()
869+
{
870+
var options = new TemplateOptions();
871+
options.MemberAccessStrategy.Register<TestEnum>();
872+
873+
var input = FluidValue.Create(TestEnum.Value2, options);
874+
var context = new TemplateContext(options);
875+
var result = await MiscFilters.Json(input, new FilterArguments(), context);
876+
877+
// Enum should be serialized as number (1 for Value2)
878+
Assert.Equal("1", result.ToStringValue());
879+
}
880+
826881
[Theory]
827882
[InlineData("", "", "", "0")]
828883
[InlineData(123456, "", "", "123456")]
@@ -1029,5 +1084,12 @@ public DictionaryWithoutIndexableTestObjects(object value) : base(value)
10291084

10301085
}
10311086
}
1087+
1088+
private enum TestEnum
1089+
{
1090+
Value1 = 0,
1091+
Value2 = 1,
1092+
Value3 = 2
1093+
}
10321094
}
10331095
}

Fluid/Filters/MiscFilters.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -792,10 +792,8 @@ private static async ValueTask WriteJson(Utf8JsonWriter writer, FluidValue input
792792
public static async ValueTask<FluidValue> Json(FluidValue input, FilterArguments arguments, TemplateContext context)
793793
{
794794
using var ms = new MemoryStream();
795-
await using (var writer = new Utf8JsonWriter(ms, new JsonWriterOptions
796-
{
797-
Indented = arguments.At(0).ToBooleanValue()
798-
}))
795+
796+
await using (var writer = new Utf8JsonWriter(ms, context.JsonWriterOptions))
799797
{
800798
await WriteJson(writer, input, context);
801799
}

Fluid/TemplateContext.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Fluid.Values;
22
using System.Globalization;
33
using System.Runtime.CompilerServices;
4+
using System.Text.Json;
45

56
namespace Fluid
67
{
@@ -59,6 +60,7 @@ public TemplateContext(TemplateOptions options, StringComparer modelNamesCompare
5960
Now = options.Now;
6061
MaxSteps = options.MaxSteps;
6162
ModelNamesComparer = modelNamesComparer;
63+
JsonWriterOptions = options.JsonWriterOptions;
6264
}
6365

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

120+
/// <summary>
121+
/// Gets or sets the <see cref="JsonWriterOptions"/> used by the <c>json</c> filter.
122+
/// </summary>
123+
public JsonWriterOptions JsonWriterOptions { get; set; } = TemplateOptions.Default.JsonWriterOptions;
124+
118125
/// <summary>
119126
/// Increments the number of statements the current template is processing.
120127
/// </summary>

Fluid/TemplateOptions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.Extensions.FileProviders;
44
using System.Globalization;
55
using System.Text.Encodings.Web;
6+
using System.Text.Json;
67

78
namespace Fluid
89
{
@@ -105,6 +106,11 @@ public class TemplateOptions
105106
/// </summary>
106107
public JavaScriptEncoder JavaScriptEncoder { get; set; } = DefaultJavaScriptEncoder;
107108

109+
/// <summary>
110+
/// Gets or sets the <see cref="JsonWriterOptions"/> used by the <c>json</c> filter.
111+
/// </summary>
112+
public JsonWriterOptions JsonWriterOptions { get; set; } = new JsonWriterOptions();
113+
108114
/// <summary>
109115
/// Gets or sets the default trimming rules.
110116
/// </summary>

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,48 @@ var context = new TemplateContext(options);
366366
"你好,这是一条短信"
367367
```
368368

369+
### Customizing JSON output
370+
371+
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`.
372+
373+
#### Example: Indented JSON output
374+
375+
```csharp
376+
var options = new TemplateOptions
377+
{
378+
JsonWriterOptions = new JsonWriterOptions
379+
{
380+
Indented = true
381+
}
382+
};
383+
384+
var context = new TemplateContext(options);
385+
context.SetValue("data", new { name = "John", age = 30 });
386+
```
387+
388+
```Liquid
389+
{{ data | json }}
390+
```
391+
392+
#### Result
393+
```json
394+
{
395+
"name": "John",
396+
"age": 30
397+
}
398+
```
399+
400+
You can also set `JsonWriterOptions` per context:
401+
402+
```csharp
403+
var context = new TemplateContext();
404+
context.JsonWriterOptions = new JsonWriterOptions
405+
{
406+
Indented = true,
407+
// Other options like MaxDepth, SkipValidation, etc.
408+
};
409+
```
410+
369411
<br>
370412

371413
## Localization

0 commit comments

Comments
 (0)