diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
index 6bfc65a005..35dae71952 100644
--- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
+++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
@@ -60,7 +60,8 @@ public class CosmosClientOptions
///
private int gatewayModeMaxConnectionLimit;
private CosmosSerializationOptions serializerOptions;
- private CosmosSerializer serializerInternal;
+ private CosmosSerializer serializerInternal;
+ private System.Text.Json.JsonSerializerOptions stjSerializerOptions;
private ConnectionMode connectionMode;
private Protocol connectionProtocol;
@@ -392,6 +393,44 @@ public ConnectionMode ConnectionMode
///
///
public bool? EnableContentResponseOnWrite { get; set; }
+
+ ///
+ /// Sets the for the System.Text.Json serializer.
+ /// Note that if this option is provided, then the SDK will use the System.Text.Json as the default serializer and set
+ /// the serializer options as the constructor args.
+ ///
+ ///
+ /// An example on how to configure the System.Text.Json serializer options to ignore null values
+ ///
+ ///
+ ///
+ ///
+ public System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOptions
+ {
+ get => this.stjSerializerOptions;
+ set
+ {
+ if (this.Serializer != null || this.SerializerOptions != null)
+ {
+ throw new ArgumentException(
+ $"{nameof(this.UseSystemTextJsonSerializerWithOptions)} is not compatible with {nameof(this.Serializer)} or {nameof(this.SerializerOptions)}. Only one can be set. ");
+ }
+
+ this.stjSerializerOptions = value;
+ this.serializerInternal = new CosmosSystemTextJsonSerializer(
+ this.stjSerializerOptions);
+ }
+ }
///
/// Gets or sets the advanced replica selection flag. The advanced replica selection logic keeps track of the replica connection
@@ -543,10 +582,10 @@ public CosmosSerializationOptions SerializerOptions
get => this.serializerOptions;
set
{
- if (this.Serializer != null)
+ if (this.Serializer != null || this.UseSystemTextJsonSerializerWithOptions != null)
{
throw new ArgumentException(
- $"{nameof(this.SerializerOptions)} is not compatible with {nameof(this.Serializer)}. Only one can be set. ");
+ $"{nameof(this.SerializerOptions)} is not compatible with {nameof(this.Serializer)} or {nameof(this.UseSystemTextJsonSerializerWithOptions)}. Only one can be set. ");
}
this.serializerOptions = value;
@@ -578,10 +617,10 @@ public CosmosSerializer Serializer
get => this.serializerInternal;
set
{
- if (this.SerializerOptions != null)
+ if (this.SerializerOptions != null || this.UseSystemTextJsonSerializerWithOptions != null)
{
throw new ArgumentException(
- $"{nameof(this.Serializer)} is not compatible with {nameof(this.SerializerOptions)}. Only one can be set. ");
+ $"{nameof(this.Serializer)} is not compatible with {nameof(this.SerializerOptions)} or {nameof(this.UseSystemTextJsonSerializerWithOptions)}. Only one can be set. ");
}
this.serializerInternal = value;
diff --git a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
index 43232aa422..c614068fae 100644
--- a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
+++ b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
@@ -646,7 +646,22 @@ public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrit
{
this.clientOptions.EnableContentResponseOnWrite = contentResponseOnWrite;
return this;
- }
+ }
+
+ ///
+ /// Configures the to use System.Text.Json for serialization.
+ /// Use to use System.Text.Json with a default configuration.
+ /// If no options are specified, Newtonsoft.Json will be used for serialization instead.
+ ///
+ /// An instance of
+ /// containing the system text json serializer options.
+ /// The object
+ public CosmosClientBuilder WithSystemTextJsonSerializerOptions(
+ System.Text.Json.JsonSerializerOptions serializerOptions)
+ {
+ this.clientOptions.UseSystemTextJsonSerializerWithOptions = serializerOptions;
+ return this;
+ }
///
/// The event handler to be invoked before the request is sent.
@@ -725,7 +740,7 @@ internal CosmosClientBuilder WithPartitionLevelFailoverEnabled()
{
this.clientOptions.EnablePartitionLevelFailover = true;
return this;
- }
+ }
///
/// Enables SDK to inject fault. Used for testing applications.
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqAggregateCustomSerializationBaseline.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqAggregateCustomSerializationBaseline.cs
index ac686b31e1..54af230979 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqAggregateCustomSerializationBaseline.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqAggregateCustomSerializationBaseline.cs
@@ -29,7 +29,6 @@ public class LinqAggregateCustomSerializationBaseline : BaselineTests cosmosClientBuilder.WithCustomSerializer(stjCosmosSerializer));
+ => cosmosClientBuilder.WithSystemTextJsonSerializerOptions(
+ new JsonSerializerOptions()),
+ useCustomSeralizer: false);
// Set a callback to get the handle of the last executed query to do the verification
// This is neede because aggregate queries return type is a scalar so it can't be used
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs
index 1224f48074..48aed543bf 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs
@@ -57,7 +57,10 @@ public async static Task Initialize(TestContext textContext)
TestDb = await CosmosClient.CreateDatabaseAsync(dbName);
CosmosDefaultSTJClient = TestCommon.CreateCosmosClient((cosmosClientBuilder)
- => cosmosClientBuilder.WithCustomSerializer(new CosmosSystemTextJsonSerializer(new JsonSerializerOptions())));
+ => cosmosClientBuilder
+ .WithSystemTextJsonSerializerOptions(
+ new JsonSerializerOptions()),
+ useCustomSeralizer: false);
string dbNameSTJ = $"{nameof(LinqTranslationBaselineTests)}-{Guid.NewGuid():N}";
TestDbSTJDefault = await CosmosDefaultSTJClient.CreateDatabaseAsync(dbNameSTJ);
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json
index 89074f0fef..2cc3fd8de5 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json
@@ -2988,6 +2988,16 @@
],
"MethodInfo": "System.String get_ApplicationRegion();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
+ "System.Text.Json.JsonSerializerOptions get_UseSystemTextJsonSerializerWithOptions()": {
+ "Type": "Method",
+ "Attributes": [],
+ "MethodInfo": "System.Text.Json.JsonSerializerOptions get_UseSystemTextJsonSerializerWithOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOptions": {
+ "Type": "Property",
+ "Attributes": [],
+ "MethodInfo": "System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOptions;CanRead:True;CanWrite:True;System.Text.Json.JsonSerializerOptions get_UseSystemTextJsonSerializerWithOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_UseSystemTextJsonSerializerWithOptions(System.Text.Json.JsonSerializerOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
"System.TimeSpan get_RequestTimeout()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
@@ -3165,6 +3175,11 @@
],
"MethodInfo": "Void set_TokenCredentialBackgroundRefreshInterval(System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
+ "Void set_UseSystemTextJsonSerializerWithOptions(System.Text.Json.JsonSerializerOptions)": {
+ "Type": "Method",
+ "Attributes": [],
+ "MethodInfo": "Void set_UseSystemTextJsonSerializerWithOptions(System.Text.Json.JsonSerializerOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
"Void set_WebProxy(System.Net.IWebProxy)": {
"Type": "Method",
"Attributes": [],
@@ -4737,6 +4752,11 @@
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithSerializerOptions(Microsoft.Azure.Cosmos.CosmosSerializationOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
+ "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithSystemTextJsonSerializerOptions(System.Text.Json.JsonSerializerOptions)": {
+ "Type": "Method",
+ "Attributes": [],
+ "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithSystemTextJsonSerializerOptions(System.Text.Json.JsonSerializerOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithThrottlingRetryOptions(System.TimeSpan, Int32)": {
"Type": "Method",
"Attributes": [],
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
index 293c3b14ab..c27ca8abf1 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
@@ -13,6 +13,7 @@ namespace Microsoft.Azure.Cosmos.Tests
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
+ using System.Text;
using global::Azure.Core;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Documents;
@@ -476,6 +477,26 @@ public void UserAgentContainsEnvironmentInformation()
Assert.AreEqual(userAgentSuffix, connectionPolicy.UserAgentSuffix);
Assert.IsTrue(connectionPolicy.UserAgentContainer.UserAgent.StartsWith(expectedValue));
Assert.IsTrue(connectionPolicy.UserAgentContainer.UserAgent.EndsWith(userAgentSuffix));
+ }
+
+ [TestMethod]
+ public void ValidateThatCustomSerializerGetsOverriddenWhenSTJSerializerEnabled()
+ {
+ CosmosClientOptions options = new CosmosClientOptions()
+ {
+ UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions()
+ {
+ PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase,
+ }
+ };
+
+ CosmosClient client = new(
+ "https://fake-account.documents.azure.com:443/",
+ Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())),
+ options
+ );
+
+ Assert.AreEqual(typeof(CosmosSystemTextJsonSerializer), client.ClientOptions.Serializer.GetType());
}
[TestMethod]
@@ -500,8 +521,58 @@ public void ThrowOnCustomSerializerWithSerializerOptions()
};
options.Serializer = new CosmosJsonDotNetSerializer();
- }
-
+ }
+
+ [TestMethod]
+ [DataRow(false, DisplayName = "Test when the client options order is maintained")]
+ [DataRow(true, DisplayName = "Test when the client options order is reversed")]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ThrowOnCustomSerializerWithSTJSerializerEnabled(
+ bool reverseOrder)
+ {
+ if (reverseOrder)
+ {
+ CosmosClientOptions options = new CosmosClientOptions()
+ {
+ Serializer = new CosmosJsonDotNetSerializer(),
+ UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
+ };
+ }
+ else
+ {
+ CosmosClientOptions options = new CosmosClientOptions()
+ {
+ UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
+ Serializer = new CosmosJsonDotNetSerializer(),
+ };
+ }
+ }
+
+ [TestMethod]
+ [DataRow(false, DisplayName = "Test when the client options order is maintained")]
+ [DataRow(true, DisplayName = "Test when the client options order is reversed")]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ThrowOnSerializerOptionsWithSTJSerializerEnabled(
+ bool reverseOrder)
+ {
+ if (reverseOrder)
+ {
+ CosmosClientOptions options = new CosmosClientOptions()
+ {
+ SerializerOptions = new CosmosSerializationOptions(),
+ UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
+ };
+ }
+ else
+ {
+ CosmosClientOptions options = new CosmosClientOptions()
+ {
+ UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
+ SerializerOptions = new CosmosSerializationOptions(),
+ };
+ }
+ }
+
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ThrowOnNullTokenCredential()