Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/RestSharp/Serializers/SerializerConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,23 @@ public SerializerConfig UseSerializer(Func<IRestSerializer> serializerFactory) {
return this;
}

public void UseDefaultSerializers() => UseSerializer<SystemTextJsonSerializer>().UseSerializer<XmlRestSerializer>();
public void UseDefaultSerializers() {
// Register factories without constructing the default serializers. Instantiating
// System.Text.Json here would pull it in even when the caller immediately replaces
// JSON with Newtonsoft.Json (or another custom serializer).
Serializers[DataFormat.Json] = new(
DataFormat.Json,
ContentType.JsonAccept,
contentType => contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase),
static () => new SystemTextJsonSerializer()
Comment on lines +45 to +48

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Duplicated default matching rules 🐞 Bug ⚙ Maintainability

SerializerConfig.UseDefaultSerializers now hard-codes the default JSON/XML AcceptedContentTypes and
SupportsContentType predicates, duplicating logic that already exists on SystemTextJsonSerializer
and XmlRestSerializer. This creates a second source of truth, so future changes to serializer
matching behavior can silently diverge from the defaults registered in SerializerConfig and lead to
inconsistent serializer resolution.
Agent Prompt
## Issue description
`SerializerConfig.UseDefaultSerializers()` duplicates the default serializers' `AcceptedContentTypes` and `SupportsContentType` logic. This increases drift risk: if `SystemTextJsonSerializer` or `XmlRestSerializer` updates its matching rules/content types later, the defaults registered in `SerializerConfig` may no longer match actual serializer behavior.

## Issue Context
The PR intentionally avoids constructing serializers in `UseDefaultSerializers()` to prevent eager loading of `System.Text.Json`. A fix should preserve that laziness while keeping matching metadata in one place.

## Fix approach (keep lazy loading)
- Introduce a shared, dependency-free helper (e.g., internal static class or methods on `ContentType`) that defines:
  - `JsonAcceptedContentTypes` / `XmlAcceptedContentTypes` (can just reference `ContentType.JsonAccept` / `ContentType.XmlAccept`), and
  - `SupportsJson(ContentType ct)` / `SupportsXml(ContentType ct)` predicates.
- Update both `SerializerConfig.UseDefaultSerializers()` and the serializer classes’ `SupportsContentType` implementations to call the shared helper.

## Fix Focus Areas
- src/RestSharp/Serializers/SerializerConfig.cs[40-55]
- src/RestSharp/Serializers/Json/SystemTextJsonSerializer.cs[39-46]
- src/RestSharp/Serializers/Xml/XmlRestSerializer.cs[23-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

);
Serializers[DataFormat.Xml] = new(
DataFormat.Xml,
ContentType.XmlAccept,
contentType => contentType.Value.EndsWith("xml", StringComparison.InvariantCultureIgnoreCase),
static () => new XmlRestSerializer()
);
}

/// <summary>
/// Replace the default serializer with a custom one
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
actual.Should().BeEquivalentTo(testData);
}

[Fact]
public void UseNewtonsoftJson_replaces_default_json_serializer() {
using var client = new RestClient(_server.Url!, configureSerialization: cfg => cfg.UseNewtonsoftJson());

Check warning on line 29 in test/RestSharp.Tests.Serializers.Json/NewtonsoftJson/IntegratedSimpleTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this null-forgiving operator; nullable warnings are disabled here.

See more on https://sonarcloud.io/project/issues?id=restsharp_RestSharp&issues=AaAPQr6mQulb_Xfng9SX&open=AaAPQr6mQulb_Xfng9SX&pullRequest=2403

client.Serializers.GetSerializer(DataFormat.Json).Should().BeOfType<JsonNetSerializer>();
client.Serializers.Serializers.Should().ContainKey(DataFormat.Xml);
}

[Fact]
public async Task Should_deserialize_response() {
var expected = Fixture.Create<TestClass>();
Expand Down
25 changes: 25 additions & 0 deletions test/RestSharp.Tests/RestClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,31 @@ public void UseOnlySerializer_leaves_only_custom_serializer() {
client.Serializers.GetSerializer(DataFormat.Json).Should().NotBeNull();
}

[Fact]
public void Default_serializers_are_registered_lazily_and_survive_replacement() {
// arrange
var baseUrl = new Uri(BaseUrl);

// act
using var client = new RestClient(baseUrl, configureSerialization: cfg => cfg.UseOnlySerializer(() => new JsonNetSerializerStub()));

// assert
client.Serializers.Serializers.Should().HaveCount(1);
client.Serializers.GetSerializer(DataFormat.Json).Should().BeOfType<JsonNetSerializerStub>();
}

sealed class JsonNetSerializerStub : IRestSerializer, ISerializer, IDeserializer {
public string? Serialize(object? obj) => null;
public string? Serialize(Parameter bodyParameter) => null;
public T? Deserialize<T>(RestResponse response) => default;
public ContentType ContentType { get; set; } = ContentType.Json;
public ISerializer Serializer => this;
public IDeserializer Deserializer => this;
public DataFormat DataFormat => DataFormat.Json;
public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType { get; } = _ => false;
}

[Fact]
public void Should_reuse_httpClient_instance() {
using var client1 = new RestClient(new Uri("https://fake.api"), useClientFactory: true);
Expand Down