Skip to content

Commit e42f979

Browse files
authored
Add a System.Text.Json based IJsonHelper (#9566)
* Add a System.Text.Json based IJsonHelper Fixes #9564
1 parent c300c8c commit e42f979

File tree

8 files changed

+185
-54
lines changed

8 files changed

+185
-54
lines changed

src/Mvc/Mvc.NewtonsoftJson/test/DependencyInjection/NewtonsoftJsonMvcCoreBuilderExtensionsTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public void AddServicesCore_ReplacesDefaultJsonHelper()
3636
{
3737
// Arrange
3838
var services = new ServiceCollection()
39-
.AddSingleton<IJsonHelper, DefaultJsonHelper>();
39+
.AddSingleton<IJsonHelper, SystemTextJsonHelper>();
4040

4141
// Act
4242
NewtonsoftJsonMvcCoreBuilderExtensions.AddServicesCore(services);

src/Mvc/Mvc.NewtonsoftJson/test/Microsoft.AspNetCore.Mvc.NewtonsoftJson.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<Compile Include="..\..\Mvc.Core\test\Formatters\JsonOutputFormatterTestBase.cs" />
1717
<Compile Include="..\..\Mvc.Core\test\Infrastructure\JsonResultExecutorTestBase.cs" />
1818
<Compile Include="..\..\Mvc.ViewFeatures\test\Infrastructure\TempDataSerializerTestBase.cs" />
19+
<Compile Include="..\..\Mvc.ViewFeatures\test\Rendering\JsonHelperTestBase.cs" />
1920
</ItemGroup>
2021

2122
</Project>

src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonHelperTest.cs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,16 @@
33

44
using System.Buffers;
55
using Microsoft.AspNetCore.Html;
6+
using Microsoft.AspNetCore.Mvc.Rendering;
67
using Microsoft.Extensions.Options;
78
using Newtonsoft.Json;
89
using Newtonsoft.Json.Serialization;
910
using Xunit;
1011

1112
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
1213
{
13-
public class NewtonsoftJsonHelperTest
14+
public class NewtonsoftJsonHelperTest : JsonHelperTestBase
1415
{
15-
[Fact]
16-
public void Serialize_EscapesHtmlByDefault()
17-
{
18-
// Arrange
19-
var options = new MvcNewtonsoftJsonOptions();
20-
options.SerializerSettings.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii;
21-
var helper = new NewtonsoftJsonHelper(
22-
Options.Create(options),
23-
ArrayPool<char>.Shared);
24-
var obj = new
25-
{
26-
HTML = "<b>John Doe</b>"
27-
};
28-
var expectedOutput = "{\"html\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
29-
30-
// Act
31-
var result = helper.Serialize(obj);
32-
33-
// Assert
34-
var htmlString = Assert.IsType<HtmlString>(result);
35-
Assert.Equal(expectedOutput, htmlString.ToString());
36-
}
37-
3816
[Fact]
3917
public void Serialize_MaintainsSettingsAndEscapesHtml()
4018
{
@@ -65,10 +43,7 @@ public void Serialize_MaintainsSettingsAndEscapesHtml()
6543
public void Serialize_UsesHtmlSafeVersionOfPassedInSettings()
6644
{
6745
// Arrange
68-
var options = new MvcNewtonsoftJsonOptions();
69-
var helper = new NewtonsoftJsonHelper(
70-
Options.Create(options),
71-
ArrayPool<char>.Shared);
46+
var helper = GetJsonHelper();
7247
var obj = new
7348
{
7449
FullHtml = "<b>John Doe</b>"
@@ -91,5 +66,33 @@ public void Serialize_UsesHtmlSafeVersionOfPassedInSettings()
9166
var htmlString = Assert.IsType<HtmlString>(result);
9267
Assert.Equal(expectedOutput, htmlString.ToString());
9368
}
69+
70+
[Fact]
71+
public override void Serialize_WithNonAsciiChars()
72+
{
73+
// Arrange
74+
var helper = GetJsonHelper();
75+
var obj = new
76+
{
77+
HTML = $"Hello pingüino"
78+
};
79+
var expectedOutput = $"{{\"html\":\"{obj.HTML}\"}}";
80+
81+
// Act
82+
var result = helper.Serialize(obj);
83+
84+
// Assert
85+
var htmlString = Assert.IsType<HtmlString>(result);
86+
Assert.Equal(expectedOutput, htmlString.ToString());
87+
}
88+
89+
protected override IJsonHelper GetJsonHelper()
90+
{
91+
var options = new MvcNewtonsoftJsonOptions();
92+
var helper = new NewtonsoftJsonHelper(
93+
Options.Create(options),
94+
ArrayPool<char>.Shared);
95+
return helper;
96+
}
9497
}
9598
}

src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ internal static void AddViewServices(IServiceCollection services)
172172
services.TryAddSingleton<IModelExpressionProvider>(s => s.GetRequiredService<ModelExpressionProvider>());
173173
services.TryAddSingleton<ValidationHtmlAttributeProvider, DefaultValidationHtmlAttributeProvider>();
174174

175-
services.TryAddSingleton<IJsonHelper, DefaultJsonHelper>();
175+
services.TryAddSingleton<IJsonHelper, SystemTextJsonHelper>();
176176

177177
//
178178
// View Components

src/Mvc/Mvc.ViewFeatures/src/Rendering/DefaultJsonHelper.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+

2+
// Copyright (c) .NET Foundation. All rights reserved.
3+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
4+
5+
using System.Text.Json.Serialization;
6+
using Microsoft.AspNetCore.Html;
7+
using Microsoft.Extensions.Options;
8+
9+
namespace Microsoft.AspNetCore.Mvc.Rendering
10+
{
11+
internal class SystemTextJsonHelper : IJsonHelper
12+
{
13+
private readonly MvcOptions _mvcOptions;
14+
15+
public SystemTextJsonHelper(IOptions<MvcOptions> mvcOptions)
16+
{
17+
_mvcOptions = mvcOptions.Value;
18+
}
19+
20+
/// <inheritdoc />
21+
public IHtmlContent Serialize(object value)
22+
{
23+
// JsonSerializer always encodes non-ASCII chars, so we do not need
24+
// to do anything special with the SerializerOptions
25+
var json = JsonSerializer.ToString(value, _mvcOptions.SerializerOptions);
26+
return new HtmlString(json);
27+
}
28+
}
29+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using Microsoft.AspNetCore.Html;
4+
using Xunit;
5+
6+
namespace Microsoft.AspNetCore.Mvc.Rendering
7+
{
8+
public abstract class JsonHelperTestBase
9+
{
10+
protected abstract IJsonHelper GetJsonHelper();
11+
12+
[Fact]
13+
public void Serialize_EscapesHtmlByDefault()
14+
{
15+
// Arrange
16+
var helper = GetJsonHelper();
17+
var obj = new
18+
{
19+
HTML = "<b>John Doe</b>"
20+
};
21+
var expectedOutput = "{\"html\":\"\\u003cb\\u003eJohn Doe\\u003c/b\\u003e\"}";
22+
23+
// Act
24+
var result = helper.Serialize(obj);
25+
26+
// Assert
27+
var htmlString = Assert.IsType<HtmlString>(result);
28+
Assert.Equal(expectedOutput, htmlString.ToString());
29+
}
30+
31+
[Fact]
32+
public void Serialize_WithNullValue()
33+
{
34+
// Arrange
35+
var helper = GetJsonHelper();
36+
var expectedOutput = "null";
37+
38+
// Act
39+
var result = helper.Serialize(value: null);
40+
41+
// Assert
42+
var htmlString = Assert.IsType<HtmlString>(result);
43+
Assert.Equal(expectedOutput, htmlString.ToString());
44+
}
45+
46+
[Fact]
47+
public void Serialize_WithControlCharacters()
48+
{
49+
// Arrange
50+
var helper = GetJsonHelper();
51+
var obj = new
52+
{
53+
HTML = $"Hello \n \b \a world"
54+
};
55+
var expectedOutput = "{\"html\":\"Hello \\n \\b \\u0007 world\"}";
56+
57+
// Act
58+
var result = helper.Serialize(obj);
59+
60+
// Assert
61+
var htmlString = Assert.IsType<HtmlString>(result);
62+
Assert.Equal(expectedOutput, htmlString.ToString());
63+
}
64+
65+
[Fact]
66+
public virtual void Serialize_WithNonAsciiChars()
67+
{
68+
// Arrange
69+
var helper = GetJsonHelper();
70+
var obj = new
71+
{
72+
HTML = $"Hello pingüino"
73+
};
74+
var expectedOutput = "{\"html\":\"Hello ping\\u00fcino\"}";
75+
76+
// Act
77+
var result = helper.Serialize(obj);
78+
79+
// Assert
80+
var htmlString = Assert.IsType<HtmlString>(result);
81+
Assert.Equal(expectedOutput, htmlString.ToString());
82+
}
83+
84+
[Fact]
85+
public void Serialize_WithHTMLEntities()
86+
{
87+
// Arrange
88+
var helper = GetJsonHelper();
89+
var obj = new
90+
{
91+
HTML = $"Hello &nbsp; &lt;John&gt;"
92+
};
93+
var expectedOutput = "{\"html\":\"Hello \\u0026nbsp; \\u0026lt;John\\u0026gt;\"}";
94+
95+
// Act
96+
var result = helper.Serialize(obj);
97+
98+
// Assert
99+
var htmlString = Assert.IsType<HtmlString>(result);
100+
Assert.Equal(expectedOutput, htmlString.ToString());
101+
}
102+
}
103+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+

2+
// Copyright (c) .NET Foundation. All rights reserved.
3+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
4+
5+
6+
using System.Text.Json.Serialization;
7+
using Microsoft.Extensions.Options;
8+
9+
namespace Microsoft.AspNetCore.Mvc.Rendering
10+
{
11+
public class SystemTextJsonHelperTest : JsonHelperTestBase
12+
{
13+
protected override IJsonHelper GetJsonHelper()
14+
{
15+
var mvcOptions = new MvcOptions { SerializerOptions = { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } };
16+
return new SystemTextJsonHelper(Options.Create(mvcOptions));
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)