Reputation: 1748
I have a complex JSON object that I'd like to pass to a MVC4 Controller route.
{
"name": "Test",
"description": "Description",
"questions": [
{
"id": "1",
"type": "1",
"text": "123",
"answers": [
{
"answer": "123",
"prerequisite": 0
},
{
"answer": "123",
"prerequisite": 0
}
],
"children": [
{
"id": "2",
"type": "2",
"text": "234",
"answers": [
{
"answer": "234",
"prerequisite": 0
},
{
"answer": "234",
"prerequisite": 0
}
],
"children": []
}
]
}
]
I have these ViewModels defined:
public class FormDataTransformContainer
{
public string name { get; set; }
public string description { get; set; }
public QuestionDataTransformContainer[] questions;
}
public class QuestionDataTransformContainer {
public int type { get; set; }
public string text { get; set; }
public AnswerDataTransformContainer[] answers { get; set; }
public QuestionDataTransformContainer[] children { get; set; }
}
public class AnswerDataTransformContainer {
public string answer { get; set; }
public int prerequisite { get; set; }
}
And this is the route I'm hitting:
[HttpPost]
public ActionResult Create(FormDataTransformContainer formData)
{
Currently, the name and description property on FormDataTransformContainer are set, but the questions array is null. I hoped that the Data Binding would figure it out, but I assume the tree nature of the data structure is a little complex for it. If I'm correct what is the best solution to this?
Upvotes: 2
Views: 2313
Reputation:
I a similar problem, solved with the following code:
public class ExtendedController : Controller
{
public T TryCreateModelFromJson<T>(string requestFormKey)
{
if (!this.Request.Form.AllKeys.Contains(requestFormKey))
{
throw new ArgumentException("Request form doesn't contain provided key.");
}
return
JsonConvert.DeserializeObject<T>(
this.Request.Form[requestFormKey]);
}
}
And usage:
[HttpPost]
[ActionName("EditAjax")]
public ActionResult EditAjaxPOST()
{
try
{
var viewModel =
this.TryCreateModelFromJson<MyModel>(
"viewModel");
this.EditFromModel(viewModel);
return
this.JsonResponse(
this.T("Model updated successfuly."),
true);
}
catch (Exception ex)
{
this.Logger.Error(ex, "Error while updating model.");
return this.JsonResponse(this.T("Error"), false);
}
}
Called from JS:
function saveViewModel() {
$.post(
'@Url.Action("EditAjax")',
{
__RequestVerificationToken: '@Html.AntiForgeryTokenValueOrchard()',
viewModel: ko.mapping.toJSON(viewModel)
},
function (data) {
// response
});
}
Used additional library for deserializing/serializing JSON: http://www.nuget.org/packages/Newtonsoft.Json
Upvotes: 0
Reputation: 56536
questions
should be a property, not a field. I'd also change from arrays to IList<>
(assuming your serialization library handles that well), because that's probably closer to what it should be, and lets you use a more generic interface instead of a specific implementation.
public class FormDataTransformContainer
{
public string name { get; set; }
public string description { get; set; }
public IList<QuestionDataTransformContainer> questions { get; set; }
}
public class QuestionDataTransformContainer {
public int type { get; set; }
public string text { get; set; }
public IList<AnswerDataTransformContainer> answers { get; set; }
public IList<QuestionDataTransformContainer> children { get; set; }
}
public class AnswerDataTransformContainer {
public string answer { get; set; }
public int prerequisite { get; set; }
}
I've tested this structure with Json.net (MVC4's default, I believe), and it works.
Upvotes: 3
Reputation: 10508
As @robert-harvey said, you should utilize libraries like JSON.NET that are already available to do the heavy lifting for you.
Pulled from the JSON.NET API docs:
If you create a string json
that holds your json, you can read from it with new JsonTextReader(new StringReader(json))
Upvotes: 0