diff --git a/src/WireMock.Net.Minimal/Matchers/JsonMatcher.cs b/src/WireMock.Net.Minimal/Matchers/JsonMatcher.cs
index 1957e497..b6a450c3 100644
--- a/src/WireMock.Net.Minimal/Matchers/JsonMatcher.cs
+++ b/src/WireMock.Net.Minimal/Matchers/JsonMatcher.cs
@@ -32,6 +32,11 @@ public class JsonMatcher : IJsonMatcher
///
public bool Regex { get; }
+ ///
+ /// Ignore array order when comparing
+ ///
+ public bool IgnoreArrayOrder { get; }
+
private readonly JToken _valueAsJToken;
///
@@ -40,7 +45,8 @@ public class JsonMatcher : IJsonMatcher
/// The string value to check for equality.
/// Ignore the case from the PropertyName and PropertyValue (string only).
/// Support Regex.
- public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
+ /// Ignore array element order when comparing.
+ public JsonMatcher(string value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}
@@ -50,7 +56,8 @@ public class JsonMatcher : IJsonMatcher
/// The object value to check for equality.
/// Ignore the case from the PropertyName and PropertyValue (string only).
/// Support Regex.
- public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
+ /// Ignore array element order when comparing.
+ public JsonMatcher(object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}
@@ -61,13 +68,15 @@ public class JsonMatcher : IJsonMatcher
/// The value to check for equality.
/// Ignore the case from the PropertyName and PropertyValue (string only).
/// Support Regex.
- public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
+ /// Ignore array element order when comparing.
+ public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
{
Guard.NotNull(value);
MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
Regex = regex;
+ IgnoreArrayOrder = ignoreArrayOrder;
Value = value;
_valueAsJToken = ConvertValueToJToken(value);
@@ -106,7 +115,8 @@ public class JsonMatcher : IJsonMatcher
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
- $"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
+ $"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}, " +
+ $"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreArrayOrder)}" +
$")";
}
@@ -183,6 +193,13 @@ public class JsonMatcher : IJsonMatcher
return false;
}
+ if (IgnoreArrayOrder)
+ {
+ // Sort both arrays by their string representation and compare
+ valueArray = valueArray.OrderBy(t => t.ToString()).ToArray();
+ inputArray = inputArray.OrderBy(t => t.ToString()).ToArray();
+ }
+
return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
default:
diff --git a/src/WireMock.Net.Minimal/Matchers/SystemTextJsonMatcher.cs b/src/WireMock.Net.Minimal/Matchers/SystemTextJsonMatcher.cs
index 1f472343..4176460f 100644
--- a/src/WireMock.Net.Minimal/Matchers/SystemTextJsonMatcher.cs
+++ b/src/WireMock.Net.Minimal/Matchers/SystemTextJsonMatcher.cs
@@ -35,6 +35,11 @@ public class SystemTextJsonMatcher : IJsonMatcher
///
public bool Regex { get; }
+ ///
+ /// Ignore array order when comparing
+ ///
+ public bool IgnoreArrayOrder { get; }
+
private readonly JsonElement _valueAsJsonElement;
///
@@ -43,8 +48,9 @@ public class SystemTextJsonMatcher : IJsonMatcher
/// The string value to check for equality.
/// Ignore the case from the PropertyName and PropertyValue (string only).
/// Support Regex.
- public SystemTextJsonMatcher(string value, bool ignoreCase = false, bool regex = false)
- : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
+ /// Ignore array element order when comparing.
+ public SystemTextJsonMatcher(string value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
+ : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}
@@ -54,8 +60,9 @@ public class SystemTextJsonMatcher : IJsonMatcher
/// The object value to check for equality.
/// Ignore the case from the PropertyName and PropertyValue (string only).
/// Support Regex.
- public SystemTextJsonMatcher(object value, bool ignoreCase = false, bool regex = false)
- : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
+ /// Ignore array element order when comparing.
+ public SystemTextJsonMatcher(object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
+ : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}
@@ -66,13 +73,15 @@ public class SystemTextJsonMatcher : IJsonMatcher
/// The value to check for equality.
/// Ignore the case from the PropertyName and PropertyValue (string only).
/// Support Regex.
- public SystemTextJsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
+ /// Ignore array element order when comparing.
+ public SystemTextJsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
{
Guard.NotNull(value);
MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
Regex = regex;
+ IgnoreArrayOrder = ignoreArrayOrder;
Value = value;
_valueAsJsonElement = ConvertToJsonElement(value);
@@ -111,7 +120,8 @@ public class SystemTextJsonMatcher : IJsonMatcher
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
- $"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
+ $"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}, " +
+ $"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreArrayOrder)}" +
$")";
}
@@ -202,6 +212,13 @@ public class SystemTextJsonMatcher : IJsonMatcher
return false;
}
+ if (IgnoreArrayOrder)
+ {
+ // Sort both arrays by their string representation and compare
+ valueArray = valueArray.OrderBy(e => e.GetRawText()).ToArray();
+ inputArray = inputArray.OrderBy(e => e.GetRawText()).ToArray();
+ }
+
return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
}
diff --git a/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs
index 00cbfb4b..5637e0b2 100644
--- a/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs
+++ b/test/WireMock.Net.Tests/Matchers/JsonMatcherTests.cs
@@ -523,4 +523,151 @@ public class JsonMatcherTests
// Assert
Assert.Equal(1.0, score);
}
-}
+
+ [Fact]
+ public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
+
+ // Act
+ var jArray = new JArray
+ {
+ "c",
+ "a",
+ "b"
+ };
+ var score = matcher.IsMatch(jArray).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Fact]
+ public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderFalse_DifferentOrder_ShouldNotMatch()
+ {
+ // Assign
+ var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: false);
+
+ // Act
+ var jArray = new JArray
+ {
+ "c",
+ "a",
+ "b"
+ };
+ var score = matcher.IsMatch(jArray).Score;
+
+ // Assert
+ Assert.Equal(MatchScores.Mismatch, score);
+ }
+
+ [Fact]
+ public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_SameOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
+
+ // Act
+ var jArray = new JArray
+ {
+ "a",
+ "b",
+ "c"
+ };
+ var score = matcher.IsMatch(jArray).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Fact]
+ public void JsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentLength_ShouldNotMatch()
+ {
+ // Assign
+ var matcher = new JsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
+
+ // Act
+ var jArray = new JArray
+ {
+ "a",
+ "b"
+ };
+ var score = matcher.IsMatch(jArray).Score;
+
+ // Assert
+ Assert.Equal(MatchScores.Mismatch, score);
+ }
+
+ [Fact]
+ public void JsonMatcher_IsMatch_ObjectWithArray_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new JsonMatcher(new { Items = new[] { "x", "y", "z" } }, ignoreArrayOrder: true);
+
+ // Act
+ var jObject = new JObject
+ {
+ { "Items", new JArray("z", "x", "y") }
+ };
+ var score = matcher.IsMatch(jObject).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Fact]
+ public void JsonMatcher_IsMatch_ArrayAsString_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new JsonMatcher("[ \"a\", \"b\", \"c\" ]", ignoreArrayOrder: true);
+
+ // Act
+ var jArray = new JArray
+ {
+ "c",
+ "b",
+ "a"
+ };
+ var score = matcher.IsMatch(jArray).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Fact]
+ public void JsonMatcher_IsMatch_ArrayOfNumbers_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new JsonMatcher(new[] { 1, 2, 3 }, ignoreArrayOrder: true);
+
+ // Act
+ var jArray = new JArray
+ {
+ 3,
+ 1,
+ 2
+ };
+ var score = matcher.IsMatch(jArray).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Theory]
+ [InlineData(MatchBehaviour.AcceptOnMatch, false, false, false)]
+ [InlineData(MatchBehaviour.AcceptOnMatch, true, false, true)]
+ [InlineData(MatchBehaviour.RejectOnMatch, true, true, false)]
+ public void JsonMatcher_GetCSharpCodeArguments_ShouldIncludeAllConstructorArguments(MatchBehaviour matchBehaviour, bool ignoreCase, bool regex, bool ignoreArrayOrder)
+ {
+ // Assign
+ var matcher = new JsonMatcher(matchBehaviour, "{ \"id\": 1 }", ignoreCase, regex, ignoreArrayOrder);
+
+ // Act
+ var result = matcher.GetCSharpCodeArguments();
+
+ // Assert
+ result.Should().StartWith($"new JsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
+ result.Should().EndWith($", {ignoreCase.ToString().ToLowerInvariant()}, {regex.ToString().ToLowerInvariant()}, {ignoreArrayOrder.ToString().ToLowerInvariant()})");
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Matchers/SystemTextJsonMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/SystemTextJsonMatcherTests.cs
index 6a97284a..4a25aec4 100644
--- a/test/WireMock.Net.Tests/Matchers/SystemTextJsonMatcherTests.cs
+++ b/test/WireMock.Net.Tests/Matchers/SystemTextJsonMatcherTests.cs
@@ -367,4 +367,118 @@ public class SystemTextJsonMatcherTests
// Assert
Assert.Equal(1.0, match);
}
-}
+
+ [Fact]
+ public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
+
+ // Act
+ var jsonElement = JsonDocument.Parse("[ \"c\", \"a\", \"b\" ]").RootElement;
+ var score = matcher.IsMatch(jsonElement).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Fact]
+ public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderFalse_DifferentOrder_ShouldNotMatch()
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: false);
+
+ // Act
+ var jsonElement = JsonDocument.Parse("[ \"c\", \"a\", \"b\" ]").RootElement;
+ var score = matcher.IsMatch(jsonElement).Score;
+
+ // Assert
+ Assert.Equal(MatchScores.Mismatch, score);
+ }
+
+ [Fact]
+ public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_SameOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
+
+ // Act
+ var jsonElement = JsonDocument.Parse("[ \"a\", \"b\", \"c\" ]").RootElement;
+ var score = matcher.IsMatch(jsonElement).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Fact]
+ public void SystemTextJsonMatcher_IsMatch_Array_WithIgnoreArrayOrderTrue_DifferentLength_ShouldNotMatch()
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher(new[] { "a", "b", "c" }, ignoreArrayOrder: true);
+
+ // Act
+ var jsonElement = JsonDocument.Parse("[ \"a\", \"b\" ]").RootElement;
+ var score = matcher.IsMatch(jsonElement).Score;
+
+ // Assert
+ Assert.Equal(MatchScores.Mismatch, score);
+ }
+
+ [Fact]
+ public void SystemTextJsonMatcher_IsMatch_ObjectWithArray_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher(new { Items = new[] { "x", "y", "z" } }, ignoreArrayOrder: true);
+
+ // Act
+ var match = matcher.IsMatch("{ \"Items\" : [ \"z\", \"x\", \"y\" ] }").Score;
+
+ // Assert
+ Assert.Equal(1.0, match);
+ }
+
+ [Fact]
+ public void SystemTextJsonMatcher_IsMatch_ArrayAsString_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher("[ \"a\", \"b\", \"c\" ]", ignoreArrayOrder: true);
+
+ // Act
+ var jsonElement = JsonDocument.Parse("[ \"c\", \"b\", \"a\" ]").RootElement;
+ var score = matcher.IsMatch(jsonElement).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Fact]
+ public void SystemTextJsonMatcher_IsMatch_ArrayOfNumbers_WithIgnoreArrayOrderTrue_DifferentOrder_ShouldMatch()
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher(new[] { 1, 2, 3 }, ignoreArrayOrder: true);
+
+ // Act
+ var jsonElement = JsonDocument.Parse("[ 3, 1, 2 ]").RootElement;
+ var score = matcher.IsMatch(jsonElement).Score;
+
+ // Assert
+ Assert.Equal(1.0, score);
+ }
+
+ [Theory]
+ [InlineData(MatchBehaviour.AcceptOnMatch, false, false, false)]
+ [InlineData(MatchBehaviour.AcceptOnMatch, true, false, true)]
+ [InlineData(MatchBehaviour.RejectOnMatch, true, true, false)]
+ public void SystemTextJsonMatcher_GetCSharpCodeArguments_ShouldIncludeAllConstructorArguments(MatchBehaviour matchBehaviour, bool ignoreCase, bool regex, bool ignoreArrayOrder)
+ {
+ // Assign
+ var matcher = new SystemTextJsonMatcher(matchBehaviour, "{ \"id\": 1 }", ignoreCase, regex, ignoreArrayOrder);
+
+ // Act
+ var result = matcher.GetCSharpCodeArguments();
+
+ // Assert
+ result.Should().StartWith($"new SystemTextJsonMatcher(WireMock.Matchers.MatchBehaviour.{matchBehaviour},");
+ result.Should().EndWith($", {ignoreCase.ToString().ToLowerInvariant()}, {regex.ToString().ToLowerInvariant()}, {ignoreArrayOrder.ToString().ToLowerInvariant()})");
+ }
+}
\ No newline at end of file