Skip to content

Commit 8059e9b

Browse files
Make invariant culture formatting opt-in via new method overloads
Co-authored-by: alexeyzimarev <2821205+alexeyzimarev@users.noreply.github.com>
1 parent 567c723 commit 8059e9b

6 files changed

Lines changed: 91 additions & 50 deletions

File tree

src/RestSharp/Parameters/ObjectParser.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
// limitations under the License.
1414
//
1515

16-
using System.Globalization;
1716
using System.Reflection;
18-
using RestSharp.Extensions;
1917

2018
namespace RestSharp;
2119

@@ -74,7 +72,7 @@ IEnumerable<ParsedParameter> GetArray(PropertyInfo propertyInfo, object? value)
7472
bool IsAllowedProperty(string propertyName)
7573
=> includedProperties.Length == 0 || includedProperties.Length > 0 && includedProperties.Contains(propertyName);
7674

77-
string? ParseValue(string? format, object? value) => format == null ? value.ToStringInvariant() : string.Format(CultureInfo.InvariantCulture, $"{{0:{format}}}", value);
75+
string? ParseValue(string? format, object? value) => format == null ? value?.ToString() : string.Format($"{{0:{format}}}", value);
7876
}
7977
}
8078

src/RestSharp/Parameters/Parameter.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
// limitations under the License.
1414

1515
using System.Diagnostics;
16-
using RestSharp.Extensions;
1716

1817
namespace RestSharp;
1918

@@ -77,10 +76,10 @@ protected Parameter(string? name, object? value, ParameterType type, bool encode
7776
public static Parameter CreateParameter(string? name, object? value, ParameterType type, bool encode = true)
7877
// ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
7978
=> type switch {
80-
ParameterType.GetOrPost => new GetOrPostParameter(Ensure.NotEmptyString(name, nameof(name)), value.ToStringInvariant(), encode),
81-
ParameterType.UrlSegment => new UrlSegmentParameter(Ensure.NotEmptyString(name, nameof(name)), value.ToStringInvariant()!, encode),
82-
ParameterType.HttpHeader => new HeaderParameter(name!, value.ToStringInvariant()!),
83-
ParameterType.QueryString => new QueryParameter(Ensure.NotEmptyString(name, nameof(name)), value.ToStringInvariant(), encode),
79+
ParameterType.GetOrPost => new GetOrPostParameter(Ensure.NotEmptyString(name, nameof(name)), value?.ToString(), encode),
80+
ParameterType.UrlSegment => new UrlSegmentParameter(Ensure.NotEmptyString(name, nameof(name)), value?.ToString()!, encode),
81+
ParameterType.HttpHeader => new HeaderParameter(name!, value?.ToString()!),
82+
ParameterType.QueryString => new QueryParameter(Ensure.NotEmptyString(name, nameof(name)), value?.ToString(), encode),
8483
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
8584
};
8685

src/RestSharp/Request/RestRequestExtensions.Query.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ public RestRequest AddQueryParameter(string name, string? value, bool encode = t
3939
/// <param name="encode">Encode the value or not, default true</param>
4040
/// <returns></returns>
4141
public RestRequest AddQueryParameter<T>(string name, T value, bool encode = true) where T : struct
42-
=> request.AddQueryParameter(name, value.ToStringInvariant(), encode);
42+
=> request.AddQueryParameter(name, value.ToString(), encode);
43+
44+
/// <summary>
45+
/// Adds a query string parameter to the request. The request resource should not contain any placeholders for this parameter.
46+
/// The parameter will be added to the request URL as a query string using name=value format.
47+
/// The value will be converted to string using invariant culture for IFormattable types.
48+
/// </summary>
49+
/// <param name="name">Parameter name</param>
50+
/// <param name="value">Parameter value</param>
51+
/// <param name="useInvariantCulture">When true, uses invariant culture for IFormattable types (e.g., numbers, dates)</param>
52+
/// <param name="encode">Encode the value or not, default true</param>
53+
/// <returns></returns>
54+
public RestRequest AddQueryParameter<T>(string name, T value, bool useInvariantCulture, bool encode = true) where T : struct
55+
=> request.AddQueryParameter(name, useInvariantCulture ? value.ToStringInvariant() : value.ToString(), encode);
4356
}
4457
}

