Skip to content

Commit 8f39208

Browse files
committed
Add a System.Text.Json based IJsonHelper
Fixes #9564
1 parent 955404c commit 8f39208

File tree

8 files changed

+170
-54
lines changed

8 files changed

+170
-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+
6+
using System.Text.Json.Serialization;
7+
using Microsoft.AspNetCore.Html;
8+
using Microsoft.Extensions.Options;
9+
10+
namespace Microsoft.AspNetCore.Mvc.Rendering
11+
{
12+
internal class SystemTextJsonHelper : IJsonHelper
13+
{
14+
private readonly MvcOptions _mvcOptions;
15+
16+
public SystemTextJsonHelper(IOptions<MvcOptions> mvcOptions)
17+
{
18+
_mvcOptions = mvcOptions.Value;
19+
}
20+
21+
/// <inheritdoc />
22+
public IHtmlContent Serialize(object value)
23+
{
24+
// JsonSerializer always encodes
25+
var json = JsonSerializer.ToString(value, value.GetType(), _mvcOptions.SerializerOptions);
26+
return new HtmlString(json);
27+
}
28+
}
29+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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_WithControlCharacters()
33+
{
34+
// Arrange
35+
var helper = GetJsonHelper();
36+
var obj = new
37+
{
38+
HTML = $"Hello \n \b \a world"
39+
};
40+
var expectedOutput = "{\"html\":\"Hello \\n \\b \\u0007 world\"}";
41+
42+
// Act
43+
var result = helper.Serialize(obj);
44+
45+
// Assert
46+
var htmlString = Assert.IsType<HtmlString>(result);
47+
Assert.Equal(expectedOutput, htmlString.ToString());
48+
}
49+
50+
[Fact]
51+
public virtual void Serialize_WithNonAsciiChars()
52+
{
53+
// Arrange
54+
var helper = GetJsonHelper();
55+
var obj = new
56+
{
57+
HTML = $"Hello pingüino"
58+
};
59+
var expectedOutput = "{\"html\":\"Hello ping\\u00fcino\"}";
60+
61+
// Act
62+
var result = helper.Serialize(obj);
63+
64+
// Assert
65+
var htmlString = Assert.IsType<HtmlString>(result);
66+
Assert.Equal(expectedOutput, htmlString.ToString());
67+
}
68+
69+
[Fact]
70+
public void Serialize_WithHTMLEntities()
71+
{
72+
// Arrange
73+
var helper = GetJsonHelper();
74+
var obj = new
75+
{
76+
HTML = $"Hello &nbsp; &lt;John&gt;"
77+
};
78+
var expectedOutput = "{\"html\":\"Hello \\u0026nbsp; \\u0026lt;John\\u0026gt;\"}";
79+
80+
// Act
81+
var result = helper.Serialize(obj);
82+
83+
// Assert
84+
var htmlString = Assert.IsType<HtmlString>(result);
85+
Assert.Equal(expectedOutput, htmlString.ToString());
86+
}
87+
}
88+
}
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)