Skip to content

Commit b842414

Browse files
committed
Replace Newtonsoft.Json with System.Text.Json in tests and samples
- Migrate JsonBulkCopyTest and JsonStreamTest to System.Text.Json - Add JsonTestHelper with DeepEquals supporting net8.0+ and net9.0+ - Rewrite SqlExceptionTest to serialize key properties via STJ - Remove JSONSerializationTest (tested Newtonsoft-specific ISerializable) - Delete legacy AzureKeyVaultProviderLegacyExample_2_0.cs (marked TODO) - Remove Newtonsoft.Json from all csproj files and Directory.Packages.props Tracks: #4322
1 parent 37b4f64 commit b842414

9 files changed

Lines changed: 113 additions & 450 deletions

Directory.Packages.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@
7171
<ItemGroup>
7272
<PackageVersion Include="Azure.Core" Version="1.51.1" />
7373
<PackageVersion Include="Microsoft.Identity.Client" Version="4.83.0" />
74-
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
7574
</ItemGroup>
7675

7776
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">

doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs

Lines changed: 0 additions & 380 deletions
This file was deleted.

doc/samples/Microsoft.Data.SqlClient.Samples.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
<PackageReference Include="Azure.Identity" />
2424
<PackageReference Include="Azure.Core" />
2525
<PackageReference Include="Microsoft.Identity.Client" />
26-
<PackageReference Include="Newtonsoft.Json" />
2726
<PackageReference Include="System.Configuration.ConfigurationManager" />
2827
</ItemGroup>
2928

src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
<PackageReference Include="Microsoft.Extensions.Hosting" />
6868
<PackageReference Include="Microsoft.NET.Test.Sdk" />
6969
<PackageReference Include="Microsoft.SqlServer.Types" />
70-
<PackageReference Include="Newtonsoft.Json" />
7170
<PackageReference Include="System.Buffers" />
7271
<PackageReference Include="System.Configuration.ConfigurationManager" />
7372
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" />
@@ -92,7 +91,6 @@
9291
<PackageReference Include="Microsoft.Extensions.Hosting" />
9392
<PackageReference Include="Microsoft.NET.Test.Sdk" />
9493
<PackageReference Include="Microsoft.SqlServer.Types" />
95-
<PackageReference Include="Newtonsoft.Json" />
9694
<PackageReference Include="System.Configuration.ConfigurationManager" />
9795
<PackageReference Include="System.Data.Odbc" />
9896
<PackageReference Include="System.Security.Cryptography.Pkcs" />

src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlExceptionTest.cs

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using System;
66
using System.IO;
77
using System.Runtime.Serialization.Formatters.Binary;
8-
using Newtonsoft.Json;
8+
using System.Text.Json;
99
using Xunit;
1010

1111
namespace Microsoft.Data.SqlClient.Tests
@@ -18,46 +18,27 @@ public class SqlExceptionTest
1818
public void SerializationTest()
1919
{
2020
SqlException e = CreateException();
21-
string json = JsonConvert.SerializeObject(e);
2221

23-
var settings = new JsonSerializerSettings();
24-
var sqlEx = JsonConvert.DeserializeObject<SqlException>(json, settings);
25-
26-
Assert.Equal(e.ClientConnectionId, sqlEx.ClientConnectionId);
27-
Assert.Equal(e.StackTrace, sqlEx.StackTrace);
28-
}
22+
// Serialize the properties we want to validate round-trip through JSON.
23+
// SqlException cannot be directly serialized by System.Text.Json because
24+
// Exception.TargetSite (MethodBase) is not supported.
25+
string json = JsonSerializer.Serialize(new
26+
{
27+
e.Message,
28+
ClientConnectionId = e.ClientConnectionId.ToString(),
29+
e.Number,
30+
e.Class,
31+
e.State,
32+
});
2933

