将JSON反序列化为C#类

42

以下是我从REST API接收到的成功创建新的“工作代码”条目的(轻微)剥离响应。 我需要将响应反序列化成一些类,但我被难住了。

供参考,我在.NET 3.5中使用JSON.NET(在SQL Server 2008 R2中运行的SSIS脚本中)尝试我的反序列化。 这是JSON数据-显然我无法控制它,因为它来自别人的API:

{
   "results":{
      "jobcodes":{
         "1":{
            "_status_code":200,
            "_status_message":"Created",
            "id":444444444,
            "assigned_to_all":false,
            "billable":true,
            "active":true,
            "type":"regular",
            "name":"1234 Main Street - Jackson"
         },
         "2":{
            "_status_code":200,
            "_status_message":"Created",
            "id":1234567890,
            "assigned_to_all":false,
            "billable":true,
            "active":true,
            "type":"regular",
            "name":"4321 Some Other Street - Jackson"
         }
      }
   }
}
在我的C#代码中,我有一个"JobCode"类定义,它仅部分地将JSON值映射到属性 - 我对返回给我的所有数据都不感兴趣:
在我的C#代码中,我有一个"JobCode"类定义,它只是部分地将JSON值映射到属性 - 对于返回的所有数据我并不感兴趣:
[JsonObject]
class JobCode
{
    [JsonProperty("_status_code")]
    public string StatusCode { get; set; }
    [JsonProperty("_status_message")]
    public string StatusMessage { get; set; }
    [JsonProperty("id")]
    public string Id {get; set;}
    [JsonProperty("name")]
    public string Name { get; set; }

    //-------------------------------------------------------------------------------
    // Empty constructor for JSON serialization support
    //-------------------------------------------------------------------------------

    public JobCode() { }
}

我正在尝试通过以下调用对数据进行反序列化:

newResource = JsonConvert.DeserializeObject<JobCode>(jsonResponse);

当执行上述代码时,"jsonResponse"是所输出的代码。
但是每次我执行代码时,"newResource"总是返回null - 这并不出乎意料,因为我知道数据中实际上有多个工作代码,而此代码正试图将其反序列化为单个JobCode对象。我尝试创建一个名为"JobCodes"的新类,它看起来像这样:

class JobCodes
{
    [JsonProperty("jobcodes")]
    public List<JobCode>_JobCodes { get; set; }
}

然后我尝试着调用这个:

newResource = JsonConvert.DeserializeObject<JobCodes>(jsonResponse);

但问题仍然存在——我的返回对象为空。 我认为让我困惑的是“1”和“2”标识符的存在。我不知道如何在我的对象设计和/或使用JSON.NET类/属性特性(如[JsonObject],[JsonProperty]等)中考虑它们的存在。

当我将JSON数据通过JSON2CSharp运行时,它构造出一些看起来很奇怪的类,所以那并没有证明太有效。我已经使用几个不同的验证器验证过JSON,它们都检查通过了——我只是不知道我漏掉了什么。

最终,我想从JSON数据返回一个列表,但我被卡住了,不知道该怎么做才能实现。


已经尝试过 var newResource = JsonConvert.DeserializeObject<List<JobCode>>(jsonResponse);//? - ale
你的JSON格式是正确的,但是我觉得resultsjobcodes应该是一个集合。在你的情况下它们不是。这种情况下,你应该准备另一个类,其包含映射到JobCode类型的12属性。然后再准备另一个类,其中包含先前构建的类类型的jobcodes属性。 - user2160375
JSON 中没有数组,因此您将无法反序列化为列表。 - Anthony Chu
这是我得到的编辑版本:“无法将当前的JSON对象反序列化为类型'System.Collections.Generic.List`1 [JobCode]',因为该类型需要一个JSON数组(例如[1,2,3])才能正确反序列化。要解决此错误,请将JSON更改为JSON数组(例如[1,2,3]),或更改反序列化类型,使其成为可以从JSON对象反序列化的普通.NET类型(例如不是像整数这样的基元类型,也不是像数组或List <T>这样的集合类型)。还可以向类型添加JsonObjectAttribute以强制它从JSON对象反序列化。” - Jake Bullet
@pwas - 问题在于可能不仅仅有“1”和“2”这两个实例 - 响应一次可能包含最多50个“jobcode”对象。我对为“1”和“2”硬编码特定属性持谨慎态度 - 除非我误解了你的建议。 - Jake Bullet
显示剩余5条评论
4个回答

