简体   繁体   中英

jQuery JSON save object to ASP.NET MVC2

I'm using ASP.NET MVC2 and jQuery to load and save objects. I'm using knockout.js's object serilization/deserialization to safely maintain the object's data structure when loading/saving data.

When I send back my object in its exact structure back to the server using the following JavaScript method, on the server side in ASP.NET, my GraduationClass object gets instantiated but it has none of the data.

I checked the post data in firebug, and all of the data is properly being sent back to the server in the request in the correct structure, yet the ASP.NET MVC internal pipeline is failing to deserialize the data back into the GraduationClass object. I tried this both with and without the contentType setting.

I'm wondering what I'm doing incorrectly.

// javascript save method
function save(graduationClass) {
    $.ajax({
        url: "/SiteManagement/saveGraduationClass",
        type: "POST",
        // data: ko.mapping.toJSON(graduationClass), // (solution 1)
        data: graduationClass, // (solution 2)
        contentType: "application/json; charset=utf-8",
        dataType: "json",            
        success: function(data) {            
    });    
}

// both solutions result in a blank object construction on the server side

// ASP.NET MVC2 AJAX method
[HttpPost]               
public ActionResult saveGraduationClass(GraduationClass graduationClass) {
    // graduationClass here has all default data rather than the data sent in the post
    return Json(new { resultText = "success" }, JsonRequestBehavior.AllowGet);
}

I believe there are two possible problem.

First, I'm pretty sure when you specify that you are sending JSON in an Ajax request, jQuery will Serialize Javascript Objects passed in the data parameter. You are definitely creating an jQuery object with a property json with a value of a string (I believe, I'm not familar with knockout). The value being passed to MVC would then look like:

{
  json : 'string of graduationClass data '
}

Which is of sorts a double serialization.

The second problem is, the previous JSON does not match your saveGraduationClass method parameter graduationClass .

I think either of these solutions should work:

data: ko.toJSON(graduationClass),  // knockout.js utility method

or

data: graduationClass,  // let jQuery serialize the object.

Update

If I have this class:

public class Person
{
  public string Name { get; set; } 
}

and I have this JSON:

{
  Name : 'Jon Doe'
}

then it will populate this method:

public ActionResult SomeMethod(Person person)

However, the following JSON will not work:

{
  json : '{ Name : "Jon Doe" }'
}

It will also not match:

{
  json :
  {
    Name : 'Jon Doe'
  }
}

The layout of the JSON needs to match exactly the layout of the class trying to be populated.

Hi I think that the problem could be in the default JsonValueProvider, but you can write a custom one:

public sealed class JsonDotNetValueProviderFactory : ValueProviderFactory
    {          

        private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
        {
            IDictionary<string, object> d = value as IDictionary<string, object>;
            if (d != null)
            {
                foreach (KeyValuePair<string, object> entry in d)
                {
                    AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
                }
                return;
            }

            IList l = value as IList;
            if (l != null)
            {
                for (int i = 0; i < l.Count; i++)
                {
                    AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
                }
                return;
            }

            // primitive
            backingStore[prefix] = value;
        }

        private static object GetDeserializedObject(ControllerContext controllerContext)
        {
            if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                // not JSON request
                return null;
            }

            StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
            string bodyText = reader.ReadToEnd();
            if (String.IsNullOrEmpty(bodyText))
            {
                // no JSON data
                return null;
            }

            object jsonData = new JavaScriptSerializer().DeserializeObject(bodyText);
            return jsonData;
        }

        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }

            object jsonData = GetDeserializedObject(controllerContext);
            if (jsonData == null)
            {
                return null;
            }

            Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            AddToBackingStore(backingStore, String.Empty, jsonData);
            return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
        }

        private static string MakeArrayKey(string prefix, int index)
        {
            return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
        }

        private static string MakePropertyKey(string prefix, string propertyName)
        {
            return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
        }


    }

Then add this into the Application_Start in Global.asax:

    ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new JsonDotNetValueProviderFactory());

This example is using Json.NET

Also is better to use ko.mapping plugin ko.mapping , because your graduationClass could be observable object:

var jsonModel = ko.mapping.toJSON(graduationClass);

                    $.ajax({
                        url: '/SiteManagement/saveGraduationClass',
                        type: 'POST',
                        dataType: 'json',
                        data: jsonModel,
                        contentType: 'application/json; charset=utf-8',
                        success: function (data) {
                            // get the result and do some magic with it                            
                        }
                    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM