将 JSON 反序列化为嵌套 C# 类

问题描述 投票:0回答:4

下面是我在成功创建新的“工作代码”条目后从 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 值部分映射到属性 - 我对返回给我的所有数据不感兴趣:

[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; }
}

然后我尝试调用这个(

JobCodes
而不是
JobCode
):

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

但问题仍然存在 - 我的返回对象为空。 我认为让我失望的是“1”和“2”标识符的存在。我不知道如何解释它们在我的对象设计和/或 JSON.NET 类/属性属性(如

[JsonObject]
[JsonProperty]
等)的使用中的存在。

当我通过 JSON2CSharp 运行 JSON 数据时,它会构造一些看起来很奇怪的类,因此这并没有被证明太有效。我已经使用几个不同的验证器验证了 JSON,并且全部检查完毕 - 我只是不知道我在这里缺少什么。

最终,我想从 JSON 数据返回

List<JobCode>
,但我不知道需要做什么才能实现这一点。

c# json.net inner-classes
4个回答
79
投票

你的问题是双重的:

  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);

这里有工作演示


26
投票

对于那些可能需要更多 JSON 类配置帮助的人,请尝试:https://json2csharp.com/

或者更简单,在 Visual Studio 中,转到:编辑 -> 选择性粘贴 -> 粘贴为 JSON 类


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;
        }
    }

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

© www.soinside.com 2019 - 2024. All rights reserved.