Browse Source

Make System.Text.Json the default for SignalR and remove Newtonsoft from shared framework (#9476)

BrennanConroy 6 years ago
parent
commit
c84e37f30d
28 changed files with 240 additions and 158 deletions
  1. 0 1
      eng/SharedFramework.External.props
  2. 0 1
      eng/SharedFramework.Local.props
  3. 0 2
      eng/targets/ResolveReferences.targets
  4. 2 2
      src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.csproj
  5. 1 1
      src/SignalR/clients/csharp/Client.Core/src/HubConnectionBuilder.cs
  6. 2 2
      src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj
  7. 1 1
      src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs
  8. 0 1
      src/SignalR/clients/ts/FunctionalTests/ComplexObject.cs
  9. 0 6
      src/SignalR/clients/ts/FunctionalTests/Startup.cs
  10. 0 1
      src/SignalR/clients/ts/FunctionalTests/TestHub.cs
  11. 2 4
      src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts
  12. 12 0
      src/SignalR/common/Protocols.Json/ref/Microsoft.AspNetCore.SignalR.Protocols.Json.netcoreapp3.0.cs
  13. 12 0
      src/SignalR/common/Protocols.Json/ref/Microsoft.AspNetCore.SignalR.Protocols.Json.netstandard2.0.cs
  14. 25 0
      src/SignalR/common/Protocols.Json/src/JsonHubProtocolOptions.cs
  15. 14 0
      src/SignalR/common/Protocols.Json/src/JsonProtocolDependencyInjectionExtensions.cs
  16. 1 1
      src/SignalR/common/Protocols.Json/src/Microsoft.AspNetCore.SignalR.Protocols.Json.csproj
  17. 35 17
      src/SignalR/common/Protocols.Json/src/Protocol/JsonHubProtocol.cs
  18. 0 2
      src/SignalR/common/Protocols.NewtonsoftJson/src/Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj
  19. 21 7
      src/SignalR/common/SignalR.Common/test/Internal/Protocol/JsonHubProtocolTests.cs
  20. 49 39
      src/SignalR/common/SignalR.Common/test/Internal/Protocol/JsonHubProtocolTestsBase.cs
  21. 42 50
      src/SignalR/common/SignalR.Common/test/Internal/Protocol/NewtonsoftJsonHubProtocolTests.cs
  22. 1 1
      src/SignalR/common/testassets/Tests.Utils/HubConnectionContextUtils.cs
  23. 1 1
      src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.csproj
  24. 2 2
      src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj
  25. 1 1
      src/SignalR/server/Core/src/SignalRDependencyInjectionExtensions.cs
  26. 13 13
      src/SignalR/server/SignalR/test/HubConnectionHandlerTestUtils/Hubs.cs
  27. 2 1
      src/SignalR/server/Specification.Tests/src/Microsoft.AspNetCore.SignalR.Specification.Tests.csproj
  28. 1 1
      src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj

+ 0 - 1
eng/SharedFramework.External.props

@@ -68,7 +68,6 @@
 
 
       If these are needed as direct dependencies, it is okay to change them to ExternalAspNetCoreAppReference and move up into sections above.
       If these are needed as direct dependencies, it is okay to change them to ExternalAspNetCoreAppReference and move up into sections above.
     -->
     -->
-    <_TransitiveExternalAspNetCoreAppReference Include="Newtonsoft.Json"                                    Version="$(NewtonsoftJsonPackageVersion)" />
     <_TransitiveExternalAspNetCoreAppReference Include="System.Security.Cryptography.Pkcs"                  Version="$(SystemSecurityCryptographyPkcsPackageVersion)" />
     <_TransitiveExternalAspNetCoreAppReference Include="System.Security.Cryptography.Pkcs"                  Version="$(SystemSecurityCryptographyPkcsPackageVersion)" />
     <_TransitiveExternalAspNetCoreAppReference Include="System.Security.Permissions"                        Version="$(SystemSecurityPermissionsPackageVersion)" />
     <_TransitiveExternalAspNetCoreAppReference Include="System.Security.Permissions"                        Version="$(SystemSecurityPermissionsPackageVersion)" />
   </ItemGroup>
   </ItemGroup>

+ 0 - 1
eng/SharedFramework.Local.props

@@ -12,7 +12,6 @@
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Connections.Abstractions" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Connections.Abstractions" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Http.Connections.Common" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Http.Connections.Common" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
-    <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Common" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Common" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Browser" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Browser" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" />
     <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" />

+ 0 - 2
eng/targets/ResolveReferences.targets

@@ -79,13 +79,11 @@
     <_InvalidReferenceToNonSharedFxAssembly Condition="'$(IsAspNetCoreApp)' == 'true'"
     <_InvalidReferenceToNonSharedFxAssembly Condition="'$(IsAspNetCoreApp)' == 'true'"
       Include="@(Reference)"
       Include="@(Reference)"
       Exclude="
       Exclude="
-        Newtonsoft.Json;
         @(AspNetCoreAppReference);
         @(AspNetCoreAppReference);
         @(AspNetCoreAppReferenceAndPackage);
         @(AspNetCoreAppReferenceAndPackage);
         @(ExternalAspNetCoreAppReference);
         @(ExternalAspNetCoreAppReference);
         @(_CompilationOnlyReference);
         @(_CompilationOnlyReference);
         @(Reference->WithMetadataValue('IsSharedSource', 'true'))" />
         @(Reference->WithMetadataValue('IsSharedSource', 'true'))" />
-    <!-- TODO: remove Newtonsoft.Json from this list once https://github.com/aspnet/AspNetCore/issues/4260 is resolved -->
     <_OriginalReferences Include="@(Reference)" />
     <_OriginalReferences Include="@(Reference)" />
     <!--
     <!--
       Turn Reference items into a ProjectReference when UseProjectReferences is true.
       Turn Reference items into a ProjectReference when UseProjectReferences is true.

+ 2 - 2
src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.csproj

@@ -6,7 +6,7 @@
   <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
   <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
     <Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs" />
     <Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common"  />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common"  />
-    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson"  />
+    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json"  />
     <Reference Include="Microsoft.Extensions.DependencyInjection"  />
     <Reference Include="Microsoft.Extensions.DependencyInjection"  />
     <Reference Include="Microsoft.Extensions.Logging"  />
     <Reference Include="Microsoft.Extensions.Logging"  />
     <Reference Include="System.Threading.Channels"  />
     <Reference Include="System.Threading.Channels"  />
@@ -14,7 +14,7 @@
 <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
 <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
     <Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netcoreapp3.0.cs" />
     <Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netcoreapp3.0.cs" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common"  />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common"  />
-    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson"  />
+    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json"  />
     <Reference Include="Microsoft.Extensions.DependencyInjection"  />
     <Reference Include="Microsoft.Extensions.DependencyInjection"  />
     <Reference Include="Microsoft.Extensions.Logging"  />
     <Reference Include="Microsoft.Extensions.Logging"  />
     <Reference Include="System.Threading.Channels"  />
     <Reference Include="System.Threading.Channels"  />

+ 1 - 1
src/SignalR/clients/csharp/Client.Core/src/HubConnectionBuilder.cs

@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
             Services = new ServiceCollection();
             Services = new ServiceCollection();
             Services.AddSingleton<HubConnection>();
             Services.AddSingleton<HubConnection>();
             Services.AddLogging();
             Services.AddLogging();
-            this.AddNewtonsoftJsonProtocol();
+            this.AddJsonProtocol();
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />

+ 2 - 2
src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
 
   <PropertyGroup>
   <PropertyGroup>
     <Description>Client for ASP.NET Core SignalR</Description>
     <Description>Client for ASP.NET Core SignalR</Description>
@@ -22,7 +22,7 @@
 
 
   <ItemGroup>
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
-    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" />
+    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
     <Reference Include="Microsoft.Extensions.DependencyInjection" />
     <Reference Include="Microsoft.Extensions.DependencyInjection" />
     <Reference Include="Microsoft.Extensions.Logging" />
     <Reference Include="Microsoft.Extensions.Logging" />
     <Reference Include="System.Threading.Channels" />
     <Reference Include="System.Threading.Channels" />

+ 1 - 1
src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs

@@ -1014,7 +1014,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
             }
             }
         }
         }
 
 
