使用Newtonsoft(或LINQ to JSON)将JSON反序列化为.NET对象

342

我知道有一些关于Newtonsoft的帖子,所以希望这不是完全重复的...我正在尝试将Kazaa API返回的JSON数据转换为某种好的对象

WebClient client = new WebClient();
Stream stream = client.OpenRead("http://api.kazaa.com/api/v1/search.json?q=muse&type=Album");
StreamReader reader = new StreamReader(stream);

List<string> list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(reader.Read().ToString());

foreach (string item in list)
{
    Console.WriteLine(item);
}

//Console.WriteLine(reader.ReadLine());
stream.Close();

那个JsonConvert的代码行是我最近尝试的...我还不太理解,希望通过询问你们来减少一些工作。最初我想将它转换为字典或其他什么东西...实际上,我只需要获取其中的一些值,根据文档,也许Newtonsoft的LINQ to JSON可能是更好的选择?有什么想法/链接吗?

这里是JSON返回数据的示例:

{
  "page": 1,
  "total_pages": 8,
  "total_entries": 74,
  "q": "muse",
  "albums": [
    {
      "name": "Muse",
      "permalink": "Muse",
      "cover_image_url": "http://image.kazaa.com/images/69/01672812 1569/Yaron_Herman_Trio/Muse/Yaron_Herman_Trio-Muse_1.jpg",
      "id": 93098,
      "artist_name": "Yaron Herman Trio"
    },
    {
      "name": "Muse",
      "permalink": "Muse",
      "cover_image_url": "htt p://image.kazaa.com/images/54/888880301154/Candy_Lo/Muse/Candy_Lo-Muse_1.jpg",
      "i d": 102702,
      "artist_name": "\u76e7\u5de7\u97f3"
    },
    {
      "name": "Absolution",
      "permalink": " Absolution",
      "cover_image_url": "http://image.kazaa.com/images/65/093624873365/Mus e/Absolution/Muse-Absolution_1.jpg",
      "id": 48896,
      "artist_name": "Muse"
    },
    {
      "name": "Ab solution",
      "permalink": "Absolution-2",
      "cover_image_url": "http://image.kazaa.com/i mages/20/825646911820/Muse/Absolution/Muse-Absolution_1.jpg",
      "id": 118573,
      "artist _name": "Muse"
    },
    {
      "name": "Black Holes And Revelations",
      "permalink": "Black-Holes-An d-Revelations",
      "cover_image_url": "http://image.kazaa.com/images/66/093624428466/ Muse/Black_Holes_And_Revelations/Muse-Black_Holes_And_Revelations_1.jpg",
      "id": 48813,
      "artist_name": "Muse"
    },
    {
      "name": "Black Holes And Revelations",
      "permalink": "Bla ck-Holes-And-Revelations-2",
      "cover_image_url": "http://image.kazaa.com/images/86/ 825646911486/Muse/Black_Holes_And_Revelations/Muse-Black_Holes_And_Revelations_1 .jpg",
      "id": 118543,
      "artist_name": "Muse"
    },
    {
      "name": "Origin Of Symmetry",
      "permalink": "Origin-Of-Symmetry",
      "cover_image_url": "http://image.kazaa.com/images/29/825646 912629/Muse/Origin_Of_Symmetry/Muse-Origin_Of_Symmetry_1.jpg",
      "id": 120491,
      "artis t_name": "Muse"
    },
    {
      "name": "Showbiz",
      "permalink": "Showbiz",
      "cover_image_url": "http: //image.kazaa.com/images/68/825646182268/Muse/Showbiz/Muse-Showbiz_1.jpg",
      "id": 60444,
      "artist_name": "Muse"
    },
    {
      "name": "Showbiz",
      "permalink": "Showbiz-2",
      "cover_imag e_url": "http://image.kazaa.com/images/50/825646912650/Muse/Showbiz/Muse-Showbiz_ 1.jpg",
      "id": 118545,
      "artist_name": "Muse"
    },
    {
      "name": "The Resistance",
      "permalink": "T he-Resistance",
      "cover_image_url": "http://image.kazaa.com/images/36/825646864836/ Muse/The_Resistance/Muse-The_Resistance_1.jpg",
      "id": 121171,
      "artist_name": "Muse"
    }
  ],
  "per_page": 10
}

