diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs
index 3570439e1..7f585c329 100644
--- a/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs
@@ -32,7 +32,7 @@ public class ResponseModel
public object? BodyAsJson { get; set; }
///
- /// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
+ /// Gets or sets a value indicating whether the Json Body String needs to be indented.
///
public bool? BodyAsJsonIndented { get; set; }
diff --git a/src/WireMock.Net.Abstractions/Models/IBodyData.cs b/src/WireMock.Net.Abstractions/Models/IBodyData.cs
index 79b737d5f..593d4c406 100644
--- a/src/WireMock.Net.Abstractions/Models/IBodyData.cs
+++ b/src/WireMock.Net.Abstractions/Models/IBodyData.cs
@@ -37,7 +37,7 @@ public interface IBodyData
object? BodyAsJson { get; set; }
///
- /// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
+ /// Gets or sets a value indicating whether the Json Body String needs to be indented.
///
bool? BodyAsJsonIndented { get; set; }
diff --git a/src/WireMock.Net.Extensions.Routing/WireMock.Net.Extensions.Routing.csproj b/src/WireMock.Net.Extensions.Routing/WireMock.Net.Extensions.Routing.csproj
index 707912275..533c93a7c 100644
--- a/src/WireMock.Net.Extensions.Routing/WireMock.Net.Extensions.Routing.csproj
+++ b/src/WireMock.Net.Extensions.Routing/WireMock.Net.Extensions.Routing.csproj
@@ -1,4 +1,4 @@
-
+
WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET
Gennadii Saltyshchak
@@ -25,7 +25,7 @@
-
+
diff --git a/src/WireMock.Net.Minimal/MappingBuilder.cs b/src/WireMock.Net.Minimal/MappingBuilder.cs
index 6e900e1a1..79b85ef67 100644
--- a/src/WireMock.Net.Minimal/MappingBuilder.cs
+++ b/src/WireMock.Net.Minimal/MappingBuilder.cs
@@ -3,7 +3,6 @@
using System;
using System.Linq;
using System.Text;
-using Newtonsoft.Json;
using Stef.Validation;
using WireMock.Admin.Mappings;
using WireMock.Matchers.Request;
@@ -164,8 +163,8 @@ private void RegisterMapping(IMapping mapping, bool saveToFile)
}
}
- private static string ToJson(object value)
+ private string ToJson(object value)
{
- return JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsDefault);
+ return _settings.DefaultJsonSerializer.Serialize(value, JsonSerializationConstants.JsonConverterOptionsDefault);
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Minimal/Serialization/MappingSerializer.cs b/src/WireMock.Net.Minimal/Serialization/MappingSerializer.cs
new file mode 100644
index 000000000..577caffaa
--- /dev/null
+++ b/src/WireMock.Net.Minimal/Serialization/MappingSerializer.cs
@@ -0,0 +1,49 @@
+// Copyright © WireMock.Net
+
+using System;
+using JsonConverter.Abstractions;
+using Newtonsoft.Json.Linq;
+#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER || NET6_0_OR_GREATER || NET461
+using System.Text.Json;
+#endif
+
+namespace WireMock.Serialization;
+
+internal class MappingSerializer(IJsonConverter jsonConverter)
+{
+ internal T[] DeserializeJsonToArray(string value)
+ {
+ return DeserializeObjectToArray(jsonConverter.Deserialize
-
-
+
+
@@ -118,11 +118,13 @@
+
+
diff --git a/src/WireMock.Net.RestClient/WireMock.Net.RestClient.csproj b/src/WireMock.Net.RestClient/WireMock.Net.RestClient.csproj
index 3baecc439..bff6b9b65 100644
--- a/src/WireMock.Net.RestClient/WireMock.Net.RestClient.csproj
+++ b/src/WireMock.Net.RestClient/WireMock.Net.RestClient.csproj
@@ -33,7 +33,7 @@
-
+
diff --git a/src/WireMock.Net.Shared/Serialization/JsonSerializationConstants.cs b/src/WireMock.Net.Shared/Serialization/JsonSerializationConstants.cs
index 1fd258a5f..f1b31ef32 100644
--- a/src/WireMock.Net.Shared/Serialization/JsonSerializationConstants.cs
+++ b/src/WireMock.Net.Shared/Serialization/JsonSerializationConstants.cs
@@ -1,5 +1,6 @@
// Copyright © WireMock.Net
+using JsonConverter.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
@@ -7,24 +8,30 @@ namespace WireMock.Serialization;
internal static class JsonSerializationConstants
{
- public static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new()
+ internal static readonly JsonConverterOptions JsonConverterOptionsDefault = new()
{
- Formatting = Formatting.Indented,
- NullValueHandling = NullValueHandling.Ignore
+ WriteIndented = true,
+ IgnoreNullValues = true
};
- public static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
+ //internal static readonly JsonSerializerSettings JsonSerializerSettingsDefault = new()
+ //{
+ // Formatting = Formatting.Indented,
+ // NullValueHandling = NullValueHandling.Ignore
+ //};
+
+ internal static readonly JsonSerializerSettings JsonSerializerSettingsIncludeNullValues = new()
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Include
};
- public static readonly JsonSerializerSettings JsonDeserializerSettingsWithDateParsingNone = new()
+ internal static readonly JsonSerializerSettings JsonDeserializerSettingsWithDateParsingNone = new()
{
DateParseHandling = DateParseHandling.None
};
- public static readonly JsonSerializerSettings JsonSerializerSettingsPact = new()
+ internal static readonly JsonSerializerSettings JsonSerializerSettingsPact = new()
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
diff --git a/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs b/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs
index 86f99f382..8e63fd9bd 100644
--- a/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs
+++ b/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs
@@ -14,6 +14,8 @@
using WireMock.Types;
using System.Globalization;
using WireMock.Models;
+using JsonConverter.Abstractions;
+using JsonConverter.Newtonsoft.Json;
#if USE_ASPNETCORE
using Microsoft.Extensions.DependencyInjection;
@@ -338,4 +340,14 @@ public class WireMockServerSettings
///
[PublicAPI]
public HandlebarsSettings? HandlebarsSettings { get; set; }
+
+ ///
+ /// Gets or sets the default JSON converter used for serialization.
+ ///
+ ///
+ /// Set this property to customize how objects are serialized to and deserialized from JSON during mapping.
+ /// Default is .
+ ///
+ [PublicAPI]
+ public IJsonConverter DefaultJsonSerializer { get; set; } = new NewtonsoftJsonConverter();
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Shared/WireMock.Net.Shared.csproj b/src/WireMock.Net.Shared/WireMock.Net.Shared.csproj
index 2b01dd010..ced033fb4 100644
--- a/src/WireMock.Net.Shared/WireMock.Net.Shared.csproj
+++ b/src/WireMock.Net.Shared/WireMock.Net.Shared.csproj
@@ -1,4 +1,4 @@
-
+
Shared interfaces, models, enumerations and types.
Stef Heyenrath
@@ -48,9 +48,10 @@
-
+
-
+
+
diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs
index 253d1ab82..151de0f7a 100644
--- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs
+++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs
@@ -4,6 +4,7 @@
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
+using JsonConverter.Newtonsoft.Json;
using Moq;
using Newtonsoft.Json.Linq;
using NFluent;
@@ -316,12 +317,11 @@ public async Task Response_ProvideResponse_WithResponseDeleted()
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request1, _settings).ConfigureAwait(false);
Check.That(response.Message.StatusCode).IsEqualTo(200);
- Check.That(response.Message.BodyData.BodyAsString).Contains("File deleted.");
+ Check.That(response.Message.BodyData?.BodyAsString).Contains("File deleted.");
}
-#if !(NET451 || NET452)
[Fact]
- public async Task Response_ProvideResponse_WithBody_IJsonConverter_SystemTextJson()
+ public async Task Response_ProvideResponse_WithBody_NewtonsoftJsonConverter()
{
// Arrange
var requestBody = new BodyData
@@ -331,13 +331,34 @@ public async Task Response_ProvideResponse_WithBody_IJsonConverter_SystemTextJso
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, requestBody);
- var responseBuilder = Response.Create().WithBody(new { foo = "bar", n = 42 }, new JsonConverter.System.Text.Json.SystemTextJsonConverter());
+ var responseBuilder = Response.Create().WithBody(new { foo = "< > & ' 😀 👍 ❤️", n = 42 }, new NewtonsoftJsonConverter());
// Act
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
// Assert
- response.Message.BodyData!.BodyAsString.Should().Be(@"{""foo"":""bar"",""n"":42}");
+ response.Message.BodyData!.BodyAsString.Should().Be("""{"foo":"< > & ' 😀 👍 ❤️","n":42}""");
+ }
+
+#if !(NET451 || NET452 || NET461)
+ [Fact]
+ public async Task Response_ProvideResponse_WithBody_SystemTextJsonConverter()
+ {
+ // Arrange
+ var requestBody = new BodyData
+ {
+ DetectedBodyType = BodyType.String,
+ BodyAsString = "abc"
+ };
+ var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, requestBody);
+
+ var responseBuilder = Response.Create().WithBody(new { foo = "< > & ' 😀 👍 ❤️", n = 42 }, new JsonConverter.System.Text.Json.SystemTextJsonConverter());
+
+ // Act
+ var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
+
+ // Assert
+ response.Message.BodyData!.BodyAsString.Should().Be("""{"foo":"\u003C \u003E \u0026 \u0027 \uD83D\uDE00 \uD83D\uDC4D \u2764\uFE0F","n":42}""");
}
#endif
}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Serialization/MappingSerializerTests.cs b/test/WireMock.Net.Tests/Serialization/MappingSerializerTests.cs
new file mode 100644
index 000000000..f1f7a11db
--- /dev/null
+++ b/test/WireMock.Net.Tests/Serialization/MappingSerializerTests.cs
@@ -0,0 +1,326 @@
+// Copyright © WireMock.Net
+
+using System;
+using FluentAssertions;
+using JsonConverter.Newtonsoft.Json;
+using WireMock.Admin.Mappings;
+using WireMock.Serialization;
+using Xunit;
+#if NET8_0_OR_GREATER
+using JsonConverter.System.Text.Json;
+#endif
+
+namespace WireMock.Net.Tests.Serialization;
+
+public class MappingSerializerTests
+{
+ private const string SingleMappingJson =
+ """
+ {
+ "Guid": "12345678-1234-1234-1234-123456789012",
+ "Priority": 1,
+ "Request": {
+ "Path": "/test"
+ },
+ "Response": {
+ "StatusCode": 200
+ }
+ }
+ """;
+
+ private const string ArrayMappingJson =
+ """
+ [
+ {
+ "Guid": "12345678-1234-1234-1234-123456789012",
+ "Priority": 1,
+ "Request": {
+ "Path": "/test1"
+ },
+ "Response": {
+ "StatusCode": 200
+ }
+ },
+ {
+ "Guid": "87654321-4321-4321-4321-210987654321",
+ "Priority": 2,
+ "Request": {
+ "Path": "/test2"
+ },
+ "Response": {
+ "StatusCode": 404
+ }
+ }
+ ]
+ """;
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_SingleObject_ShouldReturnArray()
+ {
+ // Arrange
+ var jsonConverter = new NewtonsoftJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(SingleMappingJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().HaveCount(1);
+ result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
+ result[0].Priority.Should().Be(1);
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_Array_ShouldReturnArray()
+ {
+ // Arrange
+ var jsonConverter = new NewtonsoftJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(ArrayMappingJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().HaveCount(2);
+ result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
+ result[0].Priority.Should().Be(1);
+ result[1].Guid.Should().Be(Guid.Parse("87654321-4321-4321-4321-210987654321"));
+ result[1].Priority.Should().Be(2);
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_EmptyArray_ShouldReturnEmptyArray()
+ {
+ // Arrange
+ var jsonConverter = new NewtonsoftJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var emptyArrayJson = "[]";
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(emptyArrayJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_InvalidJson_ShouldThrowException()
+ {
+ // Arrange
+ var jsonConverter = new NewtonsoftJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var invalidJson = "not valid json";
+
+ // Act
+ Action act = () => serializer.DeserializeJsonToArray(invalidJson);
+
+ // Assert
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_ComplexMapping_ShouldDeserializeCorrectly()
+ {
+ // Arrange
+ var jsonConverter = new NewtonsoftJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var complexJson =
+ """
+ {
+ "Guid": "12345678-1234-1234-1234-123456789012",
+ "Title": "Test Mapping",
+ "Description": "A test mapping",
+ "Priority": 10,
+ "Request": {
+ "Path": "/api/test",
+ "Methods": ["GET", "POST"]
+ },
+ "Response": {
+ "StatusCode": 201,
+ "Body": "Test Response"
+ }
+ }
+ """;
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(complexJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().HaveCount(1);
+ result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
+ result[0].Title.Should().Be("Test Mapping");
+ result[0].Description.Should().Be("A test mapping");
+ result[0].Priority.Should().Be(10);
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_NullValue_ShouldThrowException()
+ {
+ // Arrange
+ var jsonConverter = new NewtonsoftJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var nullJson = "null";
+
+ // Act
+ Action act = () => serializer.DeserializeJsonToArray(nullJson);
+
+ // Assert
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithNewtonsoftJson_PrimitiveValue_ShouldThrowInvalidOperationException()
+ {
+ // Arrange
+ var jsonConverter = new NewtonsoftJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var primitiveJson = "\"string value\"";
+
+ // Act
+ Action act = () => serializer.DeserializeJsonToArray(primitiveJson);
+
+ // Assert
+ act.Should().Throw()
+ .WithMessage("Cannot deserialize the provided value to an array or object.");
+ }
+
+#if NET8_0_OR_GREATER
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_SingleObject_ShouldReturnArray()
+ {
+ // Arrange
+ var jsonConverter = new SystemTextJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(SingleMappingJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().HaveCount(1);
+ result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
+ result[0].Priority.Should().Be(1);
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_Array_ShouldReturnArray()
+ {
+ // Arrange
+ var jsonConverter = new SystemTextJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(ArrayMappingJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().HaveCount(2);
+ result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
+ result[0].Priority.Should().Be(1);
+ result[1].Guid.Should().Be(Guid.Parse("87654321-4321-4321-4321-210987654321"));
+ result[1].Priority.Should().Be(2);
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_EmptyArray_ShouldReturnEmptyArray()
+ {
+ // Arrange
+ var jsonConverter = new SystemTextJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var emptyArrayJson = "[]";
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(emptyArrayJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_InvalidJson_ShouldThrowException()
+ {
+ // Arrange
+ var jsonConverter = new SystemTextJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var invalidJson = "not valid json";
+
+ // Act
+ Action act = () => serializer.DeserializeJsonToArray(invalidJson);
+
+ // Assert
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_ComplexMapping_ShouldDeserializeCorrectly()
+ {
+ // Arrange
+ var jsonConverter = new SystemTextJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var complexJson =
+ """
+ {
+ "Guid": "12345678-1234-1234-1234-123456789012",
+ "Title": "Test Mapping",
+ "Description": "A test mapping",
+ "Priority": 10,
+ "Request": {
+ "Path": "/api/test",
+ "Methods": ["GET", "POST"]
+ },
+ "Response": {
+ "StatusCode": 201,
+ "Body": "Test Response"
+ }
+ }
+ """;
+
+ // Act
+ var result = serializer.DeserializeJsonToArray(complexJson);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Should().HaveCount(1);
+ result[0].Guid.Should().Be(Guid.Parse("12345678-1234-1234-1234-123456789012"));
+ result[0].Title.Should().Be("Test Mapping");
+ result[0].Description.Should().Be("A test mapping");
+ result[0].Priority.Should().Be(10);
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_NullValue_ShouldThrowException()
+ {
+ // Arrange
+ var jsonConverter = new SystemTextJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var nullJson = "null";
+
+ // Act
+ Action act = () => serializer.DeserializeJsonToArray(nullJson);
+
+ // Assert
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void MappingSerializer_DeserializeJsonToArray_WithSystemTextJson_PrimitiveValue_ShouldThrowInvalidOperationException()
+ {
+ // Arrange
+ var jsonConverter = new SystemTextJsonConverter();
+ var serializer = new MappingSerializer(jsonConverter);
+ var primitiveJson = "\"string value\"";
+
+ // Act
+ Action act = () => serializer.DeserializeJsonToArray(primitiveJson);
+
+ // Assert
+ act.Should().Throw()
+ .WithMessage("Cannot deserialize the provided value to an array or object.");
+ }
+#endif
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
index 3602674f6..f1720cefd 100644
--- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
+++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
@@ -1,4 +1,4 @@
-
+
Stef Heyenrath
@@ -103,7 +103,7 @@
-
+