-        [Theory]
+        [Theory(Skip = "Will be fixed by https://github.com/dotnet/corefx/issues/36901")]
         [MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
         [MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
         public async Task ServerThrowsHubExceptionOnHubMethodArgumentTypeMismatch(string hubProtocolName, HttpTransportType transportType, string hubPath)
         public async Task ServerThrowsHubExceptionOnHubMethodArgumentTypeMismatch(string hubProtocolName, HttpTransportType transportType, string hubPath)
         {
         {

+ 0 - 1
src/SignalR/clients/ts/FunctionalTests/ComplexObject.cs

@@ -10,7 +10,6 @@ namespace FunctionalTests
         public string String { get; set; }
         public string String { get; set; }
         public int[] IntArray { get; set; }
         public int[] IntArray { get; set; }
         public byte[] ByteArray { get; set; }
         public byte[] ByteArray { get; set; }
-        public Guid GUID { get; set; }
         public DateTime DateTime { get;set; }
         public DateTime DateTime { get;set; }
     }
     }
 }
 }

+ 0 - 6
src/SignalR/clients/ts/FunctionalTests/Startup.cs

@@ -41,12 +41,6 @@ namespace FunctionalTests
             {
             {
                 options.EnableDetailedErrors = true;
                 options.EnableDetailedErrors = true;
             })
             })
-            .AddNewtonsoftJsonProtocol(options =>
-            {
-                // we are running the same tests with JSON and MsgPack protocols and having
-                // consistent casing makes it cleaner to verify results
-                options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
-            })
             .AddMessagePackProtocol();
             .AddMessagePackProtocol();
 
 
             services.AddCors();
             services.AddCors();

+ 0 - 1
src/SignalR/clients/ts/FunctionalTests/TestHub.cs

@@ -125,7 +125,6 @@ namespace FunctionalTests
             {
             {
                 ByteArray = new byte[] { 0x1, 0x2, 0x3 },
                 ByteArray = new byte[] { 0x1, 0x2, 0x3 },
                 DateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                 DateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc),
-                GUID = new Guid("00010203-0405-0607-0706-050403020100"),
                 IntArray = new int[] { 1, 2, 3 },
                 IntArray = new int[] { 1, 2, 3 },
                 String = "hello world",
                 String = "hello world",
             };
             };

+ 2 - 4
src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts

@@ -457,12 +457,11 @@ describe("hubConnection", () => {
 
 
                 const complexObject = {
                 const complexObject = {
                     ByteArray: protocol.name === "json"
                     ByteArray: protocol.name === "json"
-                        ? "aGVsbG8="
+                        ? new Array(0x68, 0x65, 0x6c, 0x6c, 0x6f)
                         : new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]),
                         : new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]),
                     DateTime: protocol.name === "json"
                     DateTime: protocol.name === "json"
                         ? "2002-04-01T10:20:15Z"
                         ? "2002-04-01T10:20:15Z"
                         : new Date(Date.UTC(2002, 3, 1, 10, 20, 15)), // Apr 1, 2002, 10:20:15am UTC
                         : new Date(Date.UTC(2002, 3, 1, 10, 20, 15)), // Apr 1, 2002, 10:20:15am UTC
-                    GUID: "00010203-0405-0607-0706-050403020100",
                     IntArray: [0x01, 0x02, 0x03, 0xff],
                     IntArray: [0x01, 0x02, 0x03, 0xff],
                     String: "Hello, World!",
                     String: "Hello, World!",
                 };
                 };
@@ -500,12 +499,11 @@ describe("hubConnection", () => {
 
 
                 const complexObject = {
                 const complexObject = {
                     ByteArray: protocol.name === "json"
                     ByteArray: protocol.name === "json"
-                        ? "AQID"
+                        ? new Array(0x1, 0x2, 0x3)
                         : new Uint8Array([0x1, 0x2, 0x3]),
                         : new Uint8Array([0x1, 0x2, 0x3]),
                     DateTime: protocol.name === "json"
                     DateTime: protocol.name === "json"
                         ? "2000-01-01T00:00:00Z"
                         ? "2000-01-01T00:00:00Z"
                         : new Date(Date.UTC(2000, 0, 1)),
                         : new Date(Date.UTC(2000, 0, 1)),
-                    GUID: "00010203-0405-0607-0706-050403020100",
                     IntArray: [0x01, 0x02, 0x03],
                     IntArray: [0x01, 0x02, 0x03],
                     String: "hello world",
                     String: "hello world",
                 };
                 };

+ 12 - 0
src/SignalR/common/Protocols.Json/ref/Microsoft.AspNetCore.SignalR.Protocols.Json.netcoreapp3.0.cs

@@ -1,11 +1,22 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
+namespace Microsoft.AspNetCore.SignalR
+{
+    public partial class JsonHubProtocolOptions
+    {
+        public JsonHubProtocolOptions() { }
+        public bool AllowTrailingCommas { get { throw null; } set { } }
+        public bool IgnoreNullValues { get { throw null; } set { } }
+        public bool WriteIndented { get { throw null; } set { } }
+    }
+}
 namespace Microsoft.AspNetCore.SignalR.Protocol
 namespace Microsoft.AspNetCore.SignalR.Protocol
 {
 {
     public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol
     public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol
     {
     {
         public JsonHubProtocol() { }
         public JsonHubProtocol() { }
+        public JsonHubProtocol(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> options) { }
         public int MinorVersion { get { throw null; } }
         public int MinorVersion { get { throw null; } }
         public string Name { get { throw null; } }
         public string Name { get { throw null; } }
         public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } }
         public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } }
@@ -21,5 +32,6 @@ namespace Microsoft.Extensions.DependencyInjection
     public static partial class JsonProtocolDependencyInjectionExtensions
     public static partial class JsonProtocolDependencyInjectionExtensions
     {
     {
         public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
         public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
+        public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder, System.Action<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> configure) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
     }
     }
 }
 }

