Json.Net - 反序列化一个包含对象列表的对象列表

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

一个类型为Item的对象列表,每个Item包含一些属性以及一个名为'recipe'的对象列表,该列表包含三个自身属性(action、value和target)的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并且我的“Items”列表为空。
当我使用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格式问题?任何帮助将不胜感激,我已经在这个网站和Google上寻找了很久,现在我快要抓狂了。

2个回答

3
您的Event类没有默认(无参数)构造函数,因此Json.Net尝试使用它有的构造函数。然而,参数名称与JSON中的任何内容都不匹配(不存在actvaltar属性)。在这种情况下,Json.Net将传递默认值(即null)来构造对象,然后返回并尝试设置字段。问题在于您的构造函数不能处理空值。它假设在调用ToUpper()时,acttar将不为null。这就是NullReferenceException的来源。
一种解决方案是将构造函数参数重命名以匹配JSON(我还会添加适当的null检查以确保安全):
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()
    {
    }
}

他没有默认构造函数,因为他试图在创建时将字符串设置为大写。 - Jeff Pang
啊,好的,那很有道理。所以要么有一个带有所有确切变量名称的构造函数,要么没有参数的构造函数?我会尝试一下的。 - Zack F
1
@JeffPang 谢谢,我知道那个。 - Brian Rogers
太好了,这个完美地解决了问题。感谢你们两位。我将把这标记为答案,因为它提供了更多的信息,让我更好地理解了问题。 - Zack F

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,它如何知道哪个值是哪个?我收到了一个警告,即“分配给相同变量;您是否想分配其他内容?”如果我更改类变量名称,那么当我序列化它时,Json将会不同。 - Zack F
1
@ZackF 是的,你可以这样做,编译器足够聪明,可以推断出 value = value 的意思是 this.value = value。然而,作为推荐的做法,请使用 'this' 关键字。 - Jeff Pang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接