diff --git a/examples/WireMock.Net.Console.NET6/WireMock.Net.Console.NET6.csproj b/examples/WireMock.Net.Console.NET6/WireMock.Net.Console.NET6.csproj
index 3dc9ff10..32d059a3 100644
--- a/examples/WireMock.Net.Console.NET6/WireMock.Net.Console.NET6.csproj
+++ b/examples/WireMock.Net.Console.NET6/WireMock.Net.Console.NET6.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
index 6fe7dc9c..d7932f4f 100644
--- a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using WireMock.Models;
namespace WireMock.Admin.Mappings;
@@ -84,4 +85,13 @@ public class MappingModel
/// Fire and forget for webhooks.
///
public bool? UseWebhooksFireAndForget { get; set; }
+
+ ///
+ /// Data Object which can be used when WithTransformer is used.
+ /// e.g. lookup an path in this object using
+ ///
+ /// lookup data "1"
+ ///
+ ///
+ public object? Data { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs
index 40609b7f..d0ff2309 100644
--- a/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs
@@ -1,121 +1,120 @@
using System.Collections.Generic;
-namespace WireMock.Admin.Mappings
+namespace WireMock.Admin.Mappings;
+
+///
+/// ResponseModel
+///
+[FluentBuilder.AutoGenerateBuilder]
+public class ResponseModel
{
///
- /// ResponseModel
+ /// Gets or sets the HTTP status.
///
- [FluentBuilder.AutoGenerateBuilder]
- public class ResponseModel
- {
- ///
- /// Gets or sets the HTTP status.
- ///
- public object? StatusCode { get; set; }
+ public object? StatusCode { get; set; }
- ///
- /// Gets or sets the body destination (SameAsSource, String or Bytes).
- ///
- public string? BodyDestination { get; set; }
+ ///
+ /// Gets or sets the body destination (SameAsSource, String or Bytes).
+ ///
+ public string? BodyDestination { get; set; }
- ///
- /// Gets or sets the body.
- ///
- public string? Body { get; set; }
+ ///
+ /// Gets or sets the body.
+ ///
+ public string? Body { get; set; }
- ///
- /// Gets or sets the body (as JSON object).
- ///
- public object? BodyAsJson { get; set; }
+ ///
+ /// Gets or sets the body (as JSON object).
+ ///
+ 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.
- ///
- public bool? BodyAsJsonIndented { 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.
+ ///
+ public bool? BodyAsJsonIndented { get; set; }
- ///
- /// Gets or sets the body (as bytearray).
- ///
- public byte[]? BodyAsBytes { get; set; }
+ ///
+ /// Gets or sets the body (as bytearray).
+ ///
+ public byte[]? BodyAsBytes { get; set; }
- ///
- /// Gets or sets the body as a file.
- ///
- public string? BodyAsFile { get; set; }
+ ///
+ /// Gets or sets the body as a file.
+ ///
+ public string? BodyAsFile { get; set; }
- ///
- /// Is the body as file cached?
- ///
- public bool? BodyAsFileIsCached { get; set; }
+ ///
+ /// Is the body as file cached?
+ ///
+ public bool? BodyAsFileIsCached { get; set; }
- ///
- /// Gets or sets the body encoding.
- ///
- public EncodingModel? BodyEncoding { get; set; }
+ ///
+ /// Gets or sets the body encoding.
+ ///
+ public EncodingModel? BodyEncoding { get; set; }
- ///
- /// Use ResponseMessage Transformer.
- ///
- public bool? UseTransformer { get; set; }
+ ///
+ /// Use ResponseMessage Transformer.
+ ///
+ public bool? UseTransformer { get; set; }
- ///
- /// Gets the type of the transformer.
- ///
- public string? TransformerType { get; set; }
+ ///
+ /// Gets the type of the transformer.
+ ///
+ public string? TransformerType { get; set; }
- ///
- /// Use the Handlebars transformer for the content from the referenced BodyAsFile.
- ///
- public bool? UseTransformerForBodyAsFile { get; set; }
+ ///
+ /// Use the Handlebars transformer for the content from the referenced BodyAsFile.
+ ///
+ public bool? UseTransformerForBodyAsFile { get; set; }
- ///
- /// The ReplaceNodeOptions to use when transforming a JSON node.
- ///
- public string? TransformerReplaceNodeOptions { get; set; }
+ ///
+ /// The ReplaceNodeOptions to use when transforming a JSON node.
+ ///
+ public string? TransformerReplaceNodeOptions { get; set; }
- ///
- /// Gets or sets the headers.
- ///
- public IDictionary? Headers { get; set; }
+ ///
+ /// Gets or sets the headers.
+ ///
+ public IDictionary? Headers { get; set; }
- ///
- /// Gets or sets the Headers (Raw).
- ///
- public string? HeadersRaw { get; set; }
+ ///
+ /// Gets or sets the Headers (Raw).
+ ///
+ public string? HeadersRaw { get; set; }
- ///
- /// Gets or sets the delay in milliseconds.
- ///
- public int? Delay { get; set; }
+ ///
+ /// Gets or sets the delay in milliseconds.
+ ///
+ public int? Delay { get; set; }
- ///
- /// Gets or sets the minimum random delay in milliseconds.
- ///
- public int? MinimumRandomDelay { get; set; }
+ ///
+ /// Gets or sets the minimum random delay in milliseconds.
+ ///
+ public int? MinimumRandomDelay { get; set; }
- ///
- /// Gets or sets the maximum random delay in milliseconds.
- ///
- public int? MaximumRandomDelay { get; set; }
+ ///
+ /// Gets or sets the maximum random delay in milliseconds.
+ ///
+ public int? MaximumRandomDelay { get; set; }
- ///
- /// Gets or sets the Proxy URL.
- ///
- public string? ProxyUrl { get; set; }
+ ///
+ /// Gets or sets the Proxy URL.
+ ///
+ public string? ProxyUrl { get; set; }
- ///
- /// The client X509Certificate2 Thumbprint or SubjectName to use.
- ///
- public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
+ ///
+ /// The client X509Certificate2 Thumbprint or SubjectName to use.
+ ///
+ public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
- ///
- /// Gets or sets the fault.
- ///
- public FaultModel? Fault { get; set; }
+ ///
+ /// Gets or sets the fault.
+ ///
+ public FaultModel? Fault { get; set; }
- ///
- /// Gets or sets the WebProxy settings.
- ///
- public WebProxyModel? WebProxy { get; set; }
- }
+ ///
+ /// Gets or sets the WebProxy settings.
+ ///
+ public WebProxyModel? WebProxy { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Abstractions/IResponseMessage.cs b/src/WireMock.Net.Abstractions/IResponseMessage.cs
index b5faa4cc..b1499b1a 100644
--- a/src/WireMock.Net.Abstractions/IResponseMessage.cs
+++ b/src/WireMock.Net.Abstractions/IResponseMessage.cs
@@ -44,7 +44,7 @@ public interface IResponseMessage
/// Gets or sets the status code.
///
object? StatusCode { get; }
-
+
///
/// Adds the header.
///
diff --git a/src/WireMock.Net/IMapping.cs b/src/WireMock.Net/IMapping.cs
index c657b6bc..86f35dd5 100644
--- a/src/WireMock.Net/IMapping.cs
+++ b/src/WireMock.Net/IMapping.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using WireMock.Matchers.Request;
using WireMock.Models;
@@ -120,7 +121,16 @@ public interface IMapping
///
/// Use Fire and Forget for the defined webhook(s). [Optional]
///
- public bool? UseWebhooksFireAndForget { get; set; }
+ bool? UseWebhooksFireAndForget { get; set; }
+
+ ///
+ /// Data Object which can be used when WithTransformer is used.
+ /// e.g. lookup an path in this object using
+ ///
+ /// lookup data "1"
+ ///
+ ///
+ object? Data { get; set; }
///
/// ProvideResponseAsync
diff --git a/src/WireMock.Net/Json/DynamicJsonClassOptions.cs b/src/WireMock.Net/Json/DynamicJsonClassOptions.cs
new file mode 100644
index 00000000..6d090b3d
--- /dev/null
+++ b/src/WireMock.Net/Json/DynamicJsonClassOptions.cs
@@ -0,0 +1,10 @@
+// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
+
+namespace WireMock.Json;
+
+internal class DynamicJsonClassOptions
+{
+ public IntegerBehavior IntegerConvertBehavior { get; set; } = IntegerBehavior.UseLong;
+
+ public FloatBehavior FloatConvertBehavior { get; set; } = FloatBehavior.UseDouble;
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Json/DynamicPropertyWithValue.cs b/src/WireMock.Net/Json/DynamicPropertyWithValue.cs
new file mode 100644
index 00000000..55219734
--- /dev/null
+++ b/src/WireMock.Net/Json/DynamicPropertyWithValue.cs
@@ -0,0 +1,15 @@
+// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
+
+using System.Linq.Dynamic.Core;
+
+namespace WireMock.Json;
+
+public class DynamicPropertyWithValue : DynamicProperty
+{
+ public object? Value { get; }
+
+ public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object))
+ {
+ Value = value;
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Json/FloatBehavior.cs b/src/WireMock.Net/Json/FloatBehavior.cs
new file mode 100644
index 00000000..0bfa8100
--- /dev/null
+++ b/src/WireMock.Net/Json/FloatBehavior.cs
@@ -0,0 +1,24 @@
+// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
+
+namespace WireMock.Json;
+
+///
+/// Enum to define how to convert an Float in the Json Object.
+///
+internal enum FloatBehavior
+{
+ ///
+ /// Convert all Float types in the Json Object to a double. (default)
+ ///
+ UseDouble = 0,
+
+ ///
+ /// Convert all Float types in the Json Object to a float (unless overflow).
+ ///
+ UseFloat = 1,
+
+ ///
+ /// Convert all Float types in the Json Object to a decimal (unless overflow).
+ ///
+ UseDecimal = 2
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Json/IntegerBehavior.cs b/src/WireMock.Net/Json/IntegerBehavior.cs
new file mode 100644
index 00000000..6258abfc
--- /dev/null
+++ b/src/WireMock.Net/Json/IntegerBehavior.cs
@@ -0,0 +1,20 @@
+// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
+
+namespace WireMock.Json;
+
+///
+/// Enum to define how to convert an Integer in the Json Object.
+///
+internal enum IntegerBehavior
+{
+ ///
+ /// Convert all Integer types in the Json Object to a int (unless overflow).
+ /// (default)
+ ///
+ UseInt = 0,
+
+ ///
+ /// Convert all Integer types in the Json Object to a long.
+ ///
+ UseLong = 1
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Json/JObjectExtensions.cs b/src/WireMock.Net/Json/JObjectExtensions.cs
new file mode 100644
index 00000000..1473a489
--- /dev/null
+++ b/src/WireMock.Net/Json/JObjectExtensions.cs
@@ -0,0 +1,202 @@
+// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Dynamic.Core;
+using System.Reflection;
+using Newtonsoft.Json.Linq;
+
+namespace WireMock.Json;
+
+internal static class JObjectExtensions
+{
+ private class JTokenResolvers : Dictionary>
+ {
+ }
+
+ private static readonly JTokenResolvers Resolvers = new()
+ {
+ { JTokenType.Array, ConvertJTokenArray },
+ { JTokenType.Boolean, (jToken, _) => jToken.Value() },
+ { JTokenType.Bytes, (jToken, _) => jToken.Value() },
+ { JTokenType.Date, (jToken, _) => jToken.Value() },
+ { JTokenType.Float, ConvertJTokenFloat },
+ { JTokenType.Guid, (jToken, _) => jToken.Value() },
+ { JTokenType.Integer, ConvertJTokenInteger },
+ { JTokenType.None, (_, _) => null },
+ { JTokenType.Null, (_, _) => null },
+ { JTokenType.Object, ConvertJObject },
+ { JTokenType.Property, ConvertJTokenProperty },
+ { JTokenType.String, (jToken, _) => jToken.Value() },
+ { JTokenType.TimeSpan, (jToken, _) => jToken.Value() },
+ { JTokenType.Undefined, (_, _) => null },
+ { JTokenType.Uri, (o, _) => o.Value() },
+ };
+
+ internal static DynamicClass? ToDynamicJsonClass(this JObject? src, DynamicJsonClassOptions? options = null)
+ {
+ if (src == null)
+ {
+ return null;
+ }
+
+ var dynamicPropertyWithValues = new List();
+
+ foreach (var prop in src.Properties())
+ {
+ var value = Resolvers[prop.Type](prop.Value, options);
+ if (value != null)
+ {
+ dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Name, value));
+ }
+ }
+
+ return CreateInstance(dynamicPropertyWithValues);
+ }
+
+ internal static IEnumerable ToDynamicClassArray(this JArray? src, DynamicJsonClassOptions? options = null)
+ {
+ if (src == null)
+ {
+ return new object?[0];
+ }
+
+ return ConvertJTokenArray(src, options);
+ }
+
+ private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null)
+ {
+ if (arg is JObject asJObject)
+ {
+ return asJObject.ToDynamicJsonClass(options);
+ }
+
+ return GetResolverFor(arg)(arg, options);
+ }
+
+ private static object PassThrough(JToken arg, DynamicJsonClassOptions? options)
+ {
+ return arg;
+ }
+
+ private static Func GetResolverFor(JToken arg)
+ {
+ return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough;
+ }
+
+ private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null)
+ {
+ if (arg.Type != JTokenType.Float)
+ {
+ throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float.");
+ }
+
+ if (options?.FloatConvertBehavior == FloatBehavior.UseFloat)
+ {
+ try
+ {
+ return arg.Value();
+ }
+ catch
+ {
+ return arg.Value();
+ }
+ }
+
+ if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal)
+ {
+ try
+ {
+ return arg.Value();
+ }
+ catch
+ {
+ return arg.Value();
+ }
+ }
+
+
+ return arg.Value();
+ }
+
+ private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null)
+ {
+ if (arg.Type != JTokenType.Integer)
+ {
+ throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int.");
+ }
+
+ var longValue = arg.Value();
+
+ if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt)
+ {
+ if (longValue is >= int.MinValue and <= int.MaxValue)
+ {
+ return Convert.ToInt32(longValue);
+ }
+ }
+
+ return longValue;
+ }
+
+ private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null)
+ {
+ var resolver = GetResolverFor(arg);
+ if (resolver is null)
+ {
+ throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}.");
+ }
+
+ return resolver(arg, options);
+ }
+
+ private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null)
+ {
+ if (arg is not JArray array)
+ {
+ throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}.");
+ }
+
+ var result = new List
-
+
\ No newline at end of file