使用JsonSerializer有选择地读取JSON数据并填充C#对象

5

我连接到一个第三方 Web 服务,返回一个复杂的 JSON 对象,但里面只包含了我实际需要的一些信息。

基本上,我只需要 "value" 数组。从这个数组中,我只需要 "Id"、"Title" 和 "Status" 属性。

我想把这些属性放入一个名为 Project 的 C# 类中。这是我的类:

public class Project
{
    public String Id { get; set; }
    public String Title { get; set; }
    public String Status { get; set; }
}

我正在尝试使用这段代码读取JSON并进行转换:
using (WebResponse response = request.GetResponse())
{
    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
    {
        var serializer = new JsonSerializer();
        var jsonTextReader = new JsonTextReader(reader);
        returnValue = serializer.Deserialize<Project>(jsonTextReader);
    }
}

示例JSON:

{
    "odata.metadata":"http://school.edu/Api/1/$metadata#Projects",
    "odata.count":"3",
    "value":[
        {
            "odata.id":"http://school.edu/Api/1/Projects('123')",
            "RelatedProjects@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('123')/RelatedProjects",
            "Tags@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('123')/Tags",
            "TimedEvents@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('123')/Categories",
            "ep@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('123')/ep",
            "#CreateLike":{
                    "target":"http://school.edu/Api/1/Projects('123')/CreateLike"
                  },
            "#CreateShortcut":{
                        "target":"http://school.edu/Api/1/Projects('123')/CreateShortcut"
                      },
            "#Play":{
                        "target":"http://school.edu/Play/123"
                      },
            "#SendInvitation":{
                        "target":"http://school.edu/Api/1/Projects('123')/SendInvitation"
                      },
            "#CopyProject":{
                        "target":"http://school.edu/Api/1/Projects('123')/CopyProject"
                      },
            "#AddVideoPodcast":{
                        "target":"http://school.edu/Api/1/Projects('123')/AddVideoPodcast"
                      },
            "#AddEP":{
                        "target":"http://school.edu/Api/1/Projects('123')/AddEP"
                      },
            "Id":"123",
            "Title":"Test Title 1",
            "Status":"Viewable"
        },
        {
            "odata.id":"http://school.edu/Api/1/Projects('456')",
            "RelatedProjects@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('456')/RelatedProjects",
            "Tags@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('456')/Tags",
            "TimedEvents@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('456')/Categories",
            "ep@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('456')/ep",
            "#CreateLike":{
                    "target":"http://school.edu/Api/1/Projects('456')/CreateLike"
                  },
            "#CreateShortcut":{
                        "target":"http://school.edu/Api/1/Projects('456')/CreateShortcut"
                      },
            "#Play":{
                        "target":"http://school.edu/Play/456"
                      },
            "#SendInvitation":{
                        "target":"http://school.edu/Api/1/Projects('456')/SendInvitation"
                      },
            "#CopyProject":{
                        "target":"http://school.edu/Api/1/Projects('456')/CopyProject"
                      },
            "#AddVideoPodcast":{
                        "target":"http://school.edu/Api/1/Projects('456')/AddVideoPodcast"
                      },
            "#AddEP":{
                        "target":"http://school.edu/Api/1/Projects('456')/AddEP"
                      },
            "Id":"456",
            "Title":"Test Title 2",
            "Status":"Viewable"
        },
        {
            "odata.id":"http://school.edu/Api/1/Projects('789')",
            "RelatedProjects@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('789')/RelatedProjects",
            "Tags@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('789')/Tags",
            "TimedEvents@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('789')/Categories",
            "ep@odata.navigationLinkUrl":"http://school.edu/Api/1/Projects('789')/ep",
            "#CreateLike":{
                    "target":"http://school.edu/Api/1/Projects('789')/CreateLike"
                  },
            "#CreateShortcut":{
                        "target":"http://school.edu/Api/1/Projects('789')/CreateShortcut"
                      },
            "#Play":{
                        "target":"http://school.edu/Play/789"
                      },
            "#SendInvitation":{
                        "target":"http://school.edu/Api/1/Projects('789')/SendInvitation"
                      },
            "#CopyProject":{
                        "target":"http://school.edu/Api/1/Projects('789')/CopyProject"
                      },
            "#AddVideoPodcast":{
                        "target":"http://school.edu/Api/1/Projects('789')/AddVideoPodcast"
                      },
            "#AddEP":{
                        "target":"http://school.edu/Api/1/Projects('789')/AddEP"
                      },
            "Id":"789",
            "Title":"Test Title 3",
            "Status":"Viewable"
        }
  ],
  "odata.nextLink":"http://school.edu/Api/1/Folders('xyz')/Projects?$skip=10&$top=10"
}

我只是收到一个null对象而已,但在调试器中,我可以看到它从Web服务获取了所有的JSON数据。

我该如何从JSON中获取所需内容,构建我的C#对象,忽略其余部分呢?


1
在查看了反序列化文档后,似乎没有这方面的内在功能。因此,为了节省等待答案的时间,您不能将整个内容反序列化,然后有选择地使用所需的属性吗? - m.edmondson
@m.edmondson 谢谢。这是个愚蠢的问题,但如果不进行反序列化,我怎么才能获取我所需的数据/属性? - SkyeBoniwell
1
你无法反序列化整个响应吗? - m.edmondson
当我执行以下操作时,我可以在调试器中看到它:var rawJson = new StreamReader(response.GetResponseStream()).ReadToEnd(); 这会给我一个巨大的JSON字符串。 - SkyeBoniwell
1个回答

5
如果您能使用Json.NET(Newtonsoft json),您可以像这样利用Linq-to-Json [1]。
//using Newtonsoft.Json.Linq;
var jsonString = File.ReadAllText(@"C:YourDirectory\file.json"); //source
var projects = new List<Project>(); //Your result

JObject data = JObject.Parse(jsonString);
foreach (var value in data["value"])
{
    projects.Add(new Project
    {
        Id = value["Id"].ToString(),
        Status = value["Status"].ToString(),
        Title = value["Title"].ToString()
    });
}

或者,您也可以像这样反序列化 JObject [2]

var jsonReader = data["value"].CreateReader();
projects = new JsonSerializer().Deserialize<List<Project>>(jsonReader);

两种方法都可以正常工作,但哪一种更好呢?
第二种方法意味着代码量较少(特别是对于有许多属性的Project类而言,您需要编写很多代码才能将每个属性映射到代码中[1])。
但是第一种方法的性能要好得多! 对于给定的JSON数据,Code [1] 大约运行时间为 1 毫秒 ,而 Code [2] 则需要超过 100 毫秒
更新 在James Newton-King(Json.NET的作者)的建议下,还有一种更简洁的方法可以实现这一目标[3]。
projects = data["value"].ToObject<List<Project>>();

猜猜看!这段代码 [3] 的执行时间只有方法 [1] 的一半。所以,从各个方面来看,这必定是最好的方法!


3
第二个示例可以改进:data["value"].ToObject<List<Project>>() - James Newton-King
2
@JamesNewton-King 啊!我不知道,谢谢。我已经更新了我的答案,它也显示了根据我的秒表测试的最佳性能 :) - Arghya C

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