2

When submitting a JSON body to an MVC application I get the following exception during binding:

An item with the same key has already been added. 

This happens when a HTTP body has 2+ JSON properties that rely on case sensitivity for uniqueness. E.g.

{ "foo":1, "FOO":2 } 

The Content-Type must also be specified as application\json for the exception to occur.

I am not really sure what to do to stop this from happening. I tried a custom IModelBinder but this occurs before IModelBinder.BindModel(...) is even called. This all seems to happen in standard framework code before the request gets passed to me.

Full stack trace:

System.ArgumentException: An item with the same key has already been added. at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value) at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value) at System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext) at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext) at System.Web.Mvc.ControllerBase.get_ValueProvider() at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<BeginInvokeAction>b__19(AsyncCallback asyncCallback, Object asyncState) 

How do I prevent this from occuring given that the JSON payload is perfectly valid?

Edit:

I've read a little more and I now believe the problem is in the behaviour of the default IValueProvider rather than the binder. I think if a content type of application\json is specified then the value provider is automatically parsing the JSON properties into a (case insensitive) dictionary for use by the ModelBinder afterwards. This makes more sense given the stack trace I had.

I am still not 100% sure how to accommodate for this. Is writing a custom IValueProvider for this controller the only choice here?

10
  • What are you using for deserialization? Likely it has a setting for case (in)sensitivity. Commented Oct 25, 2014 at 0:55
  • Whatever the built in MVC 4 default is. I'm not handling the serialization myself - this is default behaviour passing JSON to an MVC action, and this is framework code that runs before any that I have added (it's in the stack trace, but I should update the question for clarity). Commented Oct 25, 2014 at 0:58
  • Check out this question: stackoverflow.com/questions/13274625/… Commented Oct 25, 2014 at 1:02
  • The DefaultModelBinder is not case sensitive. What you are doing is creating an object with 2 properties with identical names which is not valid when binding (try creating a model with 2 identical properties - it wont compile). Are you trying to post back a collection? Commented Oct 25, 2014 at 1:06
  • It's an API endpoint that takes a JSON payload including free form properties. An end user is submitting us JSON where the property names are the same except for case. Unfortunately, we need to support it. Commented Oct 25, 2014 at 1:10

1 Answer 1

2

If the Content-Type is not application\json then the FormValueProvider is used to build the dictionary which ignores case and combines the values as the following shows

[HttpPost] public ActionResult YourMethod(FormCollection form) { var fooA= Request.Form["foo"]; // returns "1,2" var fooB= Request.Form["FOO"]; // returns "1,2" var fooC= Request.Form["FoO"]; // returns "1,2" 

If the Content-Type is application\json then the JsonValueProviderFactory is used to build the dictionary which in turn uses JavaScriptSerializer() to deserialize the request but this throws an exception because of the duplicate (case-insensitive) keys. You could possibly write a custom JavaScriptConverter to handle this although I am not sure if that is appropriate, because Value Providers are used in conjunction with Model Binders to build a model(s) and your model cant contain both properties foo and FOO (unless perhaps if your intending to map foo and FOO to other known model properties).

You can read the values from the Request.InputStream

[HttpPost] public ActionResult YourMethod() { StreamReader reader = new StreamReader(Request.InputStream); string body = reader.ReadToEnd(); // returns "foo=1&FOO=2" 

and the use body .Split('&') to create an array of key/value pairs

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, though I don't think your answer is correct. HTTP headers are indeed case insensitive, but that has nothing to do with the HTTP body (which is where action parameters are parsed). The given JSON is a perfectly valid HTTP body. A body is free form with its structure indicated only by the Content-Type header.
OK, misunderstood. I'll update answer shortly showing how you can at least read the key/values of the JSON. I'm not sure that writing a custom ValueProvider for the controller is going to solve the issue because that's used by a ModelBinder to bind to your model, but your model cant have properties string FOO and string foo.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.