I have problems to receive json values from my javascript/jQuery request to my controller.
MyClass looks like the following:
function MyClass() {
this.MyString = null;
this.MyInt = null;
this.Subs = null;
}
My Request looks like the following:
var testData1 = new MyClass();
testData1.MyInt = 1234;
testData1.MyString = "abcDEF";
testData1.Subs = new Array();
var testData2 = new MyClass();
testData2.MyInt = 5678;
testData2.MyString = "GHIjkl";
testData1.Subs.push(testData2);
var jsonData = JSON.stringify(testData1);
var self = this;
$.ajax({
url: '/Home/Request',
type: 'POST',
dataType: 'json',
data: jsonData,
contentType: 'application/json; charset=utf-8',
success: function (x) {
self.ParseResult(x);
}
});
Now I have a controller:
public JsonResult Request(MyClass myObj)
{
var answer = ...
return Json(answer, JsonRequestBehavior.DenyGet);
}
With the following class:
public class MyClass
{
public string MyString { get; set; }
public int MyInt { get; set; }
public List<MyClass> Subs { get; set; }
}
All names in jsonData are exactly the same like 开发者_运维问答in my class "MyClass". But there are no values in myObj.
Where is the Problem. Is there anything I can do to get this mapping working correctly?
Thank you very much in advance,
Chris
UPDATE:
Thank you for your repley. I have used the JavascriptSerializer. But I have the problem, that myString is null:
public JsonResult Data(string myString)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
var data = serializer.Deserialize<MyClass>(myString);
var answer = ...
return Json(answer, JsonRequestBehavior.DenyGet);
}
Where is the value? Should I take the value from the request-data?
@Dave Ward
You second solution is working. But I have a problem with the subs.
var testData1 = new MyClass();
testData1.MyInt = 1234;
testData1.MyString = "abcDEF";
testData1.Subs = new Array();
for (var i = 0; i < 10; i++) {
var testData2 = new MyClass();
testData2.MyInt = i;
testData2.MyString = "abcDEF";
testData1.Subs.push(testData2);
}
I get 10 Subs in my controller, but all are empty. What can I do?
@Dave Ward, @ALL
Using the traditional settings my 10 Subs are bot empty, they are not there. Subs count is 0 (not NULL). I tried to change the Subs Type from List to IEnumerable but that didn't help. Do you know anything else I can do to get Subs filled in my controller?
Ok, thank you Dave Ward, I will use the JSON method.
For somebody else having the same problem this controller code could be from help:
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(myString));
var serializer = new DataContractJsonSerializer(typeof(MyClass));
MyClass se = serializer.ReadObject(ms) as MyClass;
ms.Close();
Right now, you're sending the JSON string as the entire body of your POST. MVC has no way to connect the dots and understand that you wanted it to provide that entire string as a parameter named myString.
To do that, change your client-side data parameter like this:
$.ajax({
url: '/Home/Request',
type: 'POST',
dataType: 'json',
data: { myString: jsonData },
contentType: 'application/json; charset=utf-8',
success: function (x) {
// If ParseResult is some sort of JSON deserializing, don't do that.
// When the dataType is set to 'json', jQuery handles this for you
// automatically before the success handler is called.
self.ParseResult(x);
}
});
When you provide jQuery an object as its data parameter, it automatically URLEncodes that before sending, so something like this will be POSTed to the server:
myString={json:data, here:etc}
MVC will pick that up as the myString parameter as desired then, and you can proceed with deserializing it with JavaScriptSerializer.
Also, unless you're doing something more complicated on the client-side than you're showing, the JavaScript class is unnecessary. Something like this would work:
var testData2 = {
MyInt: 5678,
MyString: "GHIjkl",
}
var testData1 = {
MyInt: 1234,
MyString: "abcDEF",
Subs: [ testData2 ]
}
All that said, why are you using JSON for the request? If you accept a parameter of the custom type in your action, MVC's model binder is pretty good at hydrating that type from standard URLEncoded input data:
public JsonResult Data(MyClass request)
{
// request.MyInt, request.MyString, etc should exist here.
var answer = ...
// It's okay to accept URLEncoded input parameters, but still return JSON.
return Json(answer, JsonRequestBehavior.DenyGet);
}
Then, calling it doesn't require the client-side JSON serialization:
var testData2 = {
MyInt: 5678,
MyString: "GHIjkl",
}
var testData1 = {
MyInt: 1234,
MyString: "abcDEF",
Subs: [ testData2 ]
}
$.ajax({
url: '/Home/Request',
type: 'POST',
traditional: true,
dataType: 'json',
data: testData1
success: function (x) {
self.ParseResult(x);
}
});
It's a bit simpler all around, and is faster since you've removed a layer of serialization on the client-side and deserialization on the server-side.
In my experience I have to pass in the json as a string and then deserialize it. I use Newtonsoft to do this.
public ActionResult DoAjax(string jsonRequest)
{
JsonSerializer serializer = new JsonSerializer();
StringReader sr = new StringReader(jsonRequest);
Newtonsoft.Json.JsonTextReader reader = new JsonTextReader(sr);
MyClass obj = (MyClass)serializer.Deserialize(reader, typeof(MyClass));
//...do other stuff
}
on a similar tack to josh's, you can use a json actionfilter a la:
// requires newtonsoft.json.dll
public class JsonFilter : ActionFilterAttribute
{
public string Param { get; set; }
public Type JsonDataType { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext
.Request.ContentType.Contains("application/json"))
{
string inputContent;
using (var sr = new StreamReader(filterContext.HttpContext
.Request.InputStream))
{
inputContent = sr.ReadToEnd();
}
var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
filterContext.ActionParameters[Param] = result;
}
else
try
{
// we should do a loop looking for json here ONLY if there's a callback
// i.e. we're calling jsonP
if (filterContext.HttpContext.Request.QueryString["callback"] != null)
{
string inputContent =
Enumerable.Where(filterContext.HttpContext
.Request.QueryString.Keys.Cast<string>()
.Select(qs => filterContext.HttpContext
.Request.QueryString[qs]), query => !string.IsNullOrEmpty(query))
.FirstOrDefault(query => query.IndexOf("{") == 0);
var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
filterContext.ActionParameters[Param] = result;
}
}
catch (Exception e)
{
// do nothing
filterContext.ActionParameters[Param] = null;
}
}
}
usage (in this case typeof(IList)) but can be ANY class ie typeof(myClass):
[JsonFilter(Param = "jsonData", JsonDataType = typeof(IList<FundPropertyWeekSplit>))]
public virtual ActionResult AddFundPropertyWeekSplit(IList<FundPropertyWeekSplit> jsonData)
{
// code to deal with the newly created strongly typed object
}
i use this (rather too much!!) all over the shop...
You can take a look Omar Al Zabir's article about json, xml request and response handling he provided a really good solution for solving this common situations.
Create REST API using ASP.NET MVC that speaks both Json and plain Xml
精彩评论