src/RestSharp/Request/RestRequestExtensions.Url.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ public RestRequest AddUrlSegment(string name, string? value, bool encode = true)
3939
/// <param name="encode">Encode the value or not, default true</param>
4040
/// <returns></returns>
4141
public RestRequest AddUrlSegment<T>(string name, T value, bool encode = true) where T : struct
42-
=> request.AddUrlSegment(name, value.ToStringInvariant(), encode);
42+
=> request.AddUrlSegment(name, value.ToString(), encode);
43+
44+
/// <summary>
45+
/// Adds a URL segment parameter to the request. The resource URL must have a placeholder for the parameter for it to work.
46+
/// For example, if you add a URL segment parameter with the name "id", the resource URL should contain {id} in its path.
47+
/// The value will be converted to string using invariant culture for IFormattable types.
48+
/// </summary>
49+
/// <param name="name">Name of the parameter; must be matching a placeholder in the resource URL as {name}</param>
50+
/// <param name="value">Value of the parameter</param>
51+
/// <param name="useInvariantCulture">When true, uses invariant culture for IFormattable types (e.g., numbers, dates)</param>
52+
/// <param name="encode">Encode the value or not, default true</param>
53+
/// <returns></returns>
54+
public RestRequest AddUrlSegment<T>(string name, T value, bool useInvariantCulture, bool encode = true) where T : struct
55+
=> request.AddUrlSegment(name, useInvariantCulture ? value.ToStringInvariant() : value.ToString(), encode);
4356
}
4457
}