+ 12 - 0
src/SignalR/common/Protocols.Json/ref/Microsoft.AspNetCore.SignalR.Protocols.Json.netstandard2.0.cs

@@ -1,11 +1,22 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
+namespace Microsoft.AspNetCore.SignalR
+{
+    public partial class JsonHubProtocolOptions
+    {
+        public JsonHubProtocolOptions() { }
+        public bool AllowTrailingCommas { get { throw null; } set { } }
+        public bool IgnoreNullValues { get { throw null; } set { } }
+        public bool WriteIndented { get { throw null; } set { } }
+    }
+}
 namespace Microsoft.AspNetCore.SignalR.Protocol
 namespace Microsoft.AspNetCore.SignalR.Protocol
 {
 {
     public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol
     public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol
     {
     {
         public JsonHubProtocol() { }
         public JsonHubProtocol() { }
+        public JsonHubProtocol(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> options) { }
         public int MinorVersion { get { throw null; } }
         public int MinorVersion { get { throw null; } }
         public string Name { get { throw null; } }
         public string Name { get { throw null; } }
         public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } }
         public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } }
@@ -21,5 +32,6 @@ namespace Microsoft.Extensions.DependencyInjection
     public static partial class JsonProtocolDependencyInjectionExtensions
     public static partial class JsonProtocolDependencyInjectionExtensions
     {
     {
         public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
         public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
+        public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder, System.Action<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> configure) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
     }
     }
 }
 }

+ 25 - 0
src/SignalR/common/Protocols.Json/src/JsonHubProtocolOptions.cs

@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Text.Json.Serialization;
+using Microsoft.AspNetCore.SignalR.Protocol;
+
+namespace Microsoft.AspNetCore.SignalR
+{
+    /// <summary>
+    /// Options used to configure a <see cref="JsonHubProtocolOptions"/> instance.
+    /// </summary>
+    public class JsonHubProtocolOptions
+    {
+        internal readonly JsonSerializerOptions _serializerOptions;
+
+        public JsonHubProtocolOptions()
+        {
+            _serializerOptions = JsonHubProtocol.CreateDefaultSerializerSettings();
+        }
+
+        public bool IgnoreNullValues { get => _serializerOptions.IgnoreNullValues; set => _serializerOptions.IgnoreNullValues = value; }
+        public bool WriteIndented { get => _serializerOptions.WriteIndented; set => _serializerOptions.WriteIndented = value; }
+        public bool AllowTrailingCommas { get => _serializerOptions.AllowTrailingCommas; set => _serializerOptions.AllowTrailingCommas = value; }
+    }
+}

+ 14 - 0
src/SignalR/common/Protocols.Json/src/JsonProtocolDependencyInjectionExtensions.cs

@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
+using System;
 using Microsoft.AspNetCore.SignalR;
 using Microsoft.AspNetCore.SignalR;
 using Microsoft.AspNetCore.SignalR.Protocol;
 using Microsoft.AspNetCore.SignalR.Protocol;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -21,8 +22,21 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param>
         /// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param>
         /// <returns>The value of <paramref name="builder"/></returns>
         /// <returns>The value of <paramref name="builder"/></returns>
         public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : ISignalRBuilder
         public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : ISignalRBuilder
+            => AddJsonProtocol(builder, _ => { });
+
+        /// <summary>
+        /// Enables the JSON protocol for SignalR and allows options for the JSON protocol to be configured.
+        /// </summary>
+        /// <remarks>
+        /// Any options configured here will be applied, even if the JSON protocol has already been registered with the server.
+        /// </remarks>
+        /// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param>
+        /// <param name="configure">A delegate that can be used to configure the <see cref="JsonHubProtocolOptions"/></param>
+        /// <returns>The value of <paramref name="builder"/></returns>
+        public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder, Action<JsonHubProtocolOptions> configure) where TBuilder : ISignalRBuilder
         {
         {
             builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, JsonHubProtocol>());
             builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, JsonHubProtocol>());
+            builder.Services.Configure(configure);
             return builder;
             return builder;
         }
         }
     }
     }

+ 1 - 1
src/SignalR/common/Protocols.Json/src/Microsoft.AspNetCore.SignalR.Protocols.Json.csproj

@@ -21,7 +21,7 @@
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
   </ItemGroup>
   </ItemGroup>
 
 
-  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'" >
+  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
     <Reference Include="Microsoft.Bcl.Json.Sources" />
     <Reference Include="Microsoft.Bcl.Json.Sources" />
     <Reference Include="System.Buffers" />
     <Reference Include="System.Buffers" />
     <Reference Include="System.Runtime.CompilerServices.Unsafe" />
     <Reference Include="System.Runtime.CompilerServices.Unsafe" />

+ 35 - 17
src/SignalR/common/Protocols.Json/src/Protocol/JsonHubProtocol.cs

@@ -10,6 +10,7 @@ using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
 using Microsoft.AspNetCore.Connections;
 using Microsoft.AspNetCore.Connections;
 using Microsoft.AspNetCore.Internal;
 using Microsoft.AspNetCore.Internal;
+using Microsoft.Extensions.Options;
 
 
 namespace Microsoft.AspNetCore.SignalR.Protocol
 namespace Microsoft.AspNetCore.SignalR.Protocol
 {
 {
@@ -42,11 +43,25 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
         private static readonly int ProtocolVersion = 1;
         private static readonly int ProtocolVersion = 1;
         private static readonly int ProtocolMinorVersion = 0;
         private static readonly int ProtocolMinorVersion = 0;
 
 
+        /// <summary>
+        /// Gets the serializer used to serialize invocation arguments and return values.
+        /// </summary>
+        private readonly JsonSerializerOptions _payloadSerializerOptions;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="JsonHubProtocol"/> class.
+        /// </summary>
+        public JsonHubProtocol() : this(Options.Create(new JsonHubProtocolOptions()))
+        {
+        }
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="JsonHubProtocol"/> class.
         /// Initializes a new instance of the <see cref="JsonHubProtocol"/> class.
         /// </summary>
         /// </summary>
-        public JsonHubProtocol()
+        /// <param name="options">The options used to initialize the protocol.</param>
+        public JsonHubProtocol(IOptions<JsonHubProtocolOptions> options)
         {
         {
+            _payloadSerializerOptions = options.Value._serializerOptions;
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />
@@ -188,11 +203,8 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
                                 {
                                 {
                                     // If we have an invocation id already we can parse the end result
                                     // If we have an invocation id already we can parse the end result
                                     var returnType = binder.GetReturnType(invocationId);
                                     var returnType = binder.GetReturnType(invocationId);
-                                    if (reader.TokenType != JsonTokenType.Null)
-                                    {
-                                        using var token = JsonDocument.ParseValue(ref reader);
-                                        result = BindType(token.RootElement, returnType);
-                                    }
+                                    using var token = JsonDocument.ParseValue(ref reader);
+                                    result = BindType(token.RootElement, returnType);
                                 }
                                 }
                             }
                             }
                             else if (reader.TextEquals(ItemPropertyNameBytes))
                             else if (reader.TextEquals(ItemPropertyNameBytes))
@@ -216,11 +228,8 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
                                 try
                                 try
                                 {
                                 {
                                     var itemType = binder.GetStreamItemType(id);
                                     var itemType = binder.GetStreamItemType(id);
-                                    if (reader.TokenType != JsonTokenType.Null)
-                                    {
-                                        using var token = JsonDocument.ParseValue(ref reader);
-                                        item = BindType(token.RootElement, itemType);
-                                    }
+                                    using var token = JsonDocument.ParseValue(ref reader);
+                                    item = BindType(token.RootElement, itemType);
                                 }
                                 }
                                 catch (Exception ex)
                                 catch (Exception ex)
                                 {
                                 {
@@ -571,7 +580,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
 
 
         private JsonDocument GetParsedObject(object obj, Type type)
         private JsonDocument GetParsedObject(object obj, Type type)
         {
         {
-            var bytes = JsonSerializer.ToBytes(obj, type);
+            var bytes = JsonSerializer.ToBytes(obj, type, _payloadSerializerOptions);
             var token = JsonDocument.Parse(bytes);
             var token = JsonDocument.Parse(bytes);
             return token;
             return token;
         }
         }
@@ -695,11 +704,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
                 return jsonObject.GetDateTimeOffset();
                 return jsonObject.GetDateTimeOffset();
             }
             }
 
 
-            if (jsonObject.Type == JsonValueType.Null)
-            {
-                return null;
-            }
-            return JsonSerializer.Parse(jsonObject.GetRawText(), type);
+            return JsonSerializer.Parse(jsonObject.GetRawText(), type, _payloadSerializerOptions);
         }
         }
 
 
         private object[] BindTypes(JsonElement jsonArray, IReadOnlyList<Type> paramTypes)
         private object[] BindTypes(JsonElement jsonArray, IReadOnlyList<Type> paramTypes)
