Skip to content

Commit 7fbe600

Browse files
Add polymorphic support to SignalR STJ (#53035)
1 parent 73cbb8b commit 7fbe600

File tree

2 files changed

+172
-3
lines changed

2 files changed

+172
-3
lines changed

src/SignalR/common/Protocols.Json/src/Protocol/JsonHubProtocol.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ private void WriteCompletionMessage(CompletionMessage message, Utf8JsonWriter wr
611611
}
612612
else
613613
{
614-
JsonSerializer.Serialize(writer, message.Result, message.Result.GetType(), _payloadSerializerOptions);
614+
JsonSerializer.Serialize(writer, message.Result, _payloadSerializerOptions);
615615
}
616616
}
617617
}
@@ -633,7 +633,7 @@ private void WriteStreamItemMessage(StreamItemMessage message, Utf8JsonWriter wr
633633
}
634634
else
635635
{
636-
JsonSerializer.Serialize(writer, message.Item, message.Item.GetType(), _payloadSerializerOptions);
636+
JsonSerializer.Serialize(writer, message.Item, _payloadSerializerOptions);
637637
}
638638
}
639639

@@ -691,7 +691,7 @@ private void WriteArguments(object?[] arguments, Utf8JsonWriter writer)
691691
}
692692
else
693693
{
694-
JsonSerializer.Serialize(writer, argument, argument.GetType(), _payloadSerializerOptions);
694+
JsonSerializer.Serialize(writer, argument, _payloadSerializerOptions);
695695
}
696696
}
697697
writer.WriteEndArray();

src/SignalR/common/SignalR.Common/test/Internal/Protocol/JsonHubProtocolTests.cs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Text;
1010
using System.Text.Encodings.Web;
1111
using System.Text.Json;
12+
using System.Text.Json.Serialization;
1213
using Microsoft.AspNetCore.Internal;
1314
using Microsoft.AspNetCore.SignalR.Protocol;
1415
using Microsoft.Extensions.Options;
@@ -106,6 +107,106 @@ public void MagicCast()
106107
Assert.Equal(expectedMessage, message);
107108
}
108109

110+
[Fact]
111+
public void PolymorphicWorksWithInvocation()
112+
{
113+
Person todo = new JsonPersonExtended()
114+
{
115+
Name = "Person",
116+
Age = 99,
117+
};
118+
119+
var expectedJson = "{\"type\":1,\"target\":\"method\",\"arguments\":[{\"$type\":\"JsonPersonExtended\",\"age\":99,\"name\":\"Person\",\"child\":null,\"parent\":null}]}";
120+
121+
var writer = MemoryBufferWriter.Get();
122+
try
123+
{
124+
JsonHubProtocol.WriteMessage(new InvocationMessage("method", new[] { todo }), writer);
125+
126+
var json = Encoding.UTF8.GetString(writer.ToArray());
127+
Assert.Equal(Frame(expectedJson), json);
128+
129+
var binder = new TestBinder([typeof(JsonPerson)]);
130+
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(json));
131+
Assert.True(JsonHubProtocol.TryParseMessage(ref data, binder, out var message));
132+
133+
var invocationMessage = Assert.IsType<InvocationMessage>(message);
134+
Assert.Equal(1, invocationMessage.Arguments?.Length);
135+
PersonEqual(todo, invocationMessage.Arguments[0]);
136+
}
137+
finally
138+
{
139+
MemoryBufferWriter.Return(writer);
140+
}
141+
}
142+
143+
[Fact]
144+
public void PolymorphicWorksWithStreamItem()
145+
{
146+
Person todo = new JsonPersonExtended()
147+
{
148+
Name = "Person",
149+
Age = 99,
150+
};
151+
152+
var expectedJson = "{\"type\":2,\"invocationId\":\"1\",\"item\":{\"$type\":\"JsonPersonExtended\",\"age\":99,\"name\":\"Person\",\"child\":null,\"parent\":null}}";
153+
154+
var writer = MemoryBufferWriter.Get();
155+
try
156+
{
157+
JsonHubProtocol.WriteMessage(new StreamItemMessage("1", todo), writer);
158+
159+
var json = Encoding.UTF8.GetString(writer.ToArray());
160+
Assert.Equal(Frame(expectedJson), json);
161+
162+
var binder = new TestBinder(typeof(JsonPerson));
163+
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(json));
164+
Assert.True(JsonHubProtocol.TryParseMessage(ref data, binder, out var message));
165+
166+
var streamItemMessage = Assert.IsType<StreamItemMessage>(message);
167+
Assert.Equal("1", streamItemMessage.InvocationId);
168+
PersonEqual(todo, streamItemMessage.Item);
169+
}
170+
finally
171+
{
172+
MemoryBufferWriter.Return(writer);
173+
}
174+
}
175+
176+
[Fact]
177+
public void PolymorphicWorksWithCompletion()
178+
{
179+
Person todo = new JsonPersonExtended2()
180+
{
181+
Name = "Person",
182+
Location = "Canada",
183+
};
184+
185+
var expectedJson = "{\"type\":3,\"invocationId\":\"1\",\"result\":{\"$type\":\"JsonPersonExtended2\",\"location\":\"Canada\",\"name\":\"Person\",\"child\":null,\"parent\":null}}";
186+
187+
var writer = MemoryBufferWriter.Get();
188+
try
189+
{
190+
JsonHubProtocol.WriteMessage(CompletionMessage.WithResult("1", todo), writer);
191+
192+
var json = Encoding.UTF8.GetString(writer.ToArray());
193+
Assert.Equal(Frame(expectedJson), json);
194+
195+
var binder = new TestBinder(typeof(JsonPerson));
196+
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(json));
197+
Assert.True(JsonHubProtocol.TryParseMessage(ref data, binder, out var message));
198+
199+
var completionMessage = Assert.IsType<CompletionMessage>(message);
200+
Assert.Equal("1", completionMessage.InvocationId);
201+
Assert.True(completionMessage.HasResult);
202+
PersonEqual(todo, completionMessage.Result);
203+
}
204+
finally
205+
{
206+
MemoryBufferWriter.Return(writer);
207+
}
208+
}
209+
109210
public static IDictionary<string, JsonProtocolTestData> CustomProtocolTestData => new[]
110211
{
111212
new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"),
@@ -119,4 +220,72 @@ public void MagicCast()
119220
}.ToDictionary(t => t.Name);
120221