我进行了更多阅读,并发现Newtonsoft的LINQ to JSON正好符合我的要求......使用WebClient、Stream、StreamReader和Newtonsoft......我可以从Kazaa获取JSON数据,提取URL,下载文件,并在不到七行的代码中完成所有操作!我很喜欢它。

WebClient client = new WebClient();
Stream stream = client.OpenRead("http://api.kazaa.com/api/v1/search.json?q=muse&type=Album");
StreamReader reader = new StreamReader(stream);

Newtonsoft.Json.Linq.JObject jObject = Newtonsoft.Json.Linq.JObject.Parse(reader.ReadLine());

// Instead of WriteLine, 2 or 3 lines of code here using WebClient to download the file
Console.WriteLine((string)jObject["albums"][0]["cover_image_url"]);
stream.Close();

这篇文章的点击量非常高,我认为包含评论中讨论的“使用”部分可能会很有帮助。

using(var client = new WebClient())
using(var stream = client.OpenRead("http://api.kazaa.com/api/v1/search.json?q=muse&type=Album"))
using (var reader = new StreamReader(stream))
{
    var jObject = Newtonsoft.Json.Linq.JObject.Parse(reader.ReadLine());
    Console.WriteLine((string) jObject["albums"][0]["cover_image_url"]);
}

9
很棒的例子,谢谢。只是建议:你可能没有写这个是为了简洁起见,但是由于WebClientStreamStreamReader都实现了IDisposable接口,所以最好在代码中加入一些using块。 - arcain
啊,好的,很棒...(是的,这只是我为了研究即将到来的任务而运行的一个控制台应用程序) 现在去研究最后一块拼图,HLS+AES加密 :) 哎呀...哈哈 - J Benjamin
新顿软件解决方案是否也无法完全反序列化JSON,就像@arcain的解决方案一样? - AXMIM
请注意这里的链接:LINQ to JSON - yu yang Jian
12个回答

290

您可以使用C#的dynamic类型使事情更加简单。这种技术还使得重构变得更加简单,因为它不依赖于魔法字符串。

JSON

下面的JSON字符串是从HTTP API调用的简单响应,它定义了两个属性:IdName

{"Id": 1, "Name": "biofractal"}

C#

使用JsonConvert.DeserializeObject<dynamic>()将该字符串反序列化为动态类型,然后像平常一样访问它的属性即可。

dynamic results = JsonConvert.DeserializeObject<dynamic>(json);
var id = results.Id;
var name= results.Name;
如果您将results变量的类型指定为dynamic,而不是使用var关键字,则属性值将正确反序列化,例如Id将被反序列化为int而不是JValue(感谢下面GFoley83的评论)。
注意:Newtonsoft程序集的NuGet链接为http://nuget.org/packages/newtonsoft.json
包:您还可以使用nuget实时安装程序包,在打开项目时只需进行浏览程序包,然后只需安装它安装、卸载、更新,它将仅添加到依赖项/ NuGet下的项目中。

我之前使用了与上述相同的代码来反序列化 Twitter 响应,使用的是 newtonsoft.dll 版本 4.5.6,一切正常。但是在更新到版本 5.0.6 后,它开始抛出错误。有任何想法为什么会这样吗? - Pranav
1
适用于动态对象,当我们知道或拥有C#类时,我们可以将其作为C#类消耗,以替换动态对象,例如<Myclass>。 - MSTdev
2
使用 dynamic results = JsonConvert.DeserializeObject<ExpandoObject>(json); 这个超赞。它会正确地将 Id 反序列化为 int 而不是 JValue。请参见这里:https://dotnetfiddle.net/b0WxGJ - GFoley83
@biofractal 我该如何在VB.NET中实现这个 dynamic results = JsonConvert.DeserializeObject<dynamic>(json);?使用 Dim results As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Object)(json) 无法正常工作。 - Adam
@Flo 尝试使用...DeserializeObject(Of System.Dynamic.ExpandoObject) - ToolmakerSteve
顺便说一句,我最终在VB中测试了ExpandoObject。我发现将其转换为IDictionary很方便:Dim dict As IDictionary(Of String, Object) = JsonConvert.DeserializeObject(Of ExpandoObject)(json) Dim name = dict("Name") - ToolmakerSteve

