From e9ff9b3e3a33641698f69c521eb29927f82d4a37 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 11 Jul 2023 23:20:23 +0200 Subject: [PATCH 1/4] Fixed JsonPathMatcher to match nested objects --- src/WireMock.Net/Matchers/JSONPathMatcher.cs | 2 +- .../Matchers/JsonPathMatcherTests.cs | 51 +++++++++++++++---- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/WireMock.Net/Matchers/JSONPathMatcher.cs b/src/WireMock.Net/Matchers/JSONPathMatcher.cs index 1e6be6f5a..b407b1223 100644 --- a/src/WireMock.Net/Matchers/JSONPathMatcher.cs +++ b/src/WireMock.Net/Matchers/JSONPathMatcher.cs @@ -121,6 +121,6 @@ public double IsMatch(object? input) private double IsMatch(JToken jToken) { - return MatchScores.ToScore(_patterns.Select(pattern => jToken.SelectToken(pattern.GetPattern()) != null).ToArray(), MatchOperator); + return MatchScores.ToScore(_patterns.Select(pattern => jToken.SelectTokens(pattern.GetPattern()) != null).ToArray(), MatchOperator); } } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs index 46f06c620..4df387eea 100644 --- a/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs +++ b/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs @@ -11,7 +11,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_GetName() { - // Assign + // Arrange var matcher = new JsonPathMatcher("X"); // Act @@ -24,7 +24,7 @@ public void JsonPathMatcher_GetName() [Fact] public void JsonPathMatcher_GetPatterns() { - // Assign + // Arrange var matcher = new JsonPathMatcher("X"); // Act @@ -37,7 +37,7 @@ public void JsonPathMatcher_GetPatterns() [Fact] public void JsonPathMatcher_IsMatch_ByteArray() { - // Assign + // Arrange var bytes = EmptyArray.Value; var matcher = new JsonPathMatcher(""); @@ -51,7 +51,7 @@ public void JsonPathMatcher_IsMatch_ByteArray() [Fact] public void JsonPathMatcher_IsMatch_NullString() { - // Assign + // Arrange string? s = null; var matcher = new JsonPathMatcher(""); @@ -65,7 +65,7 @@ public void JsonPathMatcher_IsMatch_NullString() [Fact] public void JsonPathMatcher_IsMatch_NullObject() { - // Assign + // Arrange object? o = null; var matcher = new JsonPathMatcher(""); @@ -79,7 +79,7 @@ public void JsonPathMatcher_IsMatch_NullObject() [Fact] public void JsonPathMatcher_IsMatch_String_Exception_Mismatch() { - // Assign + // Arrange var matcher = new JsonPathMatcher("xxx"); // Act @@ -92,7 +92,7 @@ public void JsonPathMatcher_IsMatch_String_Exception_Mismatch() [Fact] public void JsonPathMatcher_IsMatch_Object_Exception_Mismatch() { - // Assign + // Arrange var matcher = new JsonPathMatcher(""); // Act @@ -105,7 +105,7 @@ public void JsonPathMatcher_IsMatch_Object_Exception_Mismatch() [Fact] public void JsonPathMatcher_IsMatch_AnonymousObject() { - // Assign + // Arrange var matcher = new JsonPathMatcher("$..[?(@.Id == 1)]"); // Act @@ -115,10 +115,39 @@ public void JsonPathMatcher_IsMatch_AnonymousObject() Check.That(match).IsEqualTo(1); } + // - https://github.com/WireMock-Net/WireMock.Net/issues/965 + // - https://stackoverflow.com/questions/66922188/newtonsoft-jsonpath-with-c-sharp-syntax + [Fact] + public void JsonPathMatcher_IsMatch_AnonymousObject_WithNestedObject() + { + // Arrange + var matcher = new JsonPathMatcher("$.things[?(@.name == 'x')]"); + + // Act + double match = matcher.IsMatch(new { things = new { name = "x" } }); + + // Assert + Check.That(match).IsEqualTo(1); + } + + [Fact] + public void JsonPathMatcher_IsMatch_String_WithNestedObject() + { + // Arrange + var json = "{ \"things\": { \"name\": \"x\" } }"; + var matcher = new JsonPathMatcher("$.things[?(@.name == 'x')]"); + + // Act + double match = matcher.IsMatch(json); + + // Assert + Check.That(match).IsEqualTo(1); + } + [Fact] public void JsonPathMatcher_IsMatch_JObject() { - // Assign + // Arrange string[] patterns = { "$..[?(@.Id == 1)]" }; var matcher = new JsonPathMatcher(patterns); @@ -137,7 +166,7 @@ public void JsonPathMatcher_IsMatch_JObject() [Fact] public void JsonPathMatcher_IsMatch_JObject_Parsed() { - // Assign + // Arrange var matcher = new JsonPathMatcher("$..[?(@.Id == 1)]"); // Act @@ -150,7 +179,7 @@ public void JsonPathMatcher_IsMatch_JObject_Parsed() [Fact] public void JsonPathMatcher_IsMatch_RejectOnMatch() { - // Assign + // Arrange var matcher = new JsonPathMatcher(MatchBehaviour.RejectOnMatch, false, MatchOperator.Or, "$..[?(@.Id == 1)]"); // Act From c87c59301d36ac974f205463d811e4c0d38e2e56 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 12 Jul 2023 21:02:14 +0200 Subject: [PATCH 2/4] fix --- src/WireMock.Net/Matchers/JSONPathMatcher.cs | 23 ++++++++++++++++++- .../Matchers/JsonPathMatcherTests.cs | 16 +++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/WireMock.Net/Matchers/JSONPathMatcher.cs b/src/WireMock.Net/Matchers/JSONPathMatcher.cs index b407b1223..7f5dda757 100644 --- a/src/WireMock.Net/Matchers/JSONPathMatcher.cs +++ b/src/WireMock.Net/Matchers/JSONPathMatcher.cs @@ -121,6 +121,27 @@ public double IsMatch(object? input) private double IsMatch(JToken jToken) { - return MatchScores.ToScore(_patterns.Select(pattern => jToken.SelectTokens(pattern.GetPattern()) != null).ToArray(), MatchOperator); + // https://github.com/WireMock-Net/WireMock.Net/issues/965 + // Filtering using SelectToken() isn't guaranteed to work for objects inside objects -- only objects inside arrays. + // So this code checks if it's an JArray, if it's not an array, construct a new JArray. + JToken newJToken; + if (jToken is JArray) + { + newJToken = (JArray)jToken; + } + else if (jToken.Count() == 1) + { + var item = jToken.First(); + newJToken = new JObject + { + [item.Path] = new JArray(item.First()) + }; + } + else + { + newJToken = jToken; + } + + return MatchScores.ToScore(_patterns.Select(pattern => newJToken.SelectToken(pattern.GetPattern())?.Any() == true).ToArray(), MatchOperator); } } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs index 4df387eea..d0a8e46c3 100644 --- a/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs +++ b/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs @@ -115,8 +115,6 @@ public void JsonPathMatcher_IsMatch_AnonymousObject() Check.That(match).IsEqualTo(1); } - // - https://github.com/WireMock-Net/WireMock.Net/issues/965 - // - https://stackoverflow.com/questions/66922188/newtonsoft-jsonpath-with-c-sharp-syntax [Fact] public void JsonPathMatcher_IsMatch_AnonymousObject_WithNestedObject() { @@ -144,6 +142,20 @@ public void JsonPathMatcher_IsMatch_String_WithNestedObject() Check.That(match).IsEqualTo(1); } + [Fact] + public void JsonPathMatcher_IsNoMatch_String_WithNestedObject() + { + // Arrange + var json = "{ \"things\": { \"name\": \"y\" } }"; + var matcher = new JsonPathMatcher("$.things[?(@.name == 'x')]"); + + // Act + double match = matcher.IsMatch(json); + + // Assert + Check.That(match).IsEqualTo(0); + } + [Fact] public void JsonPathMatcher_IsMatch_JObject() { From 97bcf6d887399968113c70c81d5a56d8991711ee Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 12 Jul 2023 21:04:35 +0200 Subject: [PATCH 3/4] . --- src/WireMock.Net/Matchers/JSONPathMatcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WireMock.Net/Matchers/JSONPathMatcher.cs b/src/WireMock.Net/Matchers/JSONPathMatcher.cs index 7f5dda757..7def58f4d 100644 --- a/src/WireMock.Net/Matchers/JSONPathMatcher.cs +++ b/src/WireMock.Net/Matchers/JSONPathMatcher.cs @@ -122,6 +122,7 @@ public double IsMatch(object? input) private double IsMatch(JToken jToken) { // https://github.com/WireMock-Net/WireMock.Net/issues/965 + // https://stackoverflow.com/questions/66922188/newtonsoft-jsonpath-with-c-sharp-syntax // Filtering using SelectToken() isn't guaranteed to work for objects inside objects -- only objects inside arrays. // So this code checks if it's an JArray, if it's not an array, construct a new JArray. JToken newJToken; From 74f5b8ba14da0f1314c840822e6eeada7662d115 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 12 Jul 2023 21:19:17 +0200 Subject: [PATCH 4/4] 100% --- src/WireMock.Net/Matchers/JSONPathMatcher.cs | 40 +++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/WireMock.Net/Matchers/JSONPathMatcher.cs b/src/WireMock.Net/Matchers/JSONPathMatcher.cs index 7def58f4d..fda66f49e 100644 --- a/src/WireMock.Net/Matchers/JSONPathMatcher.cs +++ b/src/WireMock.Net/Matchers/JSONPathMatcher.cs @@ -121,28 +121,32 @@ public double IsMatch(object? input) private double IsMatch(JToken jToken) { - // https://github.com/WireMock-Net/WireMock.Net/issues/965 - // https://stackoverflow.com/questions/66922188/newtonsoft-jsonpath-with-c-sharp-syntax - // Filtering using SelectToken() isn't guaranteed to work for objects inside objects -- only objects inside arrays. - // So this code checks if it's an JArray, if it's not an array, construct a new JArray. - JToken newJToken; - if (jToken is JArray) - { - newJToken = (JArray)jToken; - } - else if (jToken.Count() == 1) + var array = ConvertJTokenToJArrayIfNeeded(jToken); + + return MatchScores.ToScore(_patterns.Select(pattern => array.SelectToken(pattern.GetPattern())?.Any() == true).ToArray(), MatchOperator); + } + + // https://github.com/WireMock-Net/WireMock.Net/issues/965 + // https://stackoverflow.com/questions/66922188/newtonsoft-jsonpath-with-c-sharp-syntax + // Filtering using SelectToken() isn't guaranteed to work for objects inside objects -- only objects inside arrays. + // So this code checks if it's an JArray, if it's not an array, construct a new JArray. + private static JToken ConvertJTokenToJArrayIfNeeded(JToken jToken) + { + if (jToken.Count() == 1) { - var item = jToken.First(); - newJToken = new JObject + var property = jToken.First(); + var item = property.First(); + if (item is JArray) + { + return jToken; + } + + return new JObject { - [item.Path] = new JArray(item.First()) + [property.Path] = new JArray(item) }; } - else - { - newJToken = jToken; - } - return MatchScores.ToScore(_patterns.Select(pattern => newJToken.SelectToken(pattern.GetPattern())?.Any() == true).ToArray(), MatchOperator); + return jToken; } } \ No newline at end of file