@@ -756,5 +761,18 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
 
 
             return message;
             return message;
         }
         }
+
+        internal static JsonSerializerOptions CreateDefaultSerializerSettings()
+        {
+            var options = new JsonSerializerOptions();
+            options.WriteIndented = false;
+            options.ReadCommentHandling = JsonCommentHandling.Disallow;
+            options.AllowTrailingCommas = false;
+            options.IgnoreNullValues = false;
+            options.IgnoreReadOnlyProperties = false;
+            // TODO: camelCase
+
+            return options;
+        }
     }
     }
 }
 }

+ 0 - 2
src/SignalR/common/Protocols.NewtonsoftJson/src/Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj

@@ -3,8 +3,6 @@
   <PropertyGroup>
   <PropertyGroup>
     <Description>Implements the SignalR Hub Protocol using Newtonsoft.Json.</Description>
     <Description>Implements the SignalR Hub Protocol using Newtonsoft.Json.</Description>
     <TargetFramework>netstandard2.0</TargetFramework>
     <TargetFramework>netstandard2.0</TargetFramework>
-    <!-- TODO: remove this from the shared framework once we have a JSON protocol implementation which doesn't use Newtonsoft.Json. -->
-    <IsAspNetCoreApp>true</IsAspNetCoreApp>
     <RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
     <RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <IsShippingPackage>true</IsShippingPackage>
     <IsShippingPackage>true</IsShippingPackage>

+ 21 - 7
src/SignalR/common/SignalR.Common/test/Internal/Protocol/JsonHubProtocolTests.cs

@@ -9,6 +9,7 @@ using System.Linq;
 using System.Text;
 using System.Text;
 using Microsoft.AspNetCore.Internal;
 using Microsoft.AspNetCore.Internal;
 using Microsoft.AspNetCore.SignalR.Protocol;
 using Microsoft.AspNetCore.SignalR.Protocol;
+using Microsoft.Extensions.Options;
 using Xunit;
 using Xunit;
 
 
 namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
@@ -17,6 +18,17 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
     {
     {
         protected override IHubProtocol JsonHubProtocol => new JsonHubProtocol();
         protected override IHubProtocol JsonHubProtocol => new JsonHubProtocol();
 
 
+        protected override IHubProtocol GetProtocolWithOptions(bool useCamelCase, bool ignoreNullValues)
+        {
+            var protocolOptions = new JsonHubProtocolOptions()
+            {
+                IgnoreNullValues = ignoreNullValues,
+                //TODO: camelCase
+            };
+
+            return new JsonHubProtocol(Options.Create(protocolOptions));
+        }
+
         [Theory]
         [Theory]
         [InlineData("", "Error reading JSON.")]
         [InlineData("", "Error reading JSON.")]
         [InlineData("42", "Unexpected JSON Token Type 'Number'. Expected a JSON Object.")]
         [InlineData("42", "Unexpected JSON Token Type 'Number'. Expected a JSON Object.")]
@@ -42,7 +54,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
             var writer = MemoryBufferWriter.Get();
             var writer = MemoryBufferWriter.Get();
             try
             try
             {
             {
-                JsonHubProtocol.WriteMessage(testData.Message, writer);
+                var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
+                protocol.WriteMessage(testData.Message, writer);
                 var json = Encoding.UTF8.GetString(writer.ToArray());
                 var json = Encoding.UTF8.GetString(writer.ToArray());
 
 
                 Assert.Equal(expectedOutput, json);
                 Assert.Equal(expectedOutput, json);
@@ -63,7 +76,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
 
             var binder = new TestBinder(testData.Message);
             var binder = new TestBinder(testData.Message);
             var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
             var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
-            JsonHubProtocol.TryParseMessage(ref data, binder, out var message);
+            var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
+            protocol.TryParseMessage(ref data, binder, out var message);
 
 
             Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
             Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
         }
         }
@@ -83,11 +97,11 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
 
         public static IDictionary<string, JsonProtocolTestData> CustomProtocolTestData => new[]
         public static IDictionary<string, JsonProtocolTestData> CustomProtocolTestData => new[]
         {
         {
-            new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"),
-            new JsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), "{\"type\":2,\"invocationId\":\"123\",\"item\":2}"),
-            new JsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), "{\"type\":3,\"invocationId\":\"123\",\"result\":2}"),
-            new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"),
-            new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20\\u002b12:34\"]}"),
+            new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"),
+            new JsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":2}"),
+            new JsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":2}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"),
+            new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), true, true, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20\\u002b12:34\"]}"),
         }.ToDictionary(t => t.Name);
         }.ToDictionary(t => t.Name);
 
 
         public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name });
         public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name });

+ 49 - 39
src/SignalR/common/SignalR.Common/test/Internal/Protocol/JsonHubProtocolTestsBase.cs