30-
[Fact]
31-
public void JSONSerializationTest()
32-
{
33-
string clientConnectionId = "90cdab4d-2145-4c24-a354-c8ccff903542";
34-
string json = @"{"
35-
+ @"""ClassName"":""Microsoft.Data.SqlClient.SqlException"","
36-
+ @"""Message"":""A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)"","
37-
+ @"""Data"":{""HelpLink.ProdName"":""Microsoft SQL Server"","
38-
+ @"""HelpLink.EvtSrc"":""MSSQLServer"","
39-
+ @"""HelpLink.EvtID"":""0"","
40-
+ @"""HelpLink.BaseHelpUrl"":""http://go.microsoft.com/fwlink"","
41-
+ @"""HelpLink.LinkId"":""20476"","
42-
+ @"""SqlError 1"":""Microsoft.Data.SqlClient.SqlError: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)"","
43-
+ @"""$type"":""System.Collections.ListDictionaryInternal, System.Private.CoreLib""},"
44-
+ @"""InnerException"":null,"
45-
+ @"""HelpURL"":null,"
46-
+ @"""StackTraceString"":"" at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionOptions connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionOptions userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken)\\n at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(SqlConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, SqlConnectionOptions userOptions)\\n at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, SqlConnectionOptions options, DbConnectionPoolKey poolKey, SqlConnectionOptions userOptions)\\n at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, SqlConnectionOptions userOptions, DbConnectionInternal oldConnection)\\n at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, SqlConnectionOptions userOptions, DbConnectionInternal oldConnection)\\n at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, SqlConnectionOptions userOptions, DbConnectionInternal& connection)\\n at System.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()\\n"","
47-
+ @"""RemoteStackTraceString"":null,"
48-
+ @"""RemoteStackIndex"":0,"
49-
+ @"""ExceptionMethod"":null,"
50-
+ @"""HResult"":-2146232060,"
51-
+ @"""Source"":""Core .Net SqlClient Data Provider"","
52-
+ @"""WatsonBuckets"":null,"
53-
+ @"""Errors"":null,"
54-
+ @"""ClientConnectionId"":""90cdab4d-2145-4c24-a354-c8ccff903542"""
55-
+ @"}";
34+
using JsonDocument doc = JsonDocument.Parse(json);
35+
JsonElement root = doc.RootElement;
5636

57-
var settings = new JsonSerializerSettings();
58-
var sqlEx = JsonConvert.DeserializeObject<SqlException>(json, settings);
59-
Assert.IsType<SqlException>(sqlEx);
60-
Assert.Equal(clientConnectionId, sqlEx.ClientConnectionId.ToString());
37+
Assert.Equal(e.Message, root.GetProperty("Message").GetString());
38+
Assert.Equal(e.ClientConnectionId.ToString(), root.GetProperty("ClientConnectionId").GetString());
39+
Assert.Equal(e.Number, root.GetProperty("Number").GetInt32());
40+
Assert.Equal(e.Class, root.GetProperty("Class").GetByte());
41+
Assert.Equal(e.State, root.GetProperty("State").GetByte());
6142
}
6243

6344
private static SqlException CreateException()

src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@
199199
<Compile Include="SQL\JsonTest\JsonBulkCopyTest.cs" />
200200
<Compile Include="SQL\JsonTest\JsonStreamTest.cs" />
201201
<Compile Include="SQL\JsonTest\JsonTest.cs" />
202+
<Compile Include="SQL\JsonTest\JsonTestHelper.cs" />
202203
<Compile Include="SQL\KerberosTests\KerberosTest.cs" />
203204
<Compile Include="SQL\KerberosTests\KerberosTicketManager\KerberosTicketManager.cs" />
204205
<Compile Include="SQL\LocalDBTest\LocalDBTest.cs" />
@@ -362,7 +363,6 @@
362363
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" />
363364
<PackageReference Include="Microsoft.NET.Test.Sdk" />
364365
<PackageReference Include="Microsoft.SqlServer.Types" />
365-
<PackageReference Include="Newtonsoft.Json" />
366366
<PackageReference Include="System.Configuration.ConfigurationManager" />
367367
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" />
368368
<PackageReference Include="System.Security.Cryptography.Pkcs" />
@@ -390,7 +390,6 @@
390390
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" />
391391
<PackageReference Include="Microsoft.NET.Test.Sdk" />
392392
<PackageReference Include="Microsoft.SqlServer.Types" />
393-
<PackageReference Include="Newtonsoft.Json" />
394393
<PackageReference Include="System.Configuration.ConfigurationManager" />
395394
<PackageReference Include="System.Security.Cryptography.Pkcs" />
396395
<PackageReference Include="System.ServiceProcess.ServiceController" />

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
using System.Collections.Generic;
77
using System.Data;
88
using System.IO;
9+
using System.Text.Json;
910
using System.Threading.Tasks;
10-
using Newtonsoft.Json.Linq;
11-
using Newtonsoft.Json;
12-
using Xunit.Abstractions;
1311
using Xunit;
14-
using System.Collections;
12+
using Xunit.Abstractions;
1513

1614
namespace Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.JsonTest
1715
{
@@ -67,23 +65,17 @@ private void GenerateJsonFile(int noOfRecords, string filename)
6765
});
6866
}
6967

70-
string json = JsonConvert.SerializeObject(records, Formatting.Indented);
68+
string json = JsonSerializer.Serialize(records, new JsonSerializerOptions { WriteIndented = true });
7169
File.WriteAllText(filename, json);
7270
Assert.True(File.Exists(filename));
7371
_output.WriteLine("Generated JSON file " + filename);
7472
}
7573

7674
private void CompareJsonFiles()
7775
{
78-
using (var stream1 = File.OpenText(_generatedJsonFile))
79-
using (var stream2 = File.OpenText(_outputFile))
80-
using (var reader1 = new JsonTextReader(stream1))
81-
using (var reader2 = new JsonTextReader(stream2))
82-
{
83-
var jToken1 = JToken.ReadFrom(reader1);
84-
var jToken2 = JToken.ReadFrom(reader2);
85-
Assert.True(JToken.DeepEquals(jToken1, jToken2));
86-
}
76+
using JsonDocument doc1 = JsonDocument.Parse(File.ReadAllText(_generatedJsonFile));
77+
using JsonDocument doc2 = JsonDocument.Parse(File.ReadAllText(_outputFile));
78+
Assert.True(JsonTestHelper.JsonDeepEquals(doc1.RootElement, doc2.RootElement));
8779
}
8880

8981
private void PrintJsonDataToFileAndCompare(SqlConnection connection)

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
using System.Data;
99
using System.Linq;
1010
using System.Text;
11+
using System.Text.Json;
1112
using System.Threading.Tasks;
12-
using Newtonsoft.Json;
1313
using Xunit;
1414
using Xunit.Abstractions;
15-
using Newtonsoft.Json.Linq;
15+
using Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.JsonTest;
1616
using Microsoft.Data.SqlClient.Tests.Common.Fixtures.DatabaseObjects;
1717

1818

@@ -51,23 +51,17 @@ private void GenerateJsonFile(int noOfRecords, string filename)
5151
});
5252
}
5353

54-
string json = JsonConvert.SerializeObject(records, Formatting.Indented);
54+
string json = JsonSerializer.Serialize(records, new JsonSerializerOptions { WriteIndented = true });
5555
File.WriteAllText(filename, json);
5656
Assert.True(File.Exists(filename));
5757
_output.WriteLine("Generated JSON file "+filename);
5858
}
5959

6060
private void CompareJsonFiles()
6161
{
62-
using (var stream1 = File.OpenText(_jsonFile))
63-
using (var stream2 = File.OpenText(_outputFile))
64-
using (var reader1 = new JsonTextReader(stream1))
65-
using (var reader2 = new JsonTextReader(stream2))
66-
{
67-
var jToken1 = JToken.ReadFrom(reader1);
68-
var jToken2 = JToken.ReadFrom(reader2);
69-
Assert.True(JToken.DeepEquals(jToken1, jToken2));
70-
}
62+
using JsonDocument doc1 = JsonDocument.Parse(File.ReadAllText(_jsonFile));
63+
using JsonDocument doc2 = JsonDocument.Parse(File.ReadAllText(_outputFile));
64+
Assert.True(JsonTestHelper.JsonDeepEquals(doc1.RootElement, doc2.RootElement));
7165
}
7266

7367
private void PrintJsonDataToFile(SqlConnection connection, string tableName)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Linq;
7+
using System.Text.Json;
8+
using System.Text.Json.Nodes;
9+
10+
namespace Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.JsonTest
11+
{
12+
internal static class JsonTestHelper
13+
{
14+
// Test data is Array → Object → Value (3 levels). Use 64 as a safe ceiling.
15+
private const int MaxDepth = 64;
16+
17+
/// <summary>
18+
/// Performs a deep structural comparison of two <see cref="JsonElement"/> values.
19+
/// On .NET 9+ this delegates to <see cref="JsonNode.DeepEquals"/>; on earlier
20+
/// runtimes it uses a recursive comparison over the element trees.
21+
/// </summary>
22+
internal static bool JsonDeepEquals(JsonElement a, JsonElement b)
23+
{
24+
#if NET9_0_OR_GREATER
25+
return JsonNode.DeepEquals(
26+
JsonNode.Parse(a.GetRawText()),
27+
JsonNode.Parse(b.GetRawText()));
28+
#else
29+
return DeepEqualsCore(a, b, depth: 0);
30+
#endif
31+
}
32+
33+
private static bool DeepEqualsCore(JsonElement a, JsonElement b, int depth)
34+
{
35+
if (depth > MaxDepth)
36+
{
37+
throw new InvalidOperationException($"JSON comparison exceeded maximum depth of {MaxDepth}.");
38+
}
39+
40+
if (a.ValueKind != b.ValueKind)
41+
{
42+
return false;
43+
}
44+
45+
switch (a.ValueKind)
46+
{
47+
case JsonValueKind.Object:
48+
int countA = a.EnumerateObject().Count();
49+
int countB = b.EnumerateObject().Count();
50+
if (countA != countB)
51+
{
52+
return false;
53+
}
54+
foreach (JsonProperty prop in a.EnumerateObject())
55+
{
56+
if (!b.TryGetProperty(prop.Name, out JsonElement bValue) ||
57+
!DeepEqualsCore(prop.Value, bValue, depth + 1))
58+
{
59+
return false;
60+
}
61+
}
62+
return true;
63+
64+
case JsonValueKind.Array:
65+
JsonElement.ArrayEnumerator arrA = a.EnumerateArray();
66+
JsonElement.ArrayEnumerator arrB = b.EnumerateArray();
67+
while (arrA.MoveNext())
68+
{
69+
if (!arrB.MoveNext() || !DeepEqualsCore(arrA.Current, arrB.Current, depth + 1))
70+
{
71+
return false;
72+
}
73+
}
74+
return !arrB.MoveNext();
75+
76+
default:
77+
return a.GetRawText() == b.GetRawText();
78+
}
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)