Skip to content

Commit 4062c8c

Browse files
authored
Use a JsonConverter with ElementReference (#12244)
* Use a JsonConverter with ElementReference
2 parents d208b24 + 919bd7d commit 4062c8c

File tree

4 files changed

+135
-12
lines changed

4 files changed

+135
-12
lines changed

src/Components/Blazor/Build/test/RuntimeDependenciesResolverTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
1313
{
1414
public class RuntimeDependenciesResolverTest
1515
{
16-
[ConditionalFact]
16+
[ConditionalFact(Skip = " https://github.com/aspnet/AspNetCore/issues/12059")]
1717
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/10426")]
1818
public void FindsReferenceAssemblyGraph_ForStandaloneApp()
1919
{

src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,6 @@ public readonly partial struct ElementRef
154154
public readonly partial struct ElementReference
155155
{
156156
private readonly object _dummy;
157-
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
158-
public string __internalId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
159157
}
160158
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
161159
public readonly partial struct EventCallback

src/Components/Components/src/ElementReference.cs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,33 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.ComponentModel;
5+
using System.Globalization;
6+
using System.Text.Json;
7+
using System.Text.Json.Serialization;
68
using System.Threading;
79

810
namespace Microsoft.AspNetCore.Components
911
{
1012
/// <summary>
1113
/// Represents a reference to a rendered element.
1214
/// </summary>
15+
[JsonConverter(typeof(ElementReferenceConverter))]
1316
public readonly struct ElementReference
1417
{
1518
private static long _nextIdForWebAssemblyOnly = 1;
1619

1720
/// <summary>
18-
/// Gets a unique identifier for <see cref="ElementRef" />.
21+
/// Gets a unique identifier for <see cref="ElementReference" />.
1922
/// </summary>
2023
/// <remarks>
2124
/// The Id is unique at least within the scope of a given user/circuit.
2225
/// This property is public to support Json serialization and should not be used by user code.
2326
/// </remarks>
24-
[EditorBrowsable(EditorBrowsableState.Never)]
25-
public string __internalId { get; }
26-
27-
internal string Id => __internalId;
27+
internal string Id { get; }
2828

2929
private ElementReference(string id)
3030
{
31-
__internalId = id;
31+
Id = id;
3232
}
3333

3434
internal static ElementReference CreateWithUniqueId()
@@ -45,13 +45,56 @@ private static string CreateUniqueId()
4545
// fields for ElementRefCaptureId, of which only one would be in use depending
4646
// on the platform.
4747
var id = Interlocked.Increment(ref _nextIdForWebAssemblyOnly);
48-
return id.ToString();
48+
return id.ToString(CultureInfo.InvariantCulture);
4949
}
5050
else
5151
{
5252
// For remote rendering, it's important not to disclose any cross-user state,
5353
// such as the number of IDs that have been assigned.
54-
return Guid.NewGuid().ToString("D");
54+
return Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture);
55+
}
56+
}
57+
58+
private sealed class ElementReferenceConverter : JsonConverter<ElementReference>
59+
{
60+
private static readonly JsonEncodedText IdProperty = JsonEncodedText.Encode("__internalId");
61+
62+
public override ElementReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
63+
{
64+
string id = null;
65+
while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
66+
{
67+
if (reader.TokenType == JsonTokenType.PropertyName)
68+
{
69+
if (reader.ValueTextEquals(IdProperty.EncodedUtf8Bytes))
70+
{
71+
reader.Read();
72+
id = reader.GetString();
73+
}
74+
else
75+
{
76+
throw new JsonException($"Unexpected JSON property '{reader.GetString()}'.");
77+
}
78+
}
79+
else
80+
{
81+
throw new JsonException($"Unexcepted JSON Token {reader.TokenType}.");
82+
}
83+
}
84+
85+
if (id is null)
86+
{
87+
throw new JsonException("__internalId is required.");
88+
}
89+
90+
return new ElementReference(id);
91+
}
92+
93+
public override void Write(Utf8JsonWriter writer, ElementReference value, JsonSerializerOptions options)
94+
{
95+
writer.WriteStartObject();
96+
writer.WriteString(IdProperty, value.Id);
97+
writer.WriteEndObject();
5598
}
5699
}
57100
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Text.Json;
5+
using Xunit;
6+
7+
namespace Microsoft.AspNetCore.Components
8+
{
9+
public class ElementReferenceTest
10+
{
11+
[Fact]
12+
public void Serializing_Works()
13+
{
14+
// Arrange
15+
var elementReference = ElementReference.CreateWithUniqueId();
16+
var expected = $"{{\"__internalId\":\"{elementReference.Id}\"}}";
17+
18+
// Act
19+
var json = JsonSerializer.Serialize(elementReference, JsonSerializerOptionsProvider.Options);
20+
21+
// Assert
22+
Assert.Equal(expected, json);
23+
}
24+
25+
[Fact]
26+
public void Deserializing_Works()
27+
{
28+
// Arrange
29+
var id = ElementReference.CreateWithUniqueId().Id;
30+
var json = $"{{\"__internalId\":\"{id}\"}}";
31+
32+
// Act
33+
var elementReference = JsonSerializer.Deserialize<ElementReference>(json, JsonSerializerOptionsProvider.Options);
34+
35+
// Assert
36+
Assert.Equal(id, elementReference.Id);
37+
}
38+
39+
[Fact]
40+
public void Deserializing_WithFormatting_Works()
41+
{
42+
// Arrange
43+
var id = ElementReference.CreateWithUniqueId().Id;
44+
var json =
45+
@$"{{
46+
""__internalId"": ""{id}""
47+
}}";
48+
49+
// Act
50+
var elementReference = JsonSerializer.Deserialize<ElementReference>(json, JsonSerializerOptionsProvider.Options);
51+
52+
// Assert
53+
Assert.Equal(id, elementReference.Id);
54+
}
55+
56+
[Fact]
57+
public void Deserializing_Throws_IfUnknownPropertyAppears()
58+
{
59+
// Arrange
60+
var json = "{\"id\":\"some-value\"}";
61+
62+
// Act
63+
var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ElementReference>(json, JsonSerializerOptionsProvider.Options));
64+
65+
// Assert
66+
Assert.Equal("Unexpected JSON property 'id'.", ex.Message);
67+
}
68+
69+
[Fact]
70+
public void Deserializing_Throws_IfIdIsNotSpecified()
71+
{
72+
// Arrange
73+
var json = "{}";
74+
75+
// Act
76+
var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ElementReference>(json, JsonSerializerOptionsProvider.Options));
77+
78+
// Assert
79+
Assert.Equal("__internalId is required.", ex.Message);
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)