121222
public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name });
223+
224+
private class Person
225+
{
226+
public string Name { get; set; }
227+
public Person Child { get; set; }
228+
public Person Parent { get; set; }
229+
}
230+
231+
[JsonPolymorphic]
232+
[JsonDerivedType(typeof(JsonPersonExtended), nameof(JsonPersonExtended))]
233+
[JsonDerivedType(typeof(JsonPersonExtended2), nameof(JsonPersonExtended2))]
234+
private class JsonPerson : Person
235+
{ }
236+
237+
private class JsonPersonExtended : JsonPerson
238+
{
239+
public int Age { get; set; }
240+
}
241+
242+
private class JsonPersonExtended2 : JsonPerson
243+
{
244+
public string Location { get; set; }
245+
}
246+
247+
private static void PersonEqual(object expected, object actual)
248+
{
249+
if (expected is null && actual is null)
250+
{
251+
return;
252+
}
253+
254+
Assert.Equal(expected.GetType(), actual.GetType());
255+
256+
if (expected is JsonPersonExtended expectedPersonExtended && actual is JsonPersonExtended actualPersonExtended)
257+
{
258+
Assert.Equal(expectedPersonExtended.Name, actualPersonExtended.Name);
259+
Assert.Equal(expectedPersonExtended.Age, actualPersonExtended.Age);
260+
PersonEqual(expectedPersonExtended.Child, actualPersonExtended.Child);
261+
PersonEqual(expectedPersonExtended.Parent, actualPersonExtended.Parent);
262+
return;
263+
}
264+
265+
if (expected is JsonPersonExtended2 expectedPersonExtended2 && actual is JsonPersonExtended2 actualPersonExtended2)
266+
{
267+
Assert.Equal(expectedPersonExtended2.Name, actualPersonExtended2.Name);
268+
Assert.Equal(expectedPersonExtended2.Location, actualPersonExtended2.Location);
269+
PersonEqual(expectedPersonExtended2.Child, actualPersonExtended2.Child);
270+
PersonEqual(expectedPersonExtended2.Parent, actualPersonExtended2.Parent);
271+
return;
272+
}
273+
274+
if (expected is JsonPerson expectedJsonPerson && actual is JsonPerson actualJsonPerson)
275+
{
276+
Assert.Equal(expectedJsonPerson.Name, actualJsonPerson.Name);
277+
PersonEqual(expectedJsonPerson.Child, actualJsonPerson.Child);
278+
PersonEqual(expectedJsonPerson.Parent, actualJsonPerson.Parent);
279+
return;
280+
}
281+
282+
if (expected is Person expectedPerson && actual is Person actualPerson)
283+
{
284+
Assert.Equal(expectedPerson.Name, actualPerson.Name);
285+
PersonEqual(expectedPerson.Parent, actualPerson.Parent);
286+
PersonEqual(expectedPerson.Child, actualPerson.Child);
287+
}
288+
289+
Assert.False(true, "Passed in unexpected object(s)");
290+
}
122291
}

0 commit comments

Comments
 (0)