将复杂的Javascript对象传递给ASP.NET Web服务

时间:2014-09-04 03:24:21

标签: javascript jquery asp.net json serialization

我正在使用jQuery AJAX函数将复杂的JavaScript对象传递给ASP.NET Web服务中的Web方法。我遵循了本文中的建议(以及其他):

Using Complex Types to Make Calling Services Less Complex

我现在能够从客户端到浏览器以及从浏览器到客户端传递对象。但是,我遇到了一个超出我的技能来治愈的问题。当从客户端到浏览器传递复杂对象时,数据在某种程度上会在旅途中“丢失”。它是在我传递给Web服务的JSON字符串中,但是当ASP.NET AJAX将JSON字符串转换为对象的新实例并将其传递给服务方法时,有部分缺失。

复杂类型是一个Meal对象,由一些属性,用餐中使用的食谱数组和一系列单独的食物项组成。数组中的每个Recipe对象都包含一些属性和Food items的“词典”作为成分,Food项目作为键,它们各自的值作为值。 Food对象只具有属性。看看JavaScript中各种对象的结构:

function Meal() {

    this.MealNumber;
    this.MealName;
    this.MealType;
    this.Ratio;
    this.DailyCalorieTarget;
    this.Recipes = [];
    this.Foods = []
}

function Recipe() {

    this.RecipeNumber;
    this.RecipeName;
    this.RecipeInstructions;
    this.Foods = [];
}

function Food() {

    this.FoodNumber;
    this.FoodName;
    this.CaloriesPerGram;
    this.FatPerGram;
    this.CarbsPerGram;
    this.DietaryFiberPerGram;
    this.ProteinPerGram;
    this.Category;
}

现在,查看Web Service中的相应对象:

Meal:

int MealNumber;
string MealName;
string MealType;
float Ratio;
int DailyCalorieTarget;

List<Recipe> Recipes;
List<KeyValuePair<Food, float>> Foods;

Recipe:

int RecipeNumber;
string RecipeName;
string RecipeInstructions;
List<KeyValuePair<Food, float>> Foods;

Food:

int FoodNumber;
string FoodName;
float CaloriesPerGram;
float FatPerGram;
float CarbsPerGram;
float DietaryFiberPerGram;
float ProteinPerGram;
string Category;

如您所见,属性是相同的。你们中的一些人可能会问为什么我在服务器端使用KeyValuePairs列表,而不是使用Dictionary对象。其他人已经知道Dictionary对象不会序列化为JSON字符串,而KeyValuePairs列表也会。

