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

发布于 2025-01-14 23:11:52 字数 2564 浏览 1 评论 0原文

下面是我在成功创建新的“工作代码”条目后从 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,但我不知道需要做什么才能实现这一点。

Below is a (slightly) stripped down response I get from a REST API upon successful creation of a new "job code" entry. I need to deserialize the response into some classes, but I'm stumped.

For reference, I'm using JSON.NET in .NET 3.5 (running in a SSIS script in SQL Server 2008 R2) to attempt my deserialization. Here's the JSON - which I obviously have no control over as it's coming from someone else's 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"
         }
      }
   }
}

In my C# code, I do have a "JobCode" class defined which only partially maps the JSON values to properties - I'm not interested in all of the data that's returned to me:

[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() { }
}

I'm attempting to deserialize the data via this call:

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

Where jsonResponse is the code outputted above.
When I execute the code, "newResource" always comes back as null - which is not unexpected because I know that there are actually multiple job codes in the data and this code is trying to deserialize it into a single JobCode object. I tried creating a new class called JobCodes that looks like this:

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

And then I tried calling this (JobCodes instead of JobCode):

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

But the issue persists - my return object is null.
What's throwing me off, I think, is the presence of the "1" and "2" identifiers. I don't know how to account for their presence in my object design and/or usage of the JSON.NET class / property attributes like [JsonObject],[JsonProperty], etc.

When I run the JSON data through JSON2CSharp, it constructs some weird-looking classes, so that hasn't proven too effective. I've validated the JSON with several different validators and it all checks out–I just don't know what I'm missing here.

Ultimately, I'd like to return a List<JobCode> from the JSON data, but I'm stumped on what I need to do to make that happen.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

世俗缘 2025-01-21 23:11:52

您的问题是双重的:

  1. 您没有在根级别定义类。类结构需要匹配整个 JSON,不能只从中间反序列化。
  2. 每当您有一个其键可以更改的对象时,您就需要使用 Dictionary。普通的课程并不能解决这个问题; List 也不会。

让你的类像这样:

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

这里的工作演示

Your problem is twofold:

  1. You don't have a class defined at the root level. The class structure needs to match the entire JSON, you can't just deserialize from the middle.
  2. Whenever you have an object whose keys can change, you need to use a Dictionary<string, T>. A regular class won't work for that; neither will a List<T>.

Make your classes like this:

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

Then, deserialize like this:

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

Working demo here

还不是爱你 2025-01-21 23:11:52

因为您无法更改 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 架构)。

Because you can't change the scheme of JSON, and you can't set constant No. of properties, I'd suggest you to use 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();

Warning: code assumes, that JSON is always in proper schema. You should also handle invalid schema (for example where property is not of JobCode scheme).

七色彩虹 2025-01-21 23:11:52

您还可以将 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;
        }
    }

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

You can also deserialize your json to an object of your target class, and then read its properties as per normal:

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

where DeSerializeFromStrToObj is a custom class that makes use of reflection to instantiate an object of a targeted class:

    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(
quot;Error: properties from target class '{typeof(T)}' could not be read using reflection");
                    return default;
                }

                if (jsonDict.Count != props.Length)
                {
                    Debug.WriteLine(
quot;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(
quot;Error: there was a prop='null' in our target class '{typeof(T)}'");
                        return default;
                    }

                    if (!jsonDict.ContainsKey(prop.Name))
                    {
                        Debug.WriteLine(
quot;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;
        }
    }

A complete working example class can be found in my enhanced answer to a similar question, here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文