【问题标题】:Json.Net - Deserialize a list of objects which contain a list of objectsJson.Net - 反序列化包含对象列表的对象列表
【发布时间】:2016-10-01 00:00:27
【问题描述】:

我正在尝试反序列化描述以下内容的 json:

Item 类型的对象列表,每个 Item 包含一些属性以及 Effect 类型的对象的列表“配方”,其中包含它们自己的三个属性(动作、值和目标)。

当我使用 'JsonConvert.SerializeObject' 序列化我的列表时,我得到以下 json:

[
  {
    "name": "WOOD",
    "yield": 1.0,
    "recipe": [
      {
        "action": "ADD",
        "value": 1.0,
        "target": "WOOD"
      }
    ],
    "count": 0.0,
    "numWorkers": 0,
    "id": 1
  },
  {
    "name": "CLAY",
    "yield": 2.0,
    "recipe": [
      {
        "action": "ADD",
        "value": 2.0,
        "target": "CLAY"
      }
    ],
    "count": 0.0,
    "numWorkers": 0,
    "id": 2
  },
  {
    "name": "SPEAR",
    "yield": 0.5,
    "recipe": [
      {
        "action": "ADD",
        "value": 0.5,
        "target": "SPEAR"
      },
      {
        "action": "SUB",
        "value": 1.0,
        "target": "WOOD"
      },
      {
        "action": "SUB",
        "value": 5.0,
        "target": "CLAY"
      }
    ],
    "count": 0.0,
    "numWorkers": 0,
    "id": 3
  },
  {
    "name": "STICK",
    "yield": 4.0,
    "recipe": [
      {
        "action": "ADD",
        "value": 4.0,
        "target": "STICK"
      },
      {
        "action": "SUB",
        "value": 1.0,
        "target": "WOOD"
      }
    ],
    "count": 0.0,
    "numWorkers": 0,
    "id": 4
  }
]

但是当我尝试使用“Items = JsonConvert.DeserializeObject<List<Item>>(jsonstring);”反序列化时,我收到此错误:A first chance exception of type 'System.NullReferenceException' occurred in Newtonsoft.Json.dll 并且我的“项目”列表为空。

当我使用 json2csharp 生成 c# 时,我得到以下信息:

public class Recipe
{
    public string action { get; set; }
    public double value { get; set; }
    public string target { get; set; }
}

public class RootObject
{
    public string name { get; set; }
    public double yield { get; set; }
    public List<Recipe> recipe { get; set; }
    public double count { get; set; }
    public int numWorkers { get; set; }
    public int id { get; set; }
}

它认为我的 Item 对象是“RootObject”,它给了我一个“Recipe”对象,而不是“recipe”列表中的“Effect”对象列表

以下是我的游戏和类的一些代码,您可以看到我正在处理的内容:

public List<Item> Items;

private void Game_Load(object sender, EventArgs e) 
    {
        Items = JsonConvert.DeserializeObject<List<Item>>(jsonstring);
    }

public class Item
{
    public string name;
    public double yield;
    public List<Effect> recipe = new List<Effect>();
    public double count;
    public int numWorkers;
    public int id;

    public Item()
    {
        name = "";
        //configureItem();
    }

    public Item(string nm)
    {
        name = nm.ToUpper();
        //configureItem();
    }

    public List<Effect> getRecipe() {
        return recipe;
    }
}

public class Effect
{
    public string action;
    public double value;
    public string target;

    public Effect(string act, double val, string tar)
    {
        action = act.ToUpper();
        value = val;
        target = tar.ToUpper();
    }
}

我的课程中的所有变量都需要{ get; set; } 吗?我之前尝试添加它,但它似乎导致我的 VS 在调试期间跳过行以及各种其他奇怪的东西。还是只是 Json 格式问题?任何帮助将不胜感激,我已经浏览了整个网站和谷歌,我在这里扯掉了我的头发。

【问题讨论】:

    标签: c# json list json.net deserialization


    【解决方案1】:

    重命名构造函数的参数名称以匹配 json 中的属性名称,否则会产生歧义,因此 Json.net 无法确定哪个字段属于哪里。

    public class Effect
    {
        public string action;
        public double value;
        public string target;
    
        //public Effect(string act, double val, string tar)
        public Effect(string action, double value, string target)
        {
            this.action = action.ToUpper();
            this.value = value;
            this.target = target.ToUpper();
        }
    }
    

    【讨论】:

    • 我可以使用相同的名称吗?比如value = value,它怎么知道哪个值是哪个?我收到警告Assignment made to same variable; did you mean to assign something else? 如果我更改了类变量名称,那么当我序列化它时 Json 最终会有所不同。
    • @ZackF 是的,你可以,编译器足够聪明地推断出 value = value 意味着 this.value = value。但是,作为推荐做法,请使用“this”关键字
    【解决方案2】:

    您的Event 类没有默认(无参数)构造函数,因此 Json.Net 正在尝试使用它拥有的构造函数。但是,参数名称与 JSON 中的任何内容都不匹配(不存在 actvaltar 属性)。在这种情况下,Json.Net 将为参数传递默认值(即null)以构造对象,然后返回并尝试设置字段。问题是您的构造函数不具备处理空值的能力。它假定acttar 在调用ToUpper() 时不会为空。这就是NullReferenceException 的来源。

    一种解决方案是重命名您的构造函数参数以匹配 JSON(我还将添加适当的空值检查以确保安全):

    public Effect(string action, double value, string target)
    {
        this.action = (action != null ? action.ToUpper() : null);
        this.value = value;
        this.target = (target != null ? target.ToUpper() : null);
    }
    

    另一种可能的解决方案是将ToUpper 逻辑移动到属性中并提供默认构造函数。

    public class Effect
    {
        private string _action;
        public string Action
        {
            get { return _action; }
            set { _action = (value != null ? value.ToUpper() : null); }
        }
    
        public double Value { get; set; }
    
        private string _target;
        public string Target
        {
            get { return _target; }
            set { _target = (value != null ? value.ToUpper() : null); }
        }
    
        public Effect()
        {
        }
    }
    

    【讨论】:

    • 他没有默认构造函数,因为他想在创建时为字符串设置大写
    • 嗯,有道理。那么要么有一个具有所有确切变量名称的构造函数,要么有一个没有(无参数)的构造函数?我会试试看。
    • @JeffPang 谢谢,我知道了。
    • 太好了,效果很好。感谢你们俩。将此标记为答案,因为它包含更多信息,因此我对问题的理解更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-24
    • 2012-09-06
    • 2017-07-21
    相关资源
    最近更新 更多