现在,针对这个问题:我的Web服务中有两个Web方法 - getMealData()和postMealData()。这是我正在为调试目的而做的事情:

  1. 当我单击客户端中的Get Meal Data按钮时,我正在使用jQuery向getMealData()方法发出AJAX请求。 getMealData()方法使用一些Recipe和Food数据填充Meal对象,使用JavaScriptSerializer.Serializer方法序列化Meal并将JSON字符串发送回客户端,其中JSON.parse方法将字符串解析为meal对象。 / p>

  2. 之后,我单击Send Meal Data按钮,并使用以下脚本JSON.stringify我刚刚从getMealData()方法创建的EXACT SAME对象:

    function postMealData(){

    var DTO = { 'meal': meal }
    
    $.ajax({
        type: "POST",
        url: "KetoCompanionService.asmx/postMealObject",
        data: JSON.stringify(DTO),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (msg) {
    
        $('#returnedData').html(msg.d);
    
        },
    
        error: function (msg) {
    
            $('#returnedData').html(msg.responseText);
    
        }
    });
    

    }

  3. 在获取用餐数据并将其发送回服务器之间,我没有做到这一点。这里发生的一切就是我从服务器获取JSON数据并将其解析为JavaScript对象。我正在对EXACT SAME对象进行字符串化并将其直接发送回服务器。但是,我在服务器端获得的内容与我发送的内容不匹配。

    我从服务器端的getMealData方法返回的JSON字符串:

    {"MealNumber":1,"MealName":"Cheese Pizza, Strawberries, and Cream","MealType":"Dinner","Ratio":2.25,"DailyCalorieTarget":1600,"Recipes":[{"RecipeNumber":10,"RecipeName":"Cheese Pizza","RecipeInstructions":"Just fix the damned thing...","Foods":[{"Key":{"FoodNumber":1,"FoodName":"Eggs","CaloriesPerGram":1.432,"FatPerGram":0.823,"CarbsPerGram":0.234,"DietaryFiberPerGram":0,"ProteinPerGram":0.432,"Category":"Protein"},"Value":20},{"Key":{"FoodNumber":2,"FoodName":"Nuts","CaloriesPerGram":2.432,"FatPerGram":1.823,"CarbsPerGram":1.234,"DietaryFiberPerGram":1,"ProteinPerGram":1.432,"Category":"Protein"},"Value":10}]}],"Foods":[{"Key":{"FoodNumber":3,"FoodName":"Strawberries","CaloriesPerGram":0.332,"FatPerGram":0.723,"CarbsPerGram":0.034,"DietaryFiberPerGram":0.2,"ProteinPerGram":0.232,"Category":"Carbs"},"Value":120}]}
    

    我从postMealData方法返回的字符串,在ASP.NET AJAX被服务器端转换为Meal对象后,如下所示:

    {"MealNumber":1,"MealName":"Cheese Pizza, Strawberries, and Cream","MealType":"Dinner","Ratio":2.25,"DailyCalorieTarget":1600,"Recipes":[],"Foods":[]}
    

    简而言之,食谱和食物阵列现在是空的。这个数据怎么了?当然,我在来自客户端和传入Web方法的传出数据中设置断点。当它离开时,该对象非常精细但在到达时缺少数组数据。

    我非常谦虚地承认我在做复杂的字符串化时会犯一个愚蠢的错误。但是,我没有做这个字符串化 - ASP.NET和JavaScript。我所做的就是在它们之间来回传递弦乐。

    是否有关于我缺少的列表序列化的内容?

    H-E-Double Hockey Sticks中有什么可以在这里进行?我把头发拉出来了!

    感谢您花时间阅读这么长的帖子。

    杰里米

    已编辑添加:

    客户端getMealData()代码:

    function getMealData() {
        $.ajax({
            type: "POST",
            url: "KetoCompanionService.asmx/getMealObject",
            data: "{}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (msg) {
    
                meal = JSON.parse(msg.d);
    
                // Insert the returned HTML into the <div>.
                $('#returnedData').html(msg.d);
            },
            error: function (msg) {
    
                $('#returnedData').html(msg.responseText);
            }
        });
    }
    

    服务器端getMealDataMethod:

    [WebMethod]
    [ScriptMethod(UseHttpGet=false, ResponseFormat = ResponseFormat.Json)]
    public string getMealObject() {
    
        JavaScriptSerializer js = new JavaScriptSerializer();
    
        _meal = new Meal();
        _meal.MealNumber = 1;
        _meal.MealName = "Cheese Pizza, Strawberries, and Cream";
        _meal.MealType = "Dinner";
        _meal.Ratio = 2.25f;
        _meal.DailyCalorieTarget = 1600;
    
        Recipe _recipe1 = new Recipe();
        _recipe1.RecipeNumber = 10;
        _recipe1.RecipeName = "Cheese Pizza";
        _recipe1.RecipeInstructions = "Just fix the damned thing...";
    
         Food _recipe1Food1 = new Food();
        _recipe1Food1.FoodNumber = 1;
        _recipe1Food1.FoodName = "Eggs";
        _recipe1Food1.CaloriesPerGram = 1.432f;
        _recipe1Food1.FatPerGram = 0.823f;
        _recipe1Food1.CarbsPerGram = 0.234f;
        _recipe1Food1.DietaryFiberPerGram = 0.0f;
        _recipe1Food1.ProteinPerGram = 0.432f;
        _recipe1Food1.Category = "Protein";
    
        KeyValuePair<Food, float> _kvp1 = new KeyValuePair<Food, float>(_recipe1Food1, 20.0f);
        _recipe1.Foods.Add(_kvp1);
    
        Food _recipe1Food2 = new Food();
        _recipe1Food2.FoodNumber = 2;
        _recipe1Food2.FoodName = "Nuts";
        _recipe1Food2.CaloriesPerGram = 2.432f;
        _recipe1Food2.FatPerGram = 1.823f;
        _recipe1Food2.CarbsPerGram = 1.234f;
        _recipe1Food2.DietaryFiberPerGram = 1.0f;
        _recipe1Food2.ProteinPerGram = 1.432f;
        _recipe1Food2.Category = "Protein";
    
        KeyValuePair<Food, float> _kvp2 = new KeyValuePair<Food, float>(_recipe1Food2, 10.0f);
        _recipe1.Foods.Add(_kvp2);
    
        _meal.Recipes.Add(_recipe1);
    
        Food _mealFood1 = new Food();
        _mealFood1.FoodNumber = 3;
        _mealFood1.FoodName = "Strawberries";
        _mealFood1.CaloriesPerGram = 0.332f;
        _mealFood1.FatPerGram = 0.723f;
        _mealFood1.CarbsPerGram = 0.034f;
        _mealFood1.DietaryFiberPerGram = 0.2f;
        _mealFood1.ProteinPerGram = 0.232f;
        _mealFood1.Category = "Carbs";
    
        KeyValuePair<Food, float> _kvp3 = new KeyValuePair<Food, float>(_mealFood1, 120.0f);
        _meal.Foods.Add(_kvp3);
    
        string returnString = js.Serialize(_meal);
    
        return returnString;
    }
    

    更新

    在我的getMealData()JavaScript函数中,我只是存储原始字符串,而不是从服务器解析返回的JSON数据并将其存储在meal对象中。

    然后,在我的postMealData()JavaScript函数中,我使用原始文本进行了自己的字符串化,而不是对已创建的用餐对象进行字符串化处理:

    var DTO = "{ \"meal\" : " + meal + "}";
    

    然后,我在AJAX请求中将该DTO字符串作为data:属性发送。

    我得到了相同的结果。几乎就像ASP.NET使用不同的算法来序列化餐对象而不是反序列化它。当我发送回来时,即使是ASP.NET发送给我的原始JSON字符串也不起作用。