275

如果您只需要从JSON对象中获取几个项目,我建议使用Json.NET的LINQ to JSON JObject类。例如:

JToken token = JObject.Parse(stringFullOfJson);

int page = (int)token.SelectToken("page");
int totalPages = (int)token.SelectToken("total_pages");

我喜欢这种方法,因为你不需要完全反序列化JSON对象。这在处理API时非常方便,因为有时候会出现缺少对象属性的情况,比如Twitter。

文档:使用Json.NET进行JSON序列化和反序列化以及使用Json.NET进行LINQ to JSON操作


1
我实际上已经做了更多的阅读和测试...发现这也是一个不错的方法...Newtonsoft是一个相当不错的库,我会为其他人发布我的示例。 - J Benjamin
1
我发布了一个大致的示例,展示了我是如何做的...不完全相同,我看到你建议使用JToken.Parse...但我还不确定两者之间的区别,不过很好! - J Benjamin
1
@Jbenjamin 谢谢!那是个打字错误。JToken 是 JObject 的基类,而且我个人更喜欢使用更抽象的类型。感谢你指出这一点。 - arcain
@Tyrone,我没有遇到你遇到的问题。从你列出的异常和浏览Json.NET源代码来看,它似乎与从JsonReader读取有关。如果你将第一行更改为JObject token = JObject.Parse(stringFullOfJson);,是否有所帮助? - arcain
2
@Tyrone 当然,没问题。我实际上也使用这段代码来解析 Twitter 状态,并且我已经写了相当多的错误处理程序来处理对 Twitter 的调用,因为它们有时会出现问题。如果你还没有这样做,我建议在尝试解析之前将 Twitter 的原始 JSON 响应转储到日志中。然后,如果失败了,你至少可以看到是否收到了一些奇怪的东西。 - arcain
显示剩余3条评论

45

使用dynamic关键字可以方便地解析任何这种类型的对象:

dynamic x = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString);
var page = x.page;
var total_pages = x.total_pages
var albums = x.albums;
foreach(var album in albums)
{
    var albumName = album.name;

    // Access album data;
}

我想知道如何循环遍历结果,但找了很长时间才找到...谢谢!! - batoutofhell

26

如果和我一样,您更喜欢处理强类型对象**,请使用:

MyObj obj =  JsonConvert.DeserializeObject<MyObj>(jsonString);

这种方式可以使用智能感知和编译时类型错误检查。

您可以通过将 JSON 复制到内存中并将其作为 JSON 对象粘贴来轻松创建所需的对象(Visual Studio -> 编辑 -> Paste Special -> Paste JSON as Classes)。

如果您在 Visual Studio 中没有该选项,请参阅此处

您还需要确保您的 JSON 是有效的。如果它只是一组对象,则在开头添加自己的对象。即 {"obj":[{},{},{}]}。

** 我知道dynamic有时可以使事情变得更容易,但我在这方面有一点老派。


2
非常喜欢我的编程方式。我喜欢强类型对象。谢谢,因为我使用并修改了这段代码。 - j.hull
那个转换为JSON的功能真是太棒了!省了我大量的时间。 - public wireless

23

如果我错了,请纠正我,但我认为前面的例子与 James Newton 的 Json.NET 库的最新版本略有不同。

var o = JObject.Parse(stringFullOfJson);
var page = (int)o["page"];
var totalPages = (int)o["total_pages"];