src/RestSharp/Request/RestRequestExtensions.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,19 @@ public RestRequest AddParameter(string? name, object value, ParameterType type,
5454
/// <param name="encode">Encode the value or not, default true</param>
5555
/// <returns>This request</returns>
5656
public RestRequest AddParameter<T>(string name, T value, bool encode = true) where T : struct
57-
=> request.AddParameter(name, value.ToStringInvariant(), encode);
57+
=> request.AddParameter(name, value.ToString(), encode);
58+
59+
/// <summary>
60+
/// Adds a HTTP parameter to the request (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT).
61+
/// The value will be converted to string using invariant culture for IFormattable types.
62+
/// </summary>
63+
/// <param name="name">Name of the parameter</param>
64+
/// <param name="value">Value of the parameter</param>
65+
/// <param name="useInvariantCulture">When true, uses invariant culture for IFormattable types (e.g., numbers, dates)</param>
66+
/// <param name="encode">Encode the value or not, default true</param>
67+
/// <returns>This request</returns>
68+
public RestRequest AddParameter<T>(string name, T value, bool useInvariantCulture, bool encode = true) where T : struct
69+
=> request.AddParameter(name, useInvariantCulture ? value.ToStringInvariant() : value.ToString(), encode);
5870

5971
/// <summary>
6072
/// Adds or updates a HTTP parameter to the request (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT)
@@ -74,7 +86,19 @@ public RestRequest AddOrUpdateParameter(string name, string? value, bool encode
7486
/// <param name="encode">Encode the value or not, default true</param>
7587
/// <returns>This request</returns>
7688
public RestRequest AddOrUpdateParameter<T>(string name, T value, bool encode = true) where T : struct
77-
=> request.AddOrUpdateParameter(name, value.ToStringInvariant(), encode);
89+
=> request.AddOrUpdateParameter(name, value.ToString(), encode);
90+
91+
/// <summary>
92+
/// Adds or updates a HTTP parameter to the request (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT).
93+
/// The value will be converted to string using invariant culture for IFormattable types.
94+
/// </summary>
95+
/// <param name="name">Name of the parameter</param>
96+
/// <param name="value">Value of the parameter</param>
97+
/// <param name="useInvariantCulture">When true, uses invariant culture for IFormattable types (e.g., numbers, dates)</param>
98+
/// <param name="encode">Encode the value or not, default true</param>
99+
/// <returns>This request</returns>
100+
public RestRequest AddOrUpdateParameter<T>(string name, T value, bool useInvariantCulture, bool encode = true) where T : struct
101+
=> request.AddOrUpdateParameter(name, useInvariantCulture ? value.ToStringInvariant() : value.ToString(), encode);
78102

79103
RestRequest AddParameters(IEnumerable<Parameter> parameters) {
80104
request.Parameters.AddParameters(parameters);

test/RestSharp.Tests/Parameters/InvariantCultureParameterTests.cs

Lines changed: 32 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ namespace RestSharp.Tests.Parameters;
44

55
public class InvariantCultureParameterTests {
66
[Fact]
7-
public void AddParameter_Double_UsesInvariantCulture() {
7+
public void AddParameter_Double_UsesInvariantCulture_WhenOptIn() {
88
// Save original culture
99
var originalCulture = Thread.CurrentThread.CurrentCulture;
1010
try {
1111
// Set a culture that uses comma as decimal separator
1212
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
1313

1414
var request = new RestRequest();
15-
request.AddParameter("value", 1.234);
15+
request.AddParameter("value", 1.234, useInvariantCulture: true);
1616

1717
var parameter = request.Parameters.First();
1818
parameter.Value.Should().Be("1.234");
@@ -23,30 +23,33 @@ public void AddParameter_Double_UsesInvariantCulture() {
2323
}
2424

2525
[Fact]
26-
public void AddOrUpdateParameter_Double_UsesInvariantCulture() {
26+
public void AddParameter_Double_UsesCurrentCulture_ByDefault() {
27+
// Save original culture
2728
var originalCulture = Thread.CurrentThread.CurrentCulture;
2829
try {
30+
// Set a culture that uses comma as decimal separator
2931
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
3032

3133
var request = new RestRequest();
32-
request.AddOrUpdateParameter("value", 1.234);
34+
request.AddParameter("value", 1.234);
3335

3436
var parameter = request.Parameters.First();
35-
parameter.Value.Should().Be("1.234");
37+
// Default behavior uses current culture (comma as decimal separator)
38+
parameter.Value.Should().Be("1,234");
3639
}
3740
finally {
3841
Thread.CurrentThread.CurrentCulture = originalCulture;
3942
}
4043
}
4144

4245
[Fact]
43-
public void AddQueryParameter_Double_UsesInvariantCulture() {
46+
public void AddOrUpdateParameter_Double_UsesInvariantCulture_WhenOptIn() {
4447
var originalCulture = Thread.CurrentThread.CurrentCulture;
4548
try {
4649
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
4750

4851
var request = new RestRequest();
49-
request.AddQueryParameter("value", 1.234);
52+
request.AddOrUpdateParameter("value", 1.234, useInvariantCulture: true);
5053

5154
var parameter = request.Parameters.First();
5255
parameter.Value.Should().Be("1.234");
@@ -57,13 +60,13 @@ public void AddQueryParameter_Double_UsesInvariantCulture() {
5760
}
5861

5962
[Fact]
60-
public void AddUrlSegment_Double_UsesInvariantCulture() {
63+
public void AddQueryParameter_Double_UsesInvariantCulture_WhenOptIn() {
6164
var originalCulture = Thread.CurrentThread.CurrentCulture;
6265
try {
6366
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
6467

65-
var request = new RestRequest("{value}");
66-
request.AddUrlSegment("value", 1.234);
68+
var request = new RestRequest();
69+
request.AddQueryParameter("value", 1.234, useInvariantCulture: true);
6770

6871
var parameter = request.Parameters.First();
6972
parameter.Value.Should().Be("1.234");
@@ -74,13 +77,15 @@ public void AddUrlSegment_Double_UsesInvariantCulture() {
7477
}
7578

7679
[Fact]
77-
public void CreateParameter_Double_UsesInvariantCulture() {
80+
public void AddUrlSegment_Double_UsesInvariantCulture_WhenOptIn() {
7881
var originalCulture = Thread.CurrentThread.CurrentCulture;
7982
try {
8083
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
8184

82-
var parameter = Parameter.CreateParameter("value", 1.234, ParameterType.GetOrPost);
85+
var request = new RestRequest("{value}");
86+
request.AddUrlSegment("value", 1.234, useInvariantCulture: true);
8387

88+
var parameter = request.Parameters.First();
8489
parameter.Value.Should().Be("1.234");
8590
}
8691
finally {
@@ -89,13 +94,13 @@ public void CreateParameter_Double_UsesInvariantCulture() {
8994
}
9095

9196
[Fact]
92-
public void AddParameter_Decimal_UsesInvariantCulture() {
97+
public void AddParameter_Decimal_UsesInvariantCulture_WhenOptIn() {
9398
var originalCulture = Thread.CurrentThread.CurrentCulture;
9499
try {
95100
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
96101

97102
var request = new RestRequest();
98-
request.AddParameter("value", 123.456m);
103+
request.AddParameter("value", 123.456m, useInvariantCulture: true);
99104

100105
var parameter = request.Parameters.First();
101106
parameter.Value.Should().Be("123.456");
@@ -106,13 +111,13 @@ public void AddParameter_Decimal_UsesInvariantCulture() {
106111
}
107112

108113
[Fact]
109-
public void AddParameter_Float_UsesInvariantCulture() {
114+
public void AddParameter_Float_UsesInvariantCulture_WhenOptIn() {
110115
var originalCulture = Thread.CurrentThread.CurrentCulture;
111116
try {
112117
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
113118

114119
var request = new RestRequest();
115-
request.AddParameter("value", 2.5f);
120+
request.AddParameter("value", 2.5f, useInvariantCulture: true);
116121

117122
var parameter = request.Parameters.First();
118123
parameter.Value.Should().Be("2.5");
@@ -123,14 +128,14 @@ public void AddParameter_Float_UsesInvariantCulture() {
123128
}
124129

125130
[Fact]
126-
public void AddParameter_DateTime_UsesInvariantCulture() {
131+
public void AddParameter_DateTime_UsesInvariantCulture_WhenOptIn() {
127132
var originalCulture = Thread.CurrentThread.CurrentCulture;
128133
try {
129134
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
130135

131136
var dateTime = new DateTime(2024, 12, 25, 10, 30, 0, DateTimeKind.Unspecified);
132137
var request = new RestRequest();
133-
request.AddParameter("date", dateTime);
138+
request.AddParameter("date", dateTime, useInvariantCulture: true);
134139

135140
var parameter = request.Parameters.First();
136141
// DateTime.ToString with InvariantCulture uses MM/dd/yyyy format
@@ -142,33 +147,22 @@ public void AddParameter_DateTime_UsesInvariantCulture() {
142147
}
143148

144149
[Fact]
145-
public void AddParameter_Integer_WorksWithAnyCulture() {
150+
public void AddParameter_Integer_SameValueWithOrWithoutInvariantCulture() {
146151
var originalCulture = Thread.CurrentThread.CurrentCulture;
147152
try {
148153
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
149154

150-
var request = new RestRequest();
151-
request.AddParameter("value", 12345);
155+
var requestWithInvariant = new RestRequest();
156+
requestWithInvariant.AddParameter("value", 12345, useInvariantCulture: true);
152157

153-
var parameter = request.Parameters.First();
154-
parameter.Value.Should().Be("12345");
155-
}
156-
finally {
157-
Thread.CurrentThread.CurrentCulture = originalCulture;
158-
}
159-
}
160-
161-
[Fact]
162-
public void AddObject_WithDoubleProperty_UsesInvariantCulture() {
163-
var originalCulture = Thread.CurrentThread.CurrentCulture;
164-
try {
165-
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
158+
var requestWithoutInvariant = new RestRequest();
159+
requestWithoutInvariant.AddParameter("value", 12345);
166160

167-
var request = new RestRequest();
168-
request.AddObject(new { Amount = 1.234 });
161+
var parameterWithInvariant = requestWithInvariant.Parameters.First();
162+
var parameterWithoutInvariant = requestWithoutInvariant.Parameters.First();
169163

170-
var parameter = request.Parameters.First();
171-
parameter.Value.Should().Be("1.234");
164+
parameterWithInvariant.Value.Should().Be("12345");
165+
parameterWithoutInvariant.Value.Should().Be("12345");
172166
}
173167
finally {
174168
Thread.CurrentThread.CurrentCulture = originalCulture;

0 commit comments

Comments
 (0)