1 个答案:

答案 0 :(得分:3)

解决方案 - 第1部分

我发现为什么我的Recipe和Foods数组是空的 - 我为ASP.NET中的每个Meal,Recipe和Food类创建的每个公共属性都有一个getter和一个setter - 除了List。我忽略了在那个属性中放置一个二传手,这使得它只读。因此,Web方法无法访问这些属性来设置它们。

然而,我还没有走出困境。即使我找到了丢失的数据,我的Foods数组也会在服务器端出现{Key:null,Value:0},即使我正在发送食物数据键值对。

解决方案 - 第2部分

我已经解决了这个问题。

显然,当涉及到KeyValuePair对象的列表时,ASP.NET AJAX在反序列化JSON方面存在问题。

因此,我没有使用类型List<KeyValuePair<Food, float>>来存储我的个人食品,而是创建了我自己的课程,我使用属性FoodWithAmountFoodItem来调用Amount而不是使用它:List<KeyValuePair<Food, float>>,我正在使用它:List<FoodWithAmount>。完成此更改后,服务器端将JSON字符串完美地转换为膳食对象。

public class FoodWithAmount
{
    Food _foodItem;
    float _amount;

    public Food FoodItem
    {
        get { return _foodItem; }
        set { _foodItem = value; }
    }

    public float Amount
    {
        get { return _amount; }
        set { _amount = value; }
    }

    //Parameterless constructor is necessary for serialization
    public FoodWithAmount()
    {
    }

    public FoodWithAmount(Food foodItem, float amount)
    {
        _foodItem = foodItem;
        _amount = amount;
    }
}

在过去几周学习JavaScript,ASP.NET,jQuery,HTML和CSS时遇到的所有障碍中,这个问题比其他任何时候都花费了我更多的时间和精力。

我希望我的痛苦能让别人的旅程变得更轻松。

快乐的编码!!