使用C#反序列化JSON

13

在尝试在C#中反序列化JSON时,发现获取信息有些困难。

我从Google自定义搜索返回了JSON格式的结果。 我只想检查我的步骤并建立尝试反序列化它的顺序。 这样做对吗?

  1. 我需要创建类以匹配JSON格式。 类似于创建模式文件。
  2. 使用 JavaScriptSerializer() 类和 deserialize 方法来提取相关部分。

我认为我将遇到的问题之一是我不需要返回的所有数据,只需要HTML链接。 我该如何实现这个目标?

更新

我已经使用以下JSON片段和C#代码更新了我的问题。 我想将字符串“links”输出到控制台,但似乎无法正常工作。 我是否错误地定义了我的类?

来自Google自定义搜索的JSON

handleResponse({
 "kind": "customsearch#search",
 "url": {
  "type": "application/json",
  "template": "https://www.googleapis.com/customsearch/v1?q\u003d{searchTerms}&num\u003d{count?}&start\u003d{startIndex?}&hr\u003d{language?}&safe\u003d{safe?}&cx\u003d{cx?}&cref\u003d{cref?}&sort\u003d{sort?}&alt\u003djson"
 },
 "queries": {
  "nextPage": [
   {
    "title": "Google Custom Search - lectures",
    "totalResults": 9590000,
    "searchTerms": "lectures",
    "count": 1,
    "startIndex": 2,
    "inputEncoding": "utf8",
    "outputEncoding": "utf8",
    "cx": "017576662512468239146:omuauf_lfve"
   }
  ],
  "request": [
   {
    "title": "Google Custom Search - lectures",
    "totalResults": 9590000,
    "searchTerms": "lectures",
    "count": 1,
    "startIndex": 1,
    "inputEncoding": "utf8",
    "outputEncoding": "utf8",
    "cx": "017576662512468239146:omuauf_lfve"
   }
  ]
 },
 "context": {
  "title": "Curriculum",
  "facets": [
   [
    {
     "label": "lectures",
     "anchor": "Lectures"
    }
   ],
   [
    {
     "label": "assignments",
     "anchor": "Assignments"
    }
   ],
   [
    {
     "label": "reference",
     "anchor": "Reference"
    }
   ]
  ]
 },
 "items": [
  {
   "kind": "customsearch#result",
   "title": "EE364a: Lecture Videos",
   "htmlTitle": "EE364a: \u003cb\u003eLecture\u003c/b\u003e Videos",
   "link": "http://www.stanford.edu/class/ee364a/videos.html",
   "displayLink": "www.stanford.edu",
   "snippet": "Apr 7, 2010 ... Course materials. Lecture slides · Lecture videos (2008) · Review sessions.   Assignments. Homework · Reading. Exams. Final exam ...",
   "htmlSnippet": "Apr 7, 2010 \u003cb\u003e...\u003c/b\u003e Course materials. \u003cb\u003eLecture\u003c/b\u003e slides · \u003cb\u003eLecture\u003c/b\u003e videos (2008) · Review sessions. \u003cbr\u003e  Assignments. Homework · Reading. Exams. Final exam \u003cb\u003e...\u003c/b\u003e",
   "cacheid": "TxVqFzFZLOsJ"
  }
 ]
}
);

C#代码片段

public class GoogleSearchResults
{
    public string link { get; set; }

}

public class Program
{
    static void Main(string[] args)
    {
        //input search term
        Console.WriteLine("What is your search query?:");
        string searchTerm = Console.ReadLine();

        //concantenate the strings using + symbol to make it URL friendly for google
        string searchTermFormat = searchTerm.Replace(" ", "+");

        //create a new instance of Webclient and use DownloadString method from the Webclient class to extract download html
        WebClient client = new WebClient();
        string Json = client.DownloadString("https://www.googleapis.com/customsearch/v1?key=My Key&cx=My CX&q=" + searchTermFormat);

        //create a new instance of JavaScriptSerializer and deserialise the desired content
        JavaScriptSerializer js = new JavaScriptSerializer();
        GoogleSearchResults results = js.Deserialize<GoogleSearchResults>(Json);

        Console.WriteLine(results);
        //Console.WriteLine(htmlDoc);
        Console.ReadLine();
    }
}