1
感谢您的回复,Rick。是的,那看起来与我在最新文档中找到的示例类似。 - J Benjamin
1
是的,自从arcain修正了那个错别字后,我的评论看起来就像是在挑刺一样难看。我最初发表评论是因为我不认识JToken.Parse。 - Rick Leitch
1
不是挑剔,但肯定有一个错误,并且总有多种方法可以解决。顺便说一下,我的Json.NET版本支持在JObject上使用索引器的语法,但我修改答案时使用的代码来自于使用SelectToken方法重载的代码,所以我可以在找不到令牌时抑制异常:JToken JToken.SelectToken(string tokenName, bool errorWhenNoMatch),这就是冗长的原因。 - arcain

13

动态列表 松散类型 - 反序列化并读取值

// First serializing
dynamic collection = new { stud = stud_datatable }; // The stud_datable is the list or data table
string jsonString = JsonConvert.SerializeObject(collection);


// Second Deserializing
dynamic StudList = JsonConvert.DeserializeObject(jsonString);

var stud = StudList.stud;
foreach (var detail in stud)
{
    var Address = detail["stud_address"]; // Access Address data;
}

9

我喜欢这种方法:

using Newtonsoft.Json.Linq;
// jsonString is your JSON-formatted string
JObject jsonObj = JObject.Parse(jsonString);
Dictionary<string, object> dictObj = jsonObj.ToObject<Dictionary<string, object>>();

您现在可以使用dictObj作为字典来访问任何想要的内容。如果您更喜欢将值作为字符串获取,也可以使用Dictionary<string, string>

您可以使用相同的方法将其转换为任何类型的.NET对象。


2
我认为这个方法非常好,原因有两点:1)当你不关心数据类型(一切都是字符串);2)使用值的字典非常方便。 - netfed

7
此外,如果您只是想查找嵌套在JSON内容中的特定值,可以像这样操作:
yourJObject.GetValue("jsonObjectName").Value<string>("jsonPropertyName");

从那里开始等等。

如果您不想承担将整个JSON转换为C#对象的成本,这可能有所帮助。


2

我创建了一个用于解析 JSON 的扩展类:

 public static class JsonExtentions
    {
        public static string SerializeToJson(this object SourceObject) { return Newtonsoft.Json.JsonConvert.SerializeObject(SourceObject); }


        public static T JsonToObject<T>(this string JsonString) { return (T)Newtonsoft.Json.JsonConvert.DeserializeObject<T>(JsonString); }
}

设计模式:

 public class Myobject
    {
        public Myobject(){}
        public string prop1 { get; set; }

        public static Myobject  GetObject(string JsonString){return  JsonExtentions.JsonToObject<Myobject>(JsonString);}
        public  string ToJson(string JsonString){return JsonExtentions.SerializeToJson(this);}
    }

使用方法:

   Myobject dd= Myobject.GetObject(jsonstring);

                 Console.WriteLine(dd.prop1);

https://dev59.com/dnE95IYBdhLWcg3wlu4g - Sloomy

1
相对较晚才了解此问题,但我今天在工作中自己遇到了这个问题。以下是我如何解决这个问题的方法。
我正在访问一个第三方API以检索书籍列表。返回的对象是一个包含大约20多个字段的大型JSON对象,其中我只需要将ID作为List字符串对象。我在动态对象上使用linq来检索所需的特定字段,然后将其插入到我的List字符串对象中。
dynamic content = JsonConvert.DeserializeObject(requestContent);
var contentCodes = ((IEnumerable<dynamic>)content).Where(p => p._id != null).Select(p=>p._id).ToList();

List<string> codes = new List<string>();

foreach (var code in contentCodes)
{
    codes.Add(code?.ToString());
}

1
codes.Add(code?.ToString()) will add a null for any missing code. The ? is not in a position that can stop codes.Add from executing. You probably want if (code != null) ... - ToolmakerSteve

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