@@ -19,6 +19,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
     {
     {
         protected abstract IHubProtocol JsonHubProtocol { get; }
         protected abstract IHubProtocol JsonHubProtocol { get; }
 
 
+        protected abstract IHubProtocol GetProtocolWithOptions(bool useCamelCase, bool ignoreNullValues);
+
         public static readonly IDictionary<string, string> TestHeaders = new Dictionary<string, string>
         public static readonly IDictionary<string, string> TestHeaders = new Dictionary<string, string>
         {
         {
             { "Foo", "Bar" },
             { "Foo", "Bar" },
@@ -31,18 +33,18 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
 
         public static IDictionary<string, JsonProtocolTestData> ProtocolTestData => new[]
         public static IDictionary<string, JsonProtocolTestData> ProtocolTestData => new[]
         {
         {
-            new JsonProtocolTestData("InvocationMessage_HasInvocationId", new InvocationMessage("123", "Target", new object[] { 1, "Foo" }), "{\"type\":1,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"),
-            new JsonProtocolTestData("InvocationMessage_HasBoolArgument", new InvocationMessage(null, "Target", new object[] { true }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[true]}"),
-            new JsonProtocolTestData("InvocationMessage_HasNullArgument", new InvocationMessage(null, "Target", new object[] { null }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[null]}"),
-            new JsonProtocolTestData("InvocationMessage_HasStreamArgument", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__" }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"),
-            new JsonProtocolTestData("InvocationMessage_HasStreamAndNormalArgument", new InvocationMessage(null, "Target", new object[] { 42 }, new string[] { "__test_id__" }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[42],\"streamIds\":[\"__test_id__\"]}"),
-            new JsonProtocolTestData("InvocationMessage_HasMultipleStreams", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__", "__test_id2__" }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\",\"__test_id2__\"]}"),
-            new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgument", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
-
-            new JsonProtocolTestData("StreamItemMessage_HasIntegerItem", new StreamItemMessage("123", 1), "{\"type\":2,\"invocationId\":\"123\",\"item\":1}"),
-            new JsonProtocolTestData("StreamItemMessage_HasStringItem", new StreamItemMessage("123", "Foo"), "{\"type\":2,\"invocationId\":\"123\",\"item\":\"Foo\"}"),
-            new JsonProtocolTestData("StreamItemMessage_HasBoolItem", new StreamItemMessage("123", true), "{\"type\":2,\"invocationId\":\"123\",\"item\":true}"),
-            new JsonProtocolTestData("StreamItemMessage_HasNullItem", new StreamItemMessage("123", null), "{\"type\":2,\"invocationId\":\"123\",\"item\":null}"),
+            new JsonProtocolTestData("InvocationMessage_HasInvocationId", new InvocationMessage("123", "Target", new object[] { 1, "Foo" }), true, true, "{\"type\":1,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"),
+            new JsonProtocolTestData("InvocationMessage_HasBoolArgument", new InvocationMessage(null, "Target", new object[] { true }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[true]}"),
+            new JsonProtocolTestData("InvocationMessage_HasNullArgument", new InvocationMessage(null, "Target", new object[] { null }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[null]}"),
+            new JsonProtocolTestData("InvocationMessage_HasStreamArgument", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__" }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"),
+            new JsonProtocolTestData("InvocationMessage_HasStreamAndNormalArgument", new InvocationMessage(null, "Target", new object[] { 42 }, new string[] { "__test_id__" }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[42],\"streamIds\":[\"__test_id__\"]}"),
+            new JsonProtocolTestData("InvocationMessage_HasMultipleStreams", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__", "__test_id2__" }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\",\"__test_id2__\"]}"),
+            new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgument", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), true, true, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
+
+            new JsonProtocolTestData("StreamItemMessage_HasIntegerItem", new StreamItemMessage("123", 1), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":1}"),
+            new JsonProtocolTestData("StreamItemMessage_HasStringItem", new StreamItemMessage("123", "Foo"), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":\"Foo\"}"),
+            new JsonProtocolTestData("StreamItemMessage_HasBoolItem", new StreamItemMessage("123", true), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":true}"),
+            new JsonProtocolTestData("StreamItemMessage_HasNullItem", new StreamItemMessage("123", null), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":null}"),
 
 
             // Dictionary not supported yet
             // Dictionary not supported yet
             //new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}}"),
             //new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}}"),
@@ -50,25 +52,26 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
             //new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}]}"),
             //new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}]}"),
             //new JsonProtocolTestData("CancelInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new CancelInvocationMessage("123")), "{\"type\":5," + SerializedHeaders + ",\"invocationId\":\"123\"}"),
             //new JsonProtocolTestData("CancelInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new CancelInvocationMessage("123")), "{\"type\":5," + SerializedHeaders + ",\"invocationId\":\"123\"}"),
 
 
-            new JsonProtocolTestData("CompletionMessage_HasIntegerResult", CompletionMessage.WithResult("123", 1), "{\"type\":3,\"invocationId\":\"123\",\"result\":1}"),
-            new JsonProtocolTestData("CompletionMessage_HasStringResult", CompletionMessage.WithResult("123", "Foo"), "{\"type\":3,\"invocationId\":\"123\",\"result\":\"Foo\"}"),
-            new JsonProtocolTestData("CompletionMessage_HasBoolResult", CompletionMessage.WithResult("123", true), "{\"type\":3,\"invocationId\":\"123\",\"result\":true}"),
-            new JsonProtocolTestData("CompletionMessage_HasNullResult", CompletionMessage.WithResult("123", null), "{\"type\":3,\"invocationId\":\"123\",\"result\":null}"),
-            new JsonProtocolTestData("CompletionMessage_HasError", CompletionMessage.WithError("123", "Whoops!"), "{\"type\":3,\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
-            new JsonProtocolTestData("CompletionMessage_HasErrorAndHeaders", AddHeaders(TestHeaders, CompletionMessage.WithError("123", "Whoops!")), "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
+            new JsonProtocolTestData("CompletionMessage_HasIntegerResult", CompletionMessage.WithResult("123", 1), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":1}"),
+            new JsonProtocolTestData("CompletionMessage_HasStringResult", CompletionMessage.WithResult("123", "Foo"), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":\"Foo\"}"),
+            new JsonProtocolTestData("CompletionMessage_HasBoolResult", CompletionMessage.WithResult("123", true), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":true}"),
+            new JsonProtocolTestData("CompletionMessage_HasNullResult", CompletionMessage.WithResult("123", null), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":null}"),
+            new JsonProtocolTestData("CompletionMessage_HasError", CompletionMessage.WithError("123", "Whoops!"), true, true, "{\"type\":3,\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
+            new JsonProtocolTestData("CompletionMessage_HasErrorAndHeaders", AddHeaders(TestHeaders, CompletionMessage.WithError("123", "Whoops!")), true, true, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
 
 
-            new JsonProtocolTestData("StreamInvocationMessage_HasInvocationId", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo" }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"),
-            new JsonProtocolTestData("StreamInvocationMessage_HasBoolArgument", new StreamInvocationMessage("123", "Target", new object[] { true }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[true]}"),
-            new JsonProtocolTestData("StreamInvocationMessage_HasNullArgument", new StreamInvocationMessage("123", "Target", new object[] { null }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[null]}"),
-            new JsonProtocolTestData("StreamInvocationMessage_HasStreamArgument", new StreamInvocationMessage("123", "Target", Array.Empty<object>(), new string[] { "__test_id__" }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasInvocationId", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo" }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasBoolArgument", new StreamInvocationMessage("123", "Target", new object[] { true }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[true]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasNullArgument", new StreamInvocationMessage("123", "Target", new object[] { null }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[null]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasStreamArgument", new StreamInvocationMessage("123", "Target", Array.Empty<object>(), new string[] { "__test_id__" }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"),
 
 
-            new JsonProtocolTestData("CancelInvocationMessage_HasInvocationId", new CancelInvocationMessage("123"), "{\"type\":5,\"invocationId\":\"123\"}"),
+            new JsonProtocolTestData("CancelInvocationMessage_HasInvocationId", new CancelInvocationMessage("123"), true, true, "{\"type\":5,\"invocationId\":\"123\"}"),
 
 
-            new JsonProtocolTestData("PingMessage", PingMessage.Instance, "{\"type\":6}"),
+            new JsonProtocolTestData("PingMessage", PingMessage.Instance, true, true, "{\"type\":6}"),
 
 
-            new JsonProtocolTestData("CloseMessage", CloseMessage.Empty, "{\"type\":7}"),
-            new JsonProtocolTestData("CloseMessage_HasError", new CloseMessage("Error!"), "{\"type\":7,\"error\":\"Error!\"}"),
-            new JsonProtocolTestData("CloseMessage_HasErrorEmptyString", new CloseMessage(""), "{\"type\":7,\"error\":\"\"}"),
+            new JsonProtocolTestData("CloseMessage", CloseMessage.Empty, false, true, "{\"type\":7}"),
+            new JsonProtocolTestData("CloseMessage_HasError", new CloseMessage("Error!"), false, true, "{\"type\":7,\"error\":\"Error!\"}"),
+            new JsonProtocolTestData("CloseMessage_HasErrorEmptyString", new CloseMessage(""), false, true, "{\"type\":7,\"error\":\"\"}"),
+            new JsonProtocolTestData("CloseMessage_HasErrorWithCamelCase", new CloseMessage("Error!"), true, true, "{\"type\":7,\"error\":\"Error!\"}"),
 
 
         }.ToDictionary(t => t.Name);
         }.ToDictionary(t => t.Name);
 
 
@@ -76,13 +79,12 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
 
         public static IDictionary<string, JsonProtocolTestData> OutOfOrderJsonTestData => new[]
         public static IDictionary<string, JsonProtocolTestData> OutOfOrderJsonTestData => new[]
         {
         {
-            new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"),
-            //new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"),
-            //new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgumentFirst", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), false, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"),
-            new JsonProtocolTestData("InvocationMessage_IntegerArrayArgumentFirst", new InvocationMessage("Method", new object[] { 1, 2 }), "{ \"arguments\": [1,2], \"type\":1, \"target\": \"Method\" }"),
-            new JsonProtocolTestData("StreamInvocationMessage_IntegerArrayArgumentFirst", new StreamInvocationMessage("3", "Method", new object[] { 1, 2 }), "{ \"type\":4, \"arguments\": [1,2], \"target\": \"Method\", \"invocationId\": \"3\" }"),
-            new JsonProtocolTestData("CompletionMessage_ResultFirst", new CompletionMessage("15", null, 10, hasResult: true), "{ \"type\":3, \"result\": 10, \"invocationId\": \"15\" }"),
-            new JsonProtocolTestData("StreamItemMessage_ItemFirst", new StreamItemMessage("1a", "foo"), "{ \"item\": \"foo\", \"invocationId\": \"1a\", \"type\":2 }")
+            new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, true, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"),
+            new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgumentFirst", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), false, true, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"),
+            new JsonProtocolTestData("InvocationMessage_IntegerArrayArgumentFirst", new InvocationMessage("Method", new object[] { 1, 2 }), false, true, "{ \"arguments\": [1,2], \"type\":1, \"target\": \"Method\" }"),
+            new JsonProtocolTestData("StreamInvocationMessage_IntegerArrayArgumentFirst", new StreamInvocationMessage("3", "Method", new object[] { 1, 2 }), false, true, "{ \"type\":4, \"arguments\": [1,2], \"target\": \"Method\", \"invocationId\": \"3\" }"),
+            new JsonProtocolTestData("CompletionMessage_ResultFirst", new CompletionMessage("15", null, 10, hasResult: true), false, true, "{ \"type\":3, \"result\": 10, \"invocationId\": \"15\" }"),
+            new JsonProtocolTestData("StreamItemMessage_ItemFirst", new StreamItemMessage("1a", "foo"), false, true, "{ \"item\": \"foo\", \"invocationId\": \"1a\", \"type\":2 }")
 
 
         }.ToDictionary(t => t.Name);
         }.ToDictionary(t => t.Name);
 
 
@@ -99,7 +101,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
             var writer = MemoryBufferWriter.Get();
             var writer = MemoryBufferWriter.Get();
             try
             try
             {
             {
-                JsonHubProtocol.WriteMessage(testData.Message, writer);
+                var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
+                protocol.WriteMessage(testData.Message, writer);
                 var json = Encoding.UTF8.GetString(writer.ToArray());
                 var json = Encoding.UTF8.GetString(writer.ToArray());
 
 
                 Assert.Equal(expectedOutput, json);
                 Assert.Equal(expectedOutput, json);
@@ -120,7 +123,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
 
             var binder = new TestBinder(testData.Message);
             var binder = new TestBinder(testData.Message);
             var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
             var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
-            JsonHubProtocol.TryParseMessage(ref data, binder, out var message);
+            var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
+            protocol.TryParseMessage(ref data, binder, out var message);
 
 
             Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
             Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
         }
         }
@@ -179,7 +183,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
 
             var binder = new TestBinder(testData.Message);
             var binder = new TestBinder(testData.Message);
             var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
             var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
-            JsonHubProtocol.TryParseMessage(ref data, binder, out var message);
+            var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
+            protocol.TryParseMessage(ref data, binder, out var message);
 
 
             Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
             Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
         }
         }
@@ -205,7 +210,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
         [InlineData("{\"type\":4,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[ \"abc\", \"xyz\"]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
         [InlineData("{\"type\":4,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[ \"abc\", \"xyz\"]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
         [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]}", "Invocation provides 3 argument(s) but target expects 2.")]
         [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]}", "Invocation provides 3 argument(s) but target expects 2.")]
         [InlineData("{\"type\":1,\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]},\"invocationId\":\"42\",\"target\":\"foo\"", "Invocation provides 3 argument(s) but target expects 2.")]
         [InlineData("{\"type\":1,\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]},\"invocationId\":\"42\",\"target\":\"foo\"", "Invocation provides 3 argument(s) but target expects 2.")]
-        [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[1]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
+        // Both of these should be fixed by https://github.com/dotnet/corefx/issues/36901
+        // [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[1]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
         // [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
         // [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
         public void ArgumentBindingErrors(string input, string expectedMessage)
         public void ArgumentBindingErrors(string input, string expectedMessage)
         {
         {
@@ -277,12 +283,16 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
             public string Name { get; }
             public string Name { get; }
             public HubMessage Message { get; }
             public HubMessage Message { get; }
             public string Json { get; }
             public string Json { get; }
+            public bool UseCamelCase { get; }
+            public bool IgnoreNullValues { get; }
 
 
-            public JsonProtocolTestData(string name, HubMessage message, string json)
+            public JsonProtocolTestData(string name, HubMessage message, bool useCamelCase, bool ignoreNullValues, string json)
             {
             {
                 Name = name;
                 Name = name;
                 Message = message;
                 Message = message;
                 Json = json;
                 Json = json;
+                UseCamelCase = useCamelCase;
+                IgnoreNullValues = ignoreNullValues;
             }
             }
 
 
             public override string ToString() => Name;
             public override string ToString() => Name;

+ 42 - 50
src/SignalR/common/SignalR.Common/test/Internal/Protocol/NewtonsoftJsonHubProtocolTests.cs

@@ -22,6 +22,20 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
     {
     {
         protected override IHubProtocol JsonHubProtocol => new NewtonsoftJsonHubProtocol();
         protected override IHubProtocol JsonHubProtocol => new NewtonsoftJsonHubProtocol();
 
 
+        protected override IHubProtocol GetProtocolWithOptions(bool useCamelCase, bool ignoreNullValues)
+        {
+            var protocolOptions = new NewtonsoftJsonHubProtocolOptions
+            {
+                PayloadSerializerSettings = new JsonSerializerSettings()
+                {
+                    NullValueHandling = ignoreNullValues ? NullValueHandling.Ignore : NullValueHandling.Include,
+                    ContractResolver = useCamelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
+                }
+            };
+
+            return new NewtonsoftJsonHubProtocol(Options.Create(protocolOptions));
+        }
+
         [Theory]
         [Theory]
         [InlineData("", "Unexpected end when reading JSON.")]
         [InlineData("", "Unexpected end when reading JSON.")]
         [InlineData("42", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")]
         [InlineData("42", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")]
@@ -44,16 +58,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
 
             var expectedOutput = Frame(testData.Json);
             var expectedOutput = Frame(testData.Json);
 
 
-            var protocolOptions = new NewtonsoftJsonHubProtocolOptions
-            {
-                PayloadSerializerSettings = new JsonSerializerSettings()
-                {
-                    NullValueHandling = testData.NullValueHandling,
-                    ContractResolver = testData.CamelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
-                }
-            };
-
-            var protocol = new NewtonsoftJsonHubProtocol(Options.Create(protocolOptions));
+            var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
 
 
             var writer = MemoryBufferWriter.Get();
             var writer = MemoryBufferWriter.Get();
             try
             try
@@ -69,49 +74,36 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
             }
             }
         }
         }
 
 
-        public static IDictionary<string, NewtonsoftJsonProtocolTestData> CustomProtocolTestData => new[]
+        public static IDictionary<string, JsonProtocolTestData> CustomProtocolTestData => new[]
         {
         {
-            new NewtonsoftJsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
-            new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":2.0}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":2.0}"),
-            new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
-            new NewtonsoftJsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
-            new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnore", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnoreAndNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueInclude", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIgnore", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIncludeAndNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueInclude", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasTestHeadersAndCustomItemResult", AddHeaders(TestHeaders, CompletionMessage.WithResult("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasErrorAndCamelCase", CompletionMessage.Empty("123"), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\"}"),
-            new NewtonsoftJsonProtocolTestData("CompletionMessage_HasErrorAndHeadersAndCamelCase", AddHeaders(TestHeaders, CompletionMessage.Empty("123")), true, NullValueHandling.Ignore, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\"}"),
-            new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), true, NullValueHandling.Include, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
-            new NewtonsoftJsonProtocolTestData("CloseMessage_HasErrorWithCamelCase", new CloseMessage("Error!"), true, NullValueHandling.Ignore, "{\"type\":7,\"error\":\"Error!\"}"),
+            new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
+            new JsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":2.0}"),
+            new JsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":2.0}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
+            new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, true, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
+            new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, false, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, false, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnore", new StreamItemMessage("123", new CustomObject()), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnoreAndNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, false, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueInclude", new StreamItemMessage("123", new CustomObject()), true, false, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), true, false, "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIgnore", CompletionMessage.WithResult("123", new CustomObject()), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIncludeAndNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, false, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueInclude", CompletionMessage.WithResult("123", new CustomObject()), true, false, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("CompletionMessage_HasTestHeadersAndCustomItemResult", AddHeaders(TestHeaders, CompletionMessage.WithResult("123", new CustomObject())), true, false, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
+            new JsonProtocolTestData("CompletionMessage_HasErrorAndCamelCase", CompletionMessage.Empty("123"), true, true, "{\"type\":3,\"invocationId\":\"123\"}"),
+            new JsonProtocolTestData("CompletionMessage_HasErrorAndHeadersAndCamelCase", AddHeaders(TestHeaders, CompletionMessage.Empty("123")), true, true, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\"}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, false, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, false, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
+            new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), true, false, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
         }.ToDictionary(t => t.Name);
         }.ToDictionary(t => t.Name);
 
 
         public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name });
         public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name });
-
-        public class NewtonsoftJsonProtocolTestData : JsonProtocolTestData
-        {
-            public NewtonsoftJsonProtocolTestData(string name, HubMessage message, bool camelCase, NullValueHandling nullValueHandling, string json) : base(name, message, json)
-            {
-                CamelCase = camelCase;
-                NullValueHandling = nullValueHandling;
-            }
-
-            public bool CamelCase { get; }
-            public NullValueHandling NullValueHandling { get; }
-        }
     }
     }
 }
 }

+ 1 - 1
src/SignalR/common/testassets/Tests.Utils/HubConnectionContextUtils.cs

@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
         {
         {
             return new HubConnectionContext(connection, TimeSpan.FromSeconds(15), NullLoggerFactory.Instance)
             return new HubConnectionContext(connection, TimeSpan.FromSeconds(15), NullLoggerFactory.Instance)
             {
             {
-                Protocol = protocol ?? new NewtonsoftJsonHubProtocol(),
+                Protocol = protocol ?? new JsonHubProtocol(),
                 UserIdentifier = userIdentifier,
                 UserIdentifier = userIdentifier,
             };
             };
         }
         }

+ 1 - 1
src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.csproj

@@ -7,7 +7,7 @@
     <Compile Include="Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs" />
     <Compile Include="Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs" />
     <Reference Include="Microsoft.AspNetCore.Authorization"  />
     <Reference Include="Microsoft.AspNetCore.Authorization"  />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common"  />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common"  />
-    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson"  />
+    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json"  />
     <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions"  />
     <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions"  />
     <Reference Include="Microsoft.Extensions.Logging.Abstractions"  />
     <Reference Include="Microsoft.Extensions.Logging.Abstractions"  />
     <Reference Include="System.Threading.Channels"  />
     <Reference Include="System.Threading.Channels"  />

+ 2 - 2
src/SignalR/server/Core/src/Microsoft.AspNetCore.SignalR.Core.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
 
   <PropertyGroup>
   <PropertyGroup>
     <Description>Real-time communication framework for ASP.NET Core.</Description>
     <Description>Real-time communication framework for ASP.NET Core.</Description>
@@ -17,7 +17,7 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Authorization" />
     <Reference Include="Microsoft.AspNetCore.Authorization" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
-    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" />
+    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
     <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
     <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
     <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
     <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
     <Reference Include="System.Threading.Channels" />
     <Reference Include="System.Threading.Channels" />

+ 1 - 1
src/SignalR/server/Core/src/SignalRDependencyInjectionExtensions.cs

@@ -32,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
             services.AddAuthorization();
             services.AddAuthorization();
 
 
             var builder = new SignalRServerBuilder(services);
             var builder = new SignalRServerBuilder(services);
-            builder.AddNewtonsoftJsonProtocol();
+            builder.AddJsonProtocol();
             return builder;
             return builder;
         }
         }
     }
     }

+ 13 - 13
src/SignalR/server/SignalR/test/HubConnectionHandlerTestUtils/Hubs.cs

@@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.User(userId).SendAsync("Send", message);
             return Clients.User(userId).SendAsync("Send", message);
         }
         }
 
 
-        public Task SendToMultipleUsers(List<string> userIds, string message)
+        public Task SendToMultipleUsers(IReadOnlyList<string> userIds, string message)
         {
         {
             return Clients.Users(userIds).SendAsync("Send", message);
             return Clients.Users(userIds).SendAsync("Send", message);
         }
         }
@@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.Client(connectionId).SendAsync("Send", message);
             return Clients.Client(connectionId).SendAsync("Send", message);
         }
         }
 
 
