I can't bind JSON to Dictionary<string,string>
in Nancy.
This route:
Get["testGet"] = _ =>
{
var dictionary = new Dictionary<string, string>
{
{"hello", "world"},
{"foo", "bar"}
};
return Response.AsJson(dictionary);
};
returns the following JSON, as expected:
{
"hello": "world",
"foo": "bar"
}
When I try and post this exact JSON back to this route:
Post["testPost"] = _ =>
{
var data = this.Bind<Dictionary<string, string>>();
return null;
};
I get the exception:
The value "[Hello, world]" is not of type "System.String" and cannot be used in this generic collection.
Is it possible to bind to Dictionary<string,string>
using Nancys default model binding and, if so, what am I doing wrong here?
Nancy doesn't have a built-in converter for dictionaries. Because of this you'd need to use BindTo<T>()
like so
var data = this.BindTo(new Dictionary<string, string>());
which will use the CollectionConverter
. The issue with doing it like this is it will only add string values, so if you send
{
"hello": "world",
"foo": 123
}
your result will only contain the key hello
.
If you want to capture all the values as strings, even if they aren't supplied as such, then you'll need to use a custom IModelBinder
.
This will convert all the values to strings and return a Dictionary<string, string>
.
public class StringDictionaryBinder : IModelBinder
{
public object Bind(NancyContext context, Type modelType, object instance, BindingConfig configuration, params string[] blackList)
{
var result = (instance as Dictionary<string, string>) ?? new Dictionary<string, string>();
IDictionary<string, object> formData = (DynamicDictionary) context.Request.Form;
foreach (var item in formData)
{
var itemValue = Convert.ChangeType(item.Value, typeof (string)) as string;
result.Add(item.Key, itemValue);
}
return result;
}
public bool CanBind(Type modelType)
{
// http://stackoverflow.com/a/16956978/39605
if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof (Dictionary<,>))
{
if (modelType.GetGenericArguments()[0] == typeof (string) &&
modelType.GetGenericArguments()[1] == typeof (string))
{
return true;
}
}
return false;
}
}
Nancy will auto register this for you and you can bind your models as you normally would.
var data1 = this.Bind<Dictionary<string, string>>();
var data2 = this.BindTo(new Dictionary<string, string>());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With