谢谢


你可以尝试将JSON反序列化为类型对象 - serializer.Deserialize(jsonContent, typeof(object))。 根据返回的JSON,你可能会得到一个字典对象或类似的东西,然后可以开始使用它。 - Scott Mitchell
3个回答

12

我使用你的第二种方法:使用 JavaScriptSerializer 进行反序列化。

这是我从 Facebook 反序列化响应的方法:

// get the id for the uploaded photo
var jss = new JavaScriptSerializer();
var resource = jss.Deserialize<Facebook.Data.Resource>(responseText);

....其中 Facebook.Data.Resource 如下所定义:

namespace Facebook.Data
{
    public class Resource
    {
        public string id { get; set; }
    }
}

我正在反序列化的responseText看起来像这样:

{"id":"10150111918987952",
 "from":{"name":"Someone",
     "id":"782272221"},
 "name":"uploaded from Cropper. (at 12\/15\/2010 7:06:41 AM)",
 "picture":"http:\/\/photos-f.ak.fbcdn.net\/hphotos-ak-snc4\/hs817.snc4\/69790_101501113333332_782377951_7551951_8193638_s.jpg",
 ...

但是由于在Resource类中只定义了一个属性,因此我只反序列化该属性。请在您的类中定义要反序列化的字段。

当然,使用继承也是有效的。您可以像这样定义数据类:

namespace Facebook.Data
{
  public class Resource
  {
    public string id { get; set; }
  }

  public class Person : Resource
  {
    public string name { get; set; }
  }

...然后你可以反序列化一个 Person 对象。


编辑

好的,根据你在更新的问题中提供的示例JSON,这是我编写用于保存响应的类的方式:

public class GoogleSearchItem
{
    public string kind { get; set; }
    public string title { get; set; }
    public string link { get; set; }
    public string displayLink { get; set; }
    // and so on... add more properties here if you want
    // to deserialize them
}

public class SourceUrl
{
    public string type { get; set; }
    public string template { get; set; }
}

public class GoogleSearchResults
{
    public string kind { get; set; }
    public SourceUrl url { get; set; }
    public GoogleSearchItem[] items { get; set; }
    // and so on... add more properties here if you want to
    // deserialize them
}

以下是C#代码进行反序列化的示例:

    // create a new instance of JavaScriptSerializer
    JavaScriptSerializer s1 = new JavaScriptSerializer();

    // deserialise the received response 
    GoogleSearchResults results = s1.Deserialize<GoogleSearchResults>(json);

    Console.WriteLine(s1.Serialize(results));

一些评论:

  • 用于保存搜索结果的顶层类名为GoogleSearchResults。
  • GoogleSearchResults类中的第一个属性是kind,对应于json对象中第一个命名属性。你使用的是link,但这是不会起作用的,因为link不是该json对象中顶级属性的名称。在json的较低层次上有名为“link”的属性,但JavaScriptSerializer不会将这些较低级别的内容提取到较高级别中。
  • 我的GoogleSearchResults类中的下一个属性是SourceUrl类型。这是因为json中的url属性并不是一个简单的字符串——它是一个带有两个属性的json对象,每个属性都有一个字符串值。所以,在C#中,SourceUrl作为一个类得到了两个字符串属性,每个属性都有适当的名称,以反序列化其中一个命名属性。
  • GoogleSearchResults类中的下一个属性名为“items”,以便能够反序列化json中的items字典。现在,正如名称所示,items在json中是一个数组,其值由方括号表示。这意味着可能会有多个item,尽管在你的情况下只有一个item。因此,在C#中,这个属性必须是一个数组(或集合)。json结果中的每个项目都不是一个简单的字符串,因此,就像我们在SourceUrl中所做的那样,我们需要定义一个持有者类来反序列化item对象:GoogleSearchItem。这个类有一些简单的字符串属性。C#类中的属性也可以是int类型或其他类型,如果json需要的话。
  • 最后,在打印结果时,如果只调用Console.WriteLine(result),你将看到通过Console.WriteLine隐式调用的ToString()方法的结果。这仅仅会打印出类型的名称,即此例子中为“GoogleSearchResults”,这可能不是你想要的。为了查看对象中的内容,你需要进行序列化,正如我所展示的。在其中的输出中,你将仅看到你反序列化的内容值。使用我提供的类,结果将比原始结果少一些信息,因为我没有提供与某些json属性相对应的C#类中的属性,因此这些属性没有被反序列化。

@Cheeso - 我非常感谢您抽出时间回答我的问题。让我总结一下我的理解。1)我在原始答案中缺少一个“顶级”类,这就是为什么链接无法工作的原因。2)我需要反序列化Json以将其与我的类结构匹配,以便操作数据。在我的情况下,我想要将数据输出到控制台,为此,我需要重新序列化才能输出?到目前为止,这样正确吗? - super9
#1 - 是的,要从响应中获取“link”值,您需要在C#中使用一个类结构来反映json的结构。在json中,“link”不是顶级属性,而是嵌套在“items”属性内部,因此您的C#需要反映这一点。#2 - 是的,反序列化“转换”了json字符串为内存对象。但实际上它并不是真正的转换,因为字符串仍然以其原始形式存在。实际上,您使用json字符串作为数据源创建了一个新的内存.NET对象。 - Cheeso
关于“我想将数据输出到控制台,为此,我需要重新序列化以输出?”...不是真的。如果你只想将响应输出到控制台,你可以直接调用Console.WriteLine(json)。但我认为你想要的是从json中提取特定属性的值 - “link”属性的值。为了做到这一点,你需要反序列化它。在那时,如果你只想打印链接的值,那么调用Console.WriteLine(results.items[0].link)。明白了吗? - Cheeso
比较 'Console.WriteLine(results.items[0].link)' 和 'Console.WriteLine(js.Serialize(results))' 的输出后,我想我终于明白了。天啊,我真是太迟钝了。EE讲座是自定义搜索测试URL的默认查询结果:P。Fershtay是什么意思?圣诞快乐 :) - super9
我不知道为什么我的编辑被拒绝了,但是在使用 .net 4.0 时,这段代码对我没有起作用。为了使其正常工作,我不得不向数据成员添加 [DataMember] 属性。public GoogleSearchItem[] items { get; set; } - dmihailescu
显示剩余7条评论

4
你可以查看Json.NET及其LINQ支持,来创建和查询JSON。通过编写一个漂亮的LINQ查询,你将只会得到需要的信息(你可以选择、分组、计数、最小/大值等等)。

好的,我有点像初学者,所以我不知道什么是LINQ。我需要了解LINQ才能使用Json.NET吗? - super9
1
@Nai,实际上强烈建议在使用LINQ之前先学习LINQ。而且不仅仅是为了这个情况。对于你作为.NET开发人员来说,这将非常有益。 - Darin Dimitrov
你的回复是否假定JSON数据是行/列而不是自定义对象? - Tim
@Tim,这并不重要。我假设它是有效的JSON,因为它的来源是Google,这是一个非常安全的假设 :-) 您可以使用LINQ随意查询它。就像您查询内存中的任何.NET对象一样。这就是LINQ的力量。您的数据格式是什么并不重要。您可以以相同的方式查询它(SQL、XML、内存对象、JSON等等)。 - Darin Dimitrov
1
我更倾向于使用.NET中已经提供的JavaScriptSerializer。 - Cheeso

0

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