-        public Task SendToMultipleClients(string message, List<string> connectionIds)
+        public Task SendToMultipleClients(string message, IReadOnlyList<string> connectionIds)
         {
         {
             return Clients.Clients(connectionIds).SendAsync("Send", message);
             return Clients.Clients(connectionIds).SendAsync("Send", message);
         }
         }
@@ -48,12 +48,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.Group(groupName).SendAsync("Send", message);
             return Clients.Group(groupName).SendAsync("Send", message);
         }
         }
 
 
-        public Task GroupExceptSendMethod(string groupName, string message, List<string> excludedConnectionIds)
+        public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList<string> excludedConnectionIds)
         {
         {
             return Clients.GroupExcept(groupName, excludedConnectionIds).SendAsync("Send", message);
             return Clients.GroupExcept(groupName, excludedConnectionIds).SendAsync("Send", message);
         }
         }
 
 
-        public Task SendToMultipleGroups(string message, List<string> groupNames)
+        public Task SendToMultipleGroups(string message, IReadOnlyList<string> groupNames)
         {
         {
             return Clients.Groups(groupNames).SendAsync("Send", message);
             return Clients.Groups(groupNames).SendAsync("Send", message);
         }
         }
@@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
         {
         {
         }
         }
 
 
-        public Task SendToAllExcept(string message, List<string> excludedConnectionIds)
+        public Task SendToAllExcept(string message, IReadOnlyList<string> excludedConnectionIds)
         {
         {
             return Clients.AllExcept(excludedConnectionIds).SendAsync("Send", message);
             return Clients.AllExcept(excludedConnectionIds).SendAsync("Send", message);
         }
         }
