I am having a heck of a time with this one. I must be missing something.
I am posting a complex model via Angular/JSON to a .NET controller. When posting it back to the server the DefaultModelBinder only partially binds to the model (the simple values such as int (ID) and string (title) are fine). The "ListItems" dataset seems to be ignored as well as the "ProductsList" datatable which are part of the ProjectDetails model. From what I am reading, for complex objects the DefaultModelBinder takes a second pass at the JSON recursively and then maps the objects that it can find. I have tried a number of solutions that I found through searching StackOverflow but to no avail. I think I have just lost my perspective at this point. Here is what I have. Any help would be greatly appreciated.
The Model
Public Class ProjectDetails
Private _ListItems As DataSet
<JsonProperty("ListItems")> _
Public Property ListItems() As DataSet
Get
Return _ListItems
End Get
Set(ByVal value As DataSet)
_ListItems = value
End Set
End Property
Private _ProductsList As DataTable
Public Property ProductsList As DataTable
Get
Return _ProductsList
End Get
Set(value As DataTable)
_ProductsList = value
End Set
End Property
Private _imageID As Int32 = 0
Public Property imageID() As Int32
Get
Return _imageID
End Get
Set(ByVal value As Int32)
_imageID = value
End Set
End Property
Private _Title As String = String.Empty
Public Property Title() As String
Get
Return _Title
End Get
Set(ByVal value As String)
_Title = value
End Set
End Property
End Class
The Action Controller
<Authorize()> _
<AcceptVerbs(HttpVerbs.Post)> _
<ValidateInput(False)> _
Async Function ProjectUpdate(ByVal d As ProjectDetails) As Task(Of JsonResult)
'send results to database here
Save(d)
return json(true)
End Function
Header
POST http://localhost:51110/projectUpdate HTTP/1.1
Host: localhost:51110
Connection: keep-alive
Content-Length: 10119
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36
Content-Type: application/json;charset=UTF-8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
JSON data posted
{"ListItems":
{"ProjectStatusCodes":[{"id":1,"ProjectStatusTx":"Stage1 - Under Construction"},{"id":3,"ProjectStatusTx":"Stage3 - Project Released"},{"id":4,"ProjectStatusTx":"Project Closed"}],
"ProductsList":[
{"id":2336,"Name":"Product1","Description":"This is a description","$$hashKey":"00K"},
{"id":2337,"Name":"Product2","Description":"This is a second description","$$hashKey":"00M"}],
"imageID":345,
"Title":"books", }
}
AngularJS code
$scope.update = function (formData) {
$http({
method: 'POST',
url: '/projectbuilder/projectUpdate',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify($scope.formData)
}).success(function (data, status, headers, config) {
$scope.codeStatus = status;
}).error(function (data, status, headers, config) {
$scope.codeStatus = status || "Request failed";
});
HTML template with form values
<div ng-repeat="ProductsList in formData.ListItems.ProductsList">
<div>
<div>
<div>
<input value="{{ProductsList.Title}}" name="ProductsList.Title" ID="ProductsList.Title" ng-model="ProductsList.Title" >
<br />
<input value="{{ProductsList.ImageID}}" name="ProductsList.ImageID" ID="ProductsList.ImageID" ng-model="ProductsList.ImageID" >
</div>
</div>
</div>
Behavior The JSON post works fine to the controller and it binds the ImageID and Title. The ListItems dataset contains no tables and the ProductsList datatable in the ListItems dataset does not exist.
NOTE: The ProjectStatusCodes data in the JSON is not mapped back to the model as it was used only for display purposes on the form.
OK. I figured it out. It seems as if JSON.NET does a great job of serializing the object when sending making sure that it is fully JSON compliant. The bad news is that it removes most of the things that make a datatable de-serializable when sending it back to the .NET application.
What I ended up doing was catching the JSON string on postback and accessing the serialized datatable data separately using the JSON.NET fragment serialization which you can find here. http://www.newtonsoft.com/json/help/html/SerializingJSONFragments.htm I left the rest of the object "as is" and grabbed the datatable from the JSON string and did a de-serialization manually with this technique. The example in the article was very informative.
CODE TO READ THE JSON STRING
Dim req As Stream = Request.InputStream
req.Seek(0, System.IO.SeekOrigin.Begin)
Dim jsonstring As String = New StreamReader(req).ReadToEnd()
CODE TO PARSE IT OUT AND DESERIALIZE
Dim ProductsList As JObject = JObject.Parse(jsonstring)
Dim results As IList(Of JToken) = ProductsList("ListItems")("ProductsList").Children().ToList()
Console.Write(results)
Not terribly elegant but it works.