diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 000000000..15dfda8c1
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,5 @@
+# Copilot Instructions
+
+## Project Guidelines
+- When running tests in this workspace, do not run tests for the net48 target framework.
+- When changing System.Text.Json code in this workspace, verify API availability for netstandard2.0 and netstandard2.1 instead of assuming newer APIs exist.
\ No newline at end of file
diff --git a/README.md b/README.md
index d3e86b6ac..b64b40b15 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
| | | |
| **WireMock.Net.Extensions.Routing** | [](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
| **WireMock.Net.Matchers.CSharpCode** | [](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
+| **WireMock.Net.Matchers.SystemTextJsonPath** | [](https://www.nuget.org/packages/WireMock.Net.Matchers.SystemTextJsonPath) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.SystemTextJsonPath)
| **WireMock.Net.OpenApiParser** | [](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
| **WireMock.Net.MimePart** | [](https://www.nuget.org/packages/WireMock.Net.MimePart) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.MimePart)
| **WireMock.Net.GraphQL** | [](https://www.nuget.org/packages/WireMock.Net.GraphQL) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.GraphQL)
@@ -76,7 +77,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
-🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL*, *WireMock.Net.ProtoBuf* and *WireMock.Net.OpenTelemetry*.
+🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL*, *WireMock.Net.ProtoBuf*, *WireMock.Net.OpenTelemetry* and *WireMock.Net.Matchers.SystemTextJsonPath*.
---
diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
index 6968b4034..3058ff0c9 100644
--- a/WireMock.Net Solution.sln
+++ b/WireMock.Net Solution.sln
@@ -1,4 +1,3 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.0.11205.157
@@ -150,6 +149,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TestWebApplica
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.RestClient.AwesomeAssertions", "src\WireMock.Net.RestClient.AwesomeAssertions\WireMock.Net.RestClient.AwesomeAssertions.csproj", "{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Matchers.SystemTextJsonPath", "src\WireMock.Net.Matchers.SystemTextJsonPath\WireMock.Net.Matchers.SystemTextJsonPath.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Service", "examples\WireMock.Net.Service\WireMock.Net.Service.csproj", "{7F0B2446-0363-4720-AF46-F47F83B557DC}"
EndProject
Global
@@ -822,6 +823,18 @@ Global
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x64.Build.0 = Release|Any CPU
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x86.ActiveCfg = Release|Any CPU
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x86.Build.0 = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.Build.0 = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -896,6 +909,7 @@ Global
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{3B05CC76-C3CB-8667-6B65-3129DFB25681} = {0BB8B634-407A-4610-A91F-11586990767A}
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/src/WireMock.Net.AwesomeAssertions/Assertions/WireMockAssertions.WithBody.cs b/src/WireMock.Net.AwesomeAssertions/Assertions/WireMockAssertions.WithBody.cs
index e4b7aa065..70d12a646 100644
--- a/src/WireMock.Net.AwesomeAssertions/Assertions/WireMockAssertions.WithBody.cs
+++ b/src/WireMock.Net.AwesomeAssertions/Assertions/WireMockAssertions.WithBody.cs
@@ -31,19 +31,19 @@ public AndConstraint WithBody(IStringMatcher matcher, string
}
[CustomAssertion]
- public AndConstraint WithBodyAsJson(object body, string because = "", params object[] becauseArgs)
+ public AndConstraint WithBodyAsJson(object body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
{
- return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
+ return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
}
[CustomAssertion]
- public AndConstraint WithBodyAsJson(string body, string because = "", params object[] becauseArgs)
+ public AndConstraint WithBodyAsJson(string body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
{
- return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
+ return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
}
[CustomAssertion]
- public AndConstraint WithBodyAsJson(IObjectMatcher matcher, string because = "", params object[] becauseArgs)
+ public AndConstraint WithBodyAsJson(IJsonMatcher matcher, string because = "", params object[] becauseArgs)
{
var (filter, condition) = BuildFilterAndCondition(r => r.BodyAsJson, matcher);
@@ -126,15 +126,44 @@ private AndConstraint ExecuteAssertionWithBodyAsIObjectMatch
private static string? FormatBody(object? body)
{
- return body switch
+ if (body == null)
{
- null => null,
- string str => str,
- AnyOf[] stringPatterns => FormatBodies(stringPatterns.Select(p => p.GetPattern())),
- byte[] bytes => $"byte[{bytes.Length}] {{...}}",
- JToken jToken => jToken.ToString(Formatting.None),
- _ => JToken.FromObject(body).ToString(Formatting.None)
- };
+ return null;
+ }
+
+ if (body is string str)
+ {
+ return str;
+ }
+
+ if (body is AnyOf[] stringPatterns)
+ {
+ return FormatBodies(stringPatterns.Select(p => p.GetPattern()));
+ }
+
+ if (body is byte[] bytes)
+ {
+ return $"byte[{bytes.Length}] {{...}}";
+ }
+
+ if (body is JToken jToken)
+ {
+ return jToken.ToString(Formatting.None);
+ }
+
+ // System.IO.FileNotFoundException : Could not load file or assembly 'System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
+ var typeName = body.GetType().FullName;
+ if (typeName == "System.Text.Json.JsonElement")
+ {
+ return ((dynamic)body).GetRawText();
+ }
+
+ if (typeName == "System.Text.Json.JsonDocument")
+ {
+ return ((dynamic)body).RootElement.GetRawText();
+ }
+
+ return JToken.FromObject(body).ToString(Formatting.None);
}
private static string? FormatBodies(IEnumerable