From 98b8ede8261a7586b48e23fc815a0839cbad1a20 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 15 Jul 2023 09:29:13 +0200 Subject: [PATCH] Fixed JsonPathMatcher to match nested objects (#966) * Fixed JsonPathMatcher to match nested objects * fix * . * 100% --- src/WireMock.Net/Matchers/JSONPathMatcher.cs | 28 ++++++++- .../Matchers/JsonPathMatcherTests.cs | 63 +++++++++++++++---- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/WireMock.Net/Matchers/JSONPathMatcher.cs b/src/WireMock.Net/Matchers/JSONPathMatcher.cs index 1e6be6f5..fda66f49 100644 --- a/src/WireMock.Net/Matchers/JSONPathMatcher.cs +++ b/src/WireMock.Net/Matchers/JSONPathMatcher.cs @@ -121,6 +121,32 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher private double IsMatch(JToken jToken) { - return MatchScores.ToScore(_patterns.Select(pattern => jToken.SelectToken(pattern.GetPattern()) != null).ToArray(), MatchOperator); + 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 property = jToken.First(); + var item = property.First(); + if (item is JArray) + { + return jToken; + } + + return new JObject + { + [property.Path] = new JArray(item) + }; + } + + return jToken; } } \ 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 46f06c62..d0a8e46c 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 class JsonPathMatcherTests [Fact] public void JsonPathMatcher_GetPatterns() { - // Assign + // Arrange var matcher = new JsonPathMatcher("X"); // Act @@ -37,7 +37,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_ByteArray() { - // Assign + // Arrange var bytes = EmptyArray.Value; var matcher = new JsonPathMatcher(""); @@ -51,7 +51,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_NullString() { - // Assign + // Arrange string? s = null; var matcher = new JsonPathMatcher(""); @@ -65,7 +65,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_NullObject() { - // Assign + // Arrange object? o = null; var matcher = new JsonPathMatcher(""); @@ -79,7 +79,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_String_Exception_Mismatch() { - // Assign + // Arrange var matcher = new JsonPathMatcher("xxx"); // Act @@ -92,7 +92,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_Object_Exception_Mismatch() { - // Assign + // Arrange var matcher = new JsonPathMatcher(""); // Act @@ -105,7 +105,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_AnonymousObject() { - // Assign + // Arrange var matcher = new JsonPathMatcher("$..[?(@.Id == 1)]"); // Act @@ -115,10 +115,51 @@ public class JsonPathMatcherTests Check.That(match).IsEqualTo(1); } + [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_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() { - // Assign + // Arrange string[] patterns = { "$..[?(@.Id == 1)]" }; var matcher = new JsonPathMatcher(patterns); @@ -137,7 +178,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_JObject_Parsed() { - // Assign + // Arrange var matcher = new JsonPathMatcher("$..[?(@.Id == 1)]"); // Act @@ -150,7 +191,7 @@ public class JsonPathMatcherTests [Fact] public void JsonPathMatcher_IsMatch_RejectOnMatch() { - // Assign + // Arrange var matcher = new JsonPathMatcher(MatchBehaviour.RejectOnMatch, false, MatchOperator.Or, "$..[?(@.Id == 1)]"); // Act