@@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.Client(connectionId).Send(message);
             return Clients.Client(connectionId).Send(message);
         }
         }
 
 
-        public Task SendToMultipleClients(string message, List<string> connectionIds)
+        public Task SendToMultipleClients(string message, IReadOnlyList<string> connectionIds)
         {
         {
             return Clients.Clients(connectionIds).Send(message);
             return Clients.Clients(connectionIds).Send(message);
         }
         }
@@ -328,7 +328,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.Group(groupName).Send(message);
             return Clients.Group(groupName).Send(message);
         }
         }
 
 
-        public Task GroupExceptSendMethod(string groupName, string message, List<string> excludedConnectionIds)
+        public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList<string> excludedConnectionIds)
         {
         {
             return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message);
             return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message);
         }
         }
@@ -338,7 +338,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.OthersInGroup(groupName).Send(message);
             return Clients.OthersInGroup(groupName).Send(message);
         }
         }
 
 
-        public Task SendToMultipleGroups(string message, List<string> groupNames)
+        public Task SendToMultipleGroups(string message, IReadOnlyList<string> groupNames)
         {
         {
             return Clients.Groups(groupNames).Send(message);
             return Clients.Groups(groupNames).Send(message);
         }
         }
@@ -348,7 +348,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.All.Broadcast(message);
             return Clients.All.Broadcast(message);
         }
         }
 
 
