I am using Newtonsoft JSON in my MVC based off the example cited here: Setting the Default JSON Serializer in ASP.NET MVC.
My data contract class looks something like:
[DataContract]
public MyContractClass
{
public MyContractClass()
{
this.ThisPropertyFails = new List<ClassDefinedInAnotherAssembly>();
}
[DataMember(EmitDefaultValue = false, Name = "thisPropertyIsFine")]
public string ThisPropertyIsFine { get; set; }
[DataMember(EmitDefaultValue = false, Name = "thisPropertyFails")]
public IList<ClassDefinedInAnotherAssembly> ThisPropertyFails { get; internal set; }
}
The code I'm specifically using to de-serialize looks like this:
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
return null;
}
var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
var bodyText = reader.ReadToEnd();
if (String.IsNullOrEmpty(bodyText))
{
return null;
}
else
{
JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new StringEnumConverter());
serializerSettings.Converters.Add(new ExpandoObjectConverter());
DictionaryValueProvider<object> result = new DictionaryValueProvider<object>(JsonConvert.DeserializeObject<ExpandoObject>(bodyText, serializerSettings), CultureInfo.CurrentCulture);
return result;
}
//return String.IsNullOrEmpty(bodyText) ? null : new DictionaryValueProvider<object>(JsonConvert.DeserializeObject<ExpandoObject>(bodyText, new ExpandoObjectConverter(), new StringEnumConverter()), CultureInfo.InvariantCulture);
}
However, in the MVC action, ModelState.IsValid is false, and looking at the errors, I see this:
{"The parameter conversion from type 'System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' to type 'OtherAssembly.ClassDefinedInAnotherAssembly' failed because no type converter can convert between these types."}
Does anyone have any idea what is going on here? This same class works fine with my WebApi project (which is 'OtherAssembly' in this example).
Edit #1
Using the code directly, with the known type, does indeed work. So it's something to do with properties under ExpandoObject. For example, this code:
JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new StringEnumConverter());
serializerSettings.Converters.Add(new ExpandoObjectConverter());
JsonSerializer serializer = JsonSerializer.Create(serializerSettings);
using (StreamReader streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream))
{
using (JsonReader jsonReader = new JsonTextReader(streamReader))
{
var resultAbc = serializer.Deserialize(jsonReader, typeof(MyContractClass));
}
}
Works just fine.
Edit #2
It appears I'm not the only person to have this issue. Anyone using MVC and using the oft-cited source code to use Newtonsoft makes it impossible to de-serialize complex sub-properties: http://tech.pro/q/34/using-jsonnet-to-deserialize-incoming-json-in-aspnet-mvc
No idea why that code is so popular if it doesn't even work after 1 level in the contract?