Skip to content

Commit df09e3a

Browse files
authored
Replace Newtonsoft.Json with System.Text.Json in tests and samples (#4323)
1 parent 47f4e52 commit df09e3a

9 files changed

Lines changed: 119 additions & 452 deletions

Directory.Packages.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@
9696
<ItemGroup>
9797
<PackageVersion Include="Azure.Core" Version="1.51.1" />
9898
<PackageVersion Include="Microsoft.Identity.Client" Version="4.83.0" />
99-
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
10099
</ItemGroup>
101100

102101
<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 & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6-
using System.IO;
7-
using System.Runtime.Serialization.Formatters.Binary;
8-
using Newtonsoft.Json;
6+
using System.Text.Json;
97
using Xunit;
108

119
namespace Microsoft.Data.SqlClient.Tests
@@ -18,46 +16,27 @@ public class SqlExceptionTest
1816
public void SerializationTest()
1917
{
2018
SqlException e = CreateException();
21-
string json = JsonConvert.SerializeObject(e);
2219

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-
}
20+
// Serialize the properties we want to validate round-trip through JSON.
21+
// SqlException cannot be directly serialized by System.Text.Json because
22+
// Exception.TargetSite (MethodBase) is not supported.
23+
string json = JsonSerializer.Serialize(new
24+
{
25+
e.Message,
26+
ClientConnectionId = e.ClientConnectionId.ToString(),
27+
e.Number,
28+
e.Class,
29+
e.State,
30+
});
2931

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-
+ @"}";
32+
using JsonDocument doc = JsonDocument.Parse(json);
33+
JsonElement root = doc.RootElement;
5634

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());
35+
Assert.Equal(e.Message, root.GetProperty("Message").GetString());
36+
Assert.Equal(e.ClientConnectionId.ToString(), root.GetProperty("ClientConnectionId").GetString());
37+
Assert.Equal(e.Number, root.GetProperty("Number").GetInt32());
38+
Assert.Equal(e.Class, root.GetProperty("Class").GetByte());
39+
Assert.Equal(e.State, root.GetProperty("State").GetByte());
6140
}
6241

6342
private static SqlException CreateException()

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@
9696
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" />
9797
<PackageReference Include="Microsoft.NET.Test.Sdk" />
9898
<PackageReference Include="Microsoft.SqlServer.Types" />
99-
<PackageReference Include="Newtonsoft.Json" />
10099
<PackageReference Include="System.Configuration.ConfigurationManager" />
101100
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" />
102101
<PackageReference Include="System.Security.Cryptography.Pkcs" />
@@ -124,7 +123,6 @@
124123
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" />
125124
<PackageReference Include="Microsoft.NET.Test.Sdk" />
126125
<PackageReference Include="Microsoft.SqlServer.Types" />
127-
<PackageReference Include="Newtonsoft.Json" />
128126
<PackageReference Include="System.Configuration.ConfigurationManager" />
129127
<PackageReference Include="System.Security.Cryptography.Pkcs" />
130128
<PackageReference Include="System.ServiceProcess.ServiceController" />

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

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

1615
namespace Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.JsonTest
1716
{
@@ -68,23 +67,17 @@ private void GenerateJsonFile(int noOfRecords, string filename)
6867
});
6968
}
7069

71-
string json = JsonConvert.SerializeObject(records, Formatting.Indented);
70+
string json = JsonSerializer.Serialize(records, new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
7271
File.WriteAllText(filename, json);
7372
Assert.True(File.Exists(filename));
7473
_output.WriteLine("Generated JSON file " + filename);
7574
}
7675

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

9083
private void PrintJsonDataToFileAndCompare(SqlConnection connection)

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

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
using System.Data;
99
using System.Linq;
1010
using System.Text;
11+
using System.Text.Encodings.Web;
12+
using System.Text.Json;
1113
using System.Threading.Tasks;
12-
using Newtonsoft.Json;
1314
using Xunit;
1415
using Xunit.Abstractions;
15-
using Newtonsoft.Json.Linq;
16+
using Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.JsonTest;
1617
using Microsoft.Data.SqlClient.Tests.Common.Fixtures.DatabaseObjects;
1718

1819

@@ -52,23 +53,17 @@ private void GenerateJsonFile(int noOfRecords, string filename)
5253
});
5354
}
5455

55-
string json = JsonConvert.SerializeObject(records, Formatting.Indented);
56+
string json = JsonSerializer.Serialize(records, new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
5657
File.WriteAllText(filename, json);
5758
Assert.True(File.Exists(filename));
5859
_output.WriteLine("Generated JSON file "+filename);
5960
}
6061

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

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

0 commit comments

Comments
 (0)