-        public Task SendToAllExcept(string message, List<string> excludedConnectionIds)
+        public Task SendToAllExcept(string message, IReadOnlyList<string> excludedConnectionIds)
         {
         {
             return Clients.AllExcept(excludedConnectionIds).Send(message);
             return Clients.AllExcept(excludedConnectionIds).Send(message);
         }
         }
@@ -393,7 +393,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.Client(connectionId).Send(message);
             return Clients.Client(connectionId).Send(message);
         }
         }
 
 
-        public Task SendToMultipleClients(string message, List<string> connectionIds)
+        public Task SendToMultipleClients(string message, IReadOnlyList<string> connectionIds)
         {
         {
             return Clients.Clients(connectionIds).Send(message);
             return Clients.Clients(connectionIds).Send(message);
         }
         }
@@ -414,12 +414,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.Group(groupName).Send(message);
             return Clients.Group(groupName).Send(message);
         }
         }
 
 
-        public Task GroupExceptSendMethod(string groupName, string message, List<string> excludedConnectionIds)
+        public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList<string> excludedConnectionIds)
         {
         {
             return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message);
             return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message);
         }
         }
 
 
-        public Task SendToMultipleGroups(string message, List<string> groupNames)
+        public Task SendToMultipleGroups(string message, IReadOnlyList<string> groupNames)
         {
         {
             return Clients.Groups(groupNames).Send(message);
             return Clients.Groups(groupNames).Send(message);
         }
         }
@@ -434,7 +434,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
             return Clients.All.Broadcast(message);
             return Clients.All.Broadcast(message);
         }
         }
 
 
-        public Task SendToAllExcept(string message, List<string> excludedConnectionIds)
+        public Task SendToAllExcept(string message, IReadOnlyList<string> excludedConnectionIds)
         {
         {
             return Clients.AllExcept(excludedConnectionIds).Send(message);
             return Clients.AllExcept(excludedConnectionIds).Send(message);
         }
         }

+ 2 - 1
src/SignalR/server/Specification.Tests/src/Microsoft.AspNetCore.SignalR.Specification.Tests.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
 
   <PropertyGroup>
   <PropertyGroup>
     <Description>Tests for users to verify their own implementations of SignalR types</Description>
     <Description>Tests for users to verify their own implementations of SignalR types</Description>
@@ -19,6 +19,7 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Core" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Core" />
+    <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" />
     <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" />
     <Reference Include="xunit.assert" />
     <Reference Include="xunit.assert" />

+ 1 - 1
src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <Compile Include="$(SignalRSharedSourceRoot)JsonUtils.cs" Link="Internal\JsonUtils.cs" />
+    <Compile Include="$(SignalRSharedSourceRoot)SystemTextJsonExtensions.cs" Link="Internal\SystemTextJsonExtensions.cs" />
     <Compile Include="$(SignalRSharedSourceRoot)MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
     <Compile Include="$(SignalRSharedSourceRoot)MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
   </ItemGroup>
   </ItemGroup>