78

你的问题有两个:

  1. 你没有在根级别定义一个类。类的结构需要与整个 JSON 匹配,你不能只从中间反序列化。
  2. 每当你有一个键可以更改的对象时,你需要使用 Dictionary<string, T>。一般的类或者 List<T> 都行不通。

将你的类设置为这样:

class RootObject
{
    [JsonProperty("results")]
    public Results Results { get; set; }
}

class Results
{
    [JsonProperty("jobcodes")]
    public Dictionary<string, JobCode> JobCodes { get; set; }
}

class JobCode
{
    [JsonProperty("_status_code")]
    public string StatusCode { get; set; }
    [JsonProperty("_status_message")]
    public string StatusMessage { get; set; }
    [JsonProperty("id")]
    public string Id { get; set; }
    [JsonProperty("name")]
    public string Name { get; set; }
}

然后,像这样反序列化:

RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);

这里有工作演示


1
谢谢!这种方法按预期运行,所有我的数据都被解析为预期的格式。事实上,昨天我尝试了类似的方法,但我错用了字典对象而不是List<>。 - Jake Bullet

27

非常好的答案!

对于一些需要更多JSON类配置帮助的人,请试试:http://json2csharp.com/#

这是一个很好的自动生成类的方式!

甚至更简单的方法,在VS中,前往:

编辑 -> 特殊粘贴 -> 作为JSON类粘贴


1
不错,很喜欢 - Harry
非常有帮助,谢谢! - Paul Efford

4

由于无法更改JSON的结构,也无法设置固定数量的属性,因此我建议您使用JObject

var jobject = JObject.Parse(json);

var results = jobject["results"];
var jobcodes = results["jobcodes"];

var output = jobcodes.Children<JProperty>()
                     .Select(prop => prop.Value.ToObject<JobCode>())
                     .ToList();

警告:代码假定JSON始终在正确的模式中。您还应处理无效模式(例如,属性不属于JobCode模式的情况)。


0

你也可以将你的 JSON 反序列化为目标类的对象,然后像平常一样读取它的属性:

var obj = DeSerializeFromStrToObj<ClassToSerialize>(jsonStr);
Console.WriteLine($"Property: {obj.Property}");

DeSerializeFromStrToObj 是一个自定义类,利用反射来实例化目标类的对象:

    public static T DeSerializeFromStrToObj<T>(string json)
    {
        try
        {
            var o = (T)Activator.CreateInstance(typeof(T));

            try
            {
                var jsonDict = JsonSerializer.Deserialize<Dictionary<string, string>>(json);

                var props = o.GetType().GetProperties();

                if (props == null || props.Length == 0)
                {
                    Debug.WriteLine($"Error: properties from target class '{typeof(T)}' could not be read using reflection");
                    return default;
                }

                if (jsonDict.Count != props.Length)
                {
                    Debug.WriteLine($"Error: number of json lines ({jsonDict.Count}) should be the same as number of properties ({props.Length})of our class '{typeof(T)}'");
                    return default;
                }

                foreach (var prop in props)
                {
                    if (prop == null)
                    {
                        Debug.WriteLine($"Error: there was a prop='null' in our target class '{typeof(T)}'");
                        return default;
                    }

                    if (!jsonDict.ContainsKey(prop.Name))
                    {
                        Debug.WriteLine($"Error: jsonStr does not refer to target class '{typeof(T)}'");
                        return default;
                    }

                    var value = jsonDict[prop.Name];
                    Type t = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                    object safeValue = value ?? Convert.ChangeType(value, t);
                    prop.SetValue(o, safeValue, null); // initialize property
                }
                return o;
            }
            catch (Exception e2)
            {
                Debug.WriteLine(e2.Message);
                return o;
            }
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            return default;
        }
    }

一个完整的工作示例类可以在我对类似问题的增强答案中找到,这里

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