I don’t know why I’m just now noticing this behavior for the first time. Looking to confirm whether this is designed behavior, or whether I’m missing something.
Say I have a viewmodel that implements IEnumerable<T>, and provides additional properties. For example:
public class MyResultsViewModel : IEnumerable<MyResultViewModel>
{
public IEnumerable<MyResultViewModel> Results { get; set; }
public string SomeAdditionalProperty { get; set; }
public IEnumerator<MyResultViewModel> GetEnumerator()
{
return Results.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator; }
}
Say I also have a controller which returns this model as json. For example:
public ActionResult MyResults()
{
var entities = PrivateMethodToGetEntities();
var models = Mapper.Map<MyResultsViewModel>(entities);
models.SomeAdditionalProperty = "I want this in the JSON too";
return Json(models, JsonRequestBehavior.AllowGet);
// models now contains a populated Results as well as the add'l string prop
}
When I get the results back from a JSON request on the client, it always comes back as an array. For example:
$.get('/Path/To/MyResults')
.success(function (results) {
alert(results.SomeAdditionalProperty); // this alerts 'undefined'
alert(results.length); // this alerts the size / count of Results
alert(results[0]); // this alerts object
// inspecting results here shows that it is a pure array, with no add'l props
});
Before I refactor to make the viewmodel not implement IEnumerable<T>, I want to get some confirmation that this is by design and should be expected. I guess it makes sense, since the javascript array object’s prototype would have to be extended to accommodate the additional properties.
Update:
I’ve made the following changes to the viewmodel, to avoid naming the internal enumerable Results:
public class MyResultsViewModel : IEnumerable<MyResultViewModel>
{
public IEnumerable<MyResultViewModel> NotNamedResults { get; set; }
public string SomeAdditionalProperty { get; set; }
public IEnumerator<MyResultViewModel> GetEnumerator()
{
return NotNamedResults.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator; }
}
With this change, the behavior remains. alert(JSON.stringify(results)) yields normal array syntax for my enumerated collection of MyResultViewModels:
[{“ResultProp1″:”A”,”ResultProp2″:”AA”,”ResultProp3″:”AAA”},{“ResultProp1″:”B”,”ResultProp2″:”BB”,”ResultProp3″:”BBB”},{“ResultProp1″:”C”,”ResultProp2″:”CC”,”ResultProp3″:”CCC”},{“ResultProp1″:”D”,”ResultProp2″:”DD”,”ResultProp3″:”DDD”},{“ResultProp1″:”E”,”ResultProp2″:”EE”,”ResultProp3″:”EEE”}]
Is still seems that the additional property is being lost between when the controller action returns the JsonResult and the jquery success function is invoked.
For classes implementing IEnumerable, the
JavascriptSerializeronly serializes the enumerated items. It callsGetEnumerator()to get the data to be serialized, any other properties are ignored. That’s why theCountproperty of aList<T>is not serialized, nor will any other property.The reason is that this type of construction cannot be represented in json format. To include the other properties the serializer would have to create a hash object instead of an array, but a hash object is not a collection, strictly speaking. (The exception are key/value pair classes like
Dictionary, which are serialized as hash objects. But the rule stands – only the enumerated dictionary entries are serialized).But why are you creating a class that implements
IEnumerabledirectly to serialize as json instead of doing this?