1. 程式人生 > 實用技巧 >C# 解析Json檔案(使用NewtonJson庫)

C# 解析Json檔案(使用NewtonJson庫)

C#中解析json檔案有很多種方法,在多種方法中一般都會提到一個十分優秀的庫:NewtonJson。使用NewtonJson處理Json檔案十分高效,而且在配置也十分簡單,直接在Nuget包中匯入即可。

目錄

1.匯入NewtonJson庫

2.解析Json檔案

2.1 最簡單的序列化與反序列化

2.2 序列化集合和字典

2.3 反序列化集合和字典

2.4 將物件儲存為Json檔案&從檔案解析為json

2.5 有條件的序列化物件成員

2.6 解析匿名類

2.7 將派生類解析為基類

2.8 防止重複寫值

2.9 取消C#預設引數賦值& 過濾值為null的屬性

2.10 型別缺少成員報錯

3. 使用Linq處理json

3.1 解析Json的基本操作

3.2 修改Json(使用JObject)

3.3 合併Json檔案

3.4 將Json型別轉為普通型別

3.5 判斷Json檔案是否相等 &深度複製Json檔案

4. 總結



1.匯入NewtonJson庫

編寫C#程式肯定是在visual studio中寫(暫不考慮unity等其他地方),那麼無論你是在windows下還是Mac OS X下,visual studio都自帶nuget包管理工具。本文以Mac OS X平臺為例,首先新建一個專案叫ParseJson,然後在專案的依賴項中彈出快捷選單,如下:

點選Nuget包管理,彈出一個視窗:

可以看到第一個就是我們想要的包,選中然後新增包即可。新增完成後在你的程式中新增下面兩行:

  1. using Newtonsoft.Json;
  2. using Newtonsoft.Json.Linq;

至此,基本配置就完成了。


2.解析Json檔案

NewtonJson官網有詳細的使用文件教程,有很多示例程式碼,這裡就不過多介紹了,遇到不懂的問題可以去文件裡面找資料。

2.1 最簡單的序列化與反序列化

假設你在C#中有一個定義好的類,然後你生成了一個物件,想把這個物件儲存為Json檔案,你可以用SerializeObject()函式處理。看下面的程式碼:

  1. using System;
  2. using System.IO;
  3. using Newtonsoft.Json;
  4. using Newtonsoft.Json.Linq;
  5. namespace ParseJson
  6. {
  7. class Product
  8. {
  9. public string Name;
  10. public DateTime ExpiryDate;
  11. public Decimal Price;
  12. public String[] Sizes;
  13. }
  14. class Program
  15. {
  16. static void Main(string[] args)
  17. {
  18. Product product = new Product()
  19. {
  20. Name = "Apple",
  21. ExpiryDate=new DateTime(2020,12,30),
  22. Price=2.99M,
  23. Sizes=new string[] {"small","medium","large"}
  24. };
  25. string output = JsonConvert.SerializeObject(product);
  26. //將Json檔案以字串的形式儲存
  27. StreamWriter sw = new StreamWriter(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product.dat");
  28. sw.Write(output);
  29. sw.Close();
  30. Console.WriteLine(output);
  31. }
  32. }
  33. }

這裡我們建立了一個Product類,並且例項化了一個物件,利用JsonConvert.SerializeObject(product)將其轉化為Json檔案,並以字串的形式儲存在變數output,我們很容易將字串儲存到本地。可以檢視儲存到本地後的檔案內容:

既然我們可以將物件以json檔案的形式儲存,自然我們也應該可以從Json格式恢復成物件,做法很簡單,假設我們已經讀取了本地檔案Product.dat,並儲存到了字串變數output中,我們要從中恢復成Product物件只需一句話:

  1. //恢復物件
  2. Product p = JsonConvert.DeserializeObject<Product>(output);

值得一提的的是,當你的物件裡面有集合物件時:

  1. public class Acount
  2. {
  3. public string Email {get;set;}
  4. public Ilist<string> Roles {get;set}
  5. }

也可以直接使用上面的方法,轉化為Json檔案後,集合物件會以陣列的形式儲存。


2.2 序列化集合和字典

除了序列化自定義的類,還可以將C#中的集合物件序列化,這裡我就不跑程式碼了,直接搬運官網的例子。

序列化字典:

  1. List<string> videogames = new List<string>
  2. {
  3. "Starcraft",
  4. "Halo",
  5. "Legend of Zelda"
  6. };
  7. string json = JsonConvert.SerializeObject(videogames);
  8. Console.WriteLine(json);
  9. // ["Starcraft","Halo","Legend of Zelda"]

List集合被轉化為了陣列,當然List裡面可以是複雜的型別,如使用我們之前定義的Product:

  1. Product product1 = new Product()
  2. {
  3. Name = "Apple",
  4. ExpiryDate=new DateTime(2020,12,30),
  5. Price=2.99M,
  6. Sizes=new string[] {"small","medium","large"}
  7. };
  8. Product product2 = new Product()
  9. {
  10. Name = "cup",
  11. ExpiryDate = new DateTime(2099, 1, 1),
  12. Price = 9.99M,
  13. Sizes = new string[] { "s", "L", "M", "xL" }
  14. };
  15. List<Product> list = new List<Product>() { product1, product2 };
  16. string json = JsonConvert.SerializeObject(list);
  17. Console.WriteLine(json);

輸出為:

[{"Name":"Apple","ExpiryDate":"2020-12-30T00:00:00","Price":2.99,"Sizes":["small","medium","large"]},{"Name":"cup","ExpiryDate":"2099-01-01T00:00:00","Price":9.99,"Sizes":["s","L","M","xL"]}]

序列化字典例子如下:

  1. Dictionary<string, int> points = new Dictionary<string, int>
  2. {
  3. { "James", 9001 },
  4. { "Jo", 3474 },
  5. { "Jess", 11926 }
  6. };
  7. string json = JsonConvert.SerializeObject(points, Formatting.Indented);
  8. Console.WriteLine(json);
  9. // {
  10. // "James": 9001,
  11. // "Jo": 3474,
  12. // "Jess": 11926
  13. // }

這裡SerializeObject多了一個引數,Indented表示轉化為的Json檔案帶縮排,這樣輸出會更加直觀清晰。

2.3 反序列化集合和字典

反序列化和之前講的類似,反序列化需要給出轉化為物件的型別,反序列化集合:

  1. string json = @"[
  2. {
  3. 'Name': 'Product 1',
  4. 'ExpiryDate': '2000-12-29T00:00Z',
  5. 'Price': 99.95,
  6. 'Sizes': null
  7. },
  8. {
  9. 'Name': 'Product 2',
  10. 'ExpiryDate': '2009-07-31T00:00Z',
  11. 'Price': 12.50,
  12. 'Sizes': null
  13. }
  14. ]";
  15. List<Product> products = JsonConvert.DeserializeObject<List<Product>>(json);
  16. Console.WriteLine(products.Count);
  17. // 2
  18. Product p1 = products[0];
  19. Console.WriteLine(p1.Name);
  20. // Product 1

反序列化集合一般轉為List型別,如果你需要轉化為其它型別,你可以後續再處理。反序列化字典也是如此:

  1. string json = @"{""key1"":""value1"",""key2"":""value2""}";
  2. Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
  3. Console.WriteLine(values.Count);
  4. // 2
  5. Console.WriteLine(values["key1"]);
  6. // value1

2.4 將物件儲存為Json檔案&從檔案解析為json

在最開始的例子中,我們將物件先轉為字串,然後再寫入一個dat檔案中,事實上完全可以將檔案儲存為Json檔案。有兩種思路,一種還是先將json變為字串,然後儲存到自定義的json檔案中。

  1. Product product1 = new Product()
  2. {
  3. Name = "Apple",
  4. ExpiryDate=new DateTime(2020,12,30),
  5. Price=2.99M,
  6. Sizes=new string[] {"small","medium","large"}
  7. };
  8. Product product2 = new Product()
  9. {
  10. Name = "cup",
  11. ExpiryDate = new DateTime(2099, 1, 1),
  12. Price = 9.99M,
  13. Sizes = new string[] { "s", "L", "M", "xL" }
  14. };
  15. List<Product> list = new List<Product>() { product1, product2 };
  16. File.WriteAllText(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product1.json",
  17. JsonConvert.SerializeObject(list,Formatting.Indented));

我們用Vscode開啟product1.json如下:

另一種方法是直接將物件轉化為json檔案:

  1. using(StreamWriterfile=File.CreateText(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product2.json"))
  2. {
  3. JsonSerializer serializer = new JsonSerializer() { Formatting=Formatting.Indented};
  4. serializer.Serialize(file, list);
  5. }

得到的product2.json和前面是一樣的。

從json檔案中解析物件的操作幾乎是一模一樣的:只需要將SerializeObject函式換成DeserializeObject,WriteAllText換成ReadAllText,CreatText換成OpenText。

  1. // read file into a string and deserialize JSON to a type
  2. Movie movie1 = JsonConvert.DeserializeObject<Movie>(File.ReadAllText(@"c:\movie.json"));
  3. // deserialize JSON directly from a file
  4. using (StreamReader file = File.OpenText(@"c:\movie.json"))
  5. {
  6. JsonSerializer serializer = new JsonSerializer();
  7. Movie movie2 = (Movie)serializer.Deserialize(file, typeof(Movie));
  8. }

注意:直接從json轉為物件,除了提供物件型別引數,還需要強制轉化操作。


2.5 有條件的序列化物件成員

NewTonJson還支援有條件序列化物件,即對某些屬性進行判斷,如果不滿足要求,則忽略該屬性。

要實現部分屬性的條件序列化,需要新增一些函式,這個函式和屬性一一對應,函式名為:ShouldSerialize+屬性名,函式的返回值為bool型別,當返回為True時,該屬性將被序列化,為False則被忽略。看一個官方例子:

首先你有這樣一個簡單類:

  1. public class Employee
  2. {
  3. public string Name { get; set; }
  4. public Employee Manager { get; set; }
  5. public bool ShouldSerializeManager()
  6. {
  7. // don't serialize the Manager property if an employee is their own manager
  8. return (Manager != this);
  9. }
  10. }

這裡已經添加了Manager這個篩選函式,所以當Manage就是自己時,這個Manage會被忽略。

  1. Employee joe = new Employee();
  2. joe.Name = "Joe Employee";
  3. Employee mike = new Employee();
  4. mike.Name = "Mike Manager";
  5. joe.Manager = mike;
  6. // mike is his own manager
  7. // ShouldSerialize will skip this property
  8. mike.Manager = mike;
  9. string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented);
  10. Console.WriteLine(json);
  11. // [
  12. // {
  13. // "Name": "Joe Employee",
  14. // "Manager": {
  15. // "Name": "Mike Manager"
  16. // }
  17. // },
  18. // {
  19. // "Name": "Mike Manager"
  20. // }
  21. // ]

不過一般而言,當資料量不是很大時,你可以有條件的使用Json檔案的資料,也就是我不用這個屬性,我就可以假設它不存在。

上面的例子固然可行,但是有個很麻煩的問題:你必須在設計類的時候就確定好條件序列化屬性。

那麼有沒有更好的控制辦法呢?答案是肯定的。

通過派生介面:IContractResolver,生產一個篩選物件可以實現定製化篩選,你不用在你的類裡面新增函式。一般我們不用直接派生自IContractResolver,而是派生自它的一個派生類:

DefaultContractResolver

看下面這個例子,我們假設有這樣一個圖書類:

  1. public class Book
  2. {
  3. public string BookName { get; set; }
  4. public decimal BookPrice { get; set; }
  5. public string AuthorName { get; set; }
  6. public int AuthorAge { get; set; }
  7. public string AuthorCountry { get; set; }
  8. }

我們想實現序列化以字母“A”開頭或者“B”開頭的屬性,顯然用之前的方法會很麻煩,我們用剛才的方法,派生一個篩選類:

  1. public class DynamicContractResolver : DefaultContractResolver
  2. {
  3. private readonly char _startingWithChar;
  4. public DynamicContractResolver(char startingWithChar)
  5. {
  6. _startingWithChar = startingWithChar;
  7. }
  8. protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
  9. {
  10. IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
  11. // only serializer properties that start with the specified character
  12. //只需要在這裡新增屬性篩選
  13. properties =
  14. properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList();
  15. return properties;
  16. }
  17. }

這個類有一個成員變數:_startingWithChar,用來接受篩選引數;一個建構函式;一個篩選函式:CreateProperties,這個函式返回一個屬性集合,函式首先獲得類的所有屬性,儲存帶properties中,然後你根據需求對properties進行處理。上面的函式實現了根據屬性名的首字母進行篩選。所以我們就可以用這個類的物件作為引數去實現我們的需求:

  1. Book book = new Book
  2. {
  3. BookName = "The Gathering Storm",
  4. BookPrice = 16.19m,
  5. AuthorName = "Brandon Sanderson",
  6. AuthorAge = 34,
  7. AuthorCountry = "United States of America"
  8. };
  9. string startingWithA = JsonConvert.SerializeObject(book, Formatting.Indented,
  10. new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('A') });
  11. // {
  12. // "AuthorName": "Brandon Sanderson",
  13. // "AuthorAge": 34,
  14. // "AuthorCountry": "United States of America"
  15. // }
  16. string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented,
  17. new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') });
  18. // {
  19. // "BookName": "The Gathering Storm",
  20. // "BookPrice": 16.19
  21. // }

DefaultContractResolver中的CreateProperties是對多個屬性篩選而言的,回到本節最開始的問題,我只想對Manage成員進行篩選,那麼可以用CreateProperty這個函式,具體用法如下:

  1. public class ShouldSerializeContractResolver : DefaultContractResolver
  2. {
  3. public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
  4. protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
  5. {
  6. JsonProperty property = base.CreateProperty(member, memberSerialization);
  7. if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
  8. {
  9. property.ShouldSerialize =
  10. instance =>
  11. {
  12. Employee e = (Employee)instance;
  13. return e.Manager != e;
  14. };
  15. }
  16. return property;
  17. }
  18. }

這個的用法和前面類似,不過這裡直接聲明瞭一個類的靜態成員,所以不需要new一個物件:

  1. string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented,
  2. new JsonSerializerSettings { ContractResolver=ShouldSerializeContractResolver.Instance});

順便要說明的是,為了成功執行需要新增新的名稱空間:

  1. using Newtonsoft.Json.Linq;
  2. using Newtonsoft.Json.Serialization;

這裡給出官網關於DefaultContrctResolver的連結


2.6 解析匿名類

對於匿名類,由於序列化不需要給出物件型別,所以可以依然使用前面序列化自定義類的方法,但是反序列是需要提供型別的,那對於匿名類怎麼辦?,這個NewtonJson也替我們考慮了,例子如下:

  1. var definition = new { Name = "" };
  2. string json1 = @"{'Name':'James'}";
  3. var customer1 = JsonConvert.DeserializeAnonymousType(json1, definition);
  4. Console.WriteLine(customer1.Name);
  5. // James
  6. string json2 = @"{'Name':'Mike'}";
  7. var customer2 = JsonConvert.DeserializeAnonymousType(json2, definition);
  8. Console.WriteLine(customer2.Name);
  9. // Mike

辦法很簡單,直接給一個匿名類的定義物件,傳入引數即可。

2.7 將派生類解析為基類

將派生類解析為基類,需要一個派生自CustomeCreationConverter的物件,操作起來其實很簡單,看一下官方的例子:

首先你有一個Person基類,然後派生了Employee類,並寫好了派生自CustomeCreationConverter類的類PersonConverter:

  1. public class Person
  2. {
  3. public string FirstName { get; set; }
  4. public string LastName { get; set; }
  5. public DateTime BirthDate { get; set; }
  6. }
  7. public class Employee : Person
  8. {
  9. public string Department { get; set; }
  10. public string JobTitle { get; set; }
  11. }
  12. public class PersonConverter : CustomCreationConverter<Person>
  13. {
  14. public override Person Create(Type objectType)
  15. {
  16. return new Employee();
  17. }
  18. }

然後你想將Employee物件解析為Person,只需要傳入一個PersonConvrter物件:

  1. string json = @"{
  2. 'Department': 'Furniture',
  3. 'JobTitle': 'Carpenter',
  4. 'FirstName': 'John',
  5. 'LastName': 'Joinery',
  6. 'BirthDate': '1983-02-02T00:00:00'
  7. }";
  8. Person person = JsonConvert.DeserializeObject<Person>(json, new PersonConverter());
  9. Console.WriteLine(person.GetType().Name);
  10. // Employee
  11. Employee employee = (Employee)person;
  12. Console.WriteLine(employee.JobTitle);
  13. // Carpenter

從結果可以看出,雖然是以Person解析的,但是實際上仍然是Employee型別。


2.8 防止重複寫值

如果一個類的建構函式本身就對成員進行了賦值,那麼在反序列化時,可能會呼叫一次建構函式容易造成重複寫入,看下面的例子:

  1. public class UserViewModel
  2. {
  3. public string Name { get; set; }
  4. public IList<string> Offices { get; private set; }
  5. public UserViewModel()
  6. {
  7. Offices = new List<string>
  8. {
  9. "Auckland",
  10. "Wellington",
  11. "Christchurch"
  12. };
  13. }
  14. }

建構函式對成員Offices進行了賦值:

  1. string json = @"{
  2. 'Name': 'James',
  3. 'Offices': [
  4. 'Auckland',
  5. 'Wellington',
  6. 'Christchurch'
  7. ]
  8. }";
  9. UserViewModel model1 = JsonConvert.DeserializeObject<UserViewModel>(json);
  10. foreach (string office in model1.Offices)
  11. {
  12. Console.WriteLine(office);
  13. }
  14. // Auckland
  15. // Wellington
  16. // Christchurch
  17. // Auckland
  18. // Wellington
  19. // Christchurch
  20. UserViewModel model2 = JsonConvert.DeserializeObject<UserViewModel>(json, new JsonSerializerSettings
  21. {
  22. ObjectCreationHandling = ObjectCreationHandling.Replace
  23. });
  24. foreach (string office in model2.Offices)
  25. {
  26. Console.WriteLine(office);
  27. }
  28. // Auckland
  29. // Wellington
  30. // Christchurch

如果不新增設定,Offices就是存在2遍初始值,為例避免這種情況,在反序列化的時候傳入了一個setting物件,其ObejctCreationHandling屬性為Replcae。


2.9 取消C#預設引數賦值& 過濾值為null的屬性

C#對於沒有賦值的型別提供一個預設值,如Int型別預設值為0,string型別預設值為null,如果當一個物件的成員沒有被賦值,我們希望得到的是一個空json,那麼需要將setting的DefaultValueHandleling設定為Ignore。

  1. public class Person
  2. {
  3. public string Name { get; set; }
  4. public int Age { get; set; }
  5. public Person Partner { get; set; }
  6. public decimal? Salary { get; set; }
  7. }
  8. Person person = new Person();
  9. string jsonIncludeDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented);
  10. Console.WriteLine(jsonIncludeDefaultValues);
  11. // {
  12. // "Name": null,
  13. // "Age": 0,
  14. // "Partner": null,
  15. // "Salary": null
  16. // }
  17. string jsonIgnoreDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
  18. {
  19. DefaultValueHandling = DefaultValueHandling.Ignore
  20. });
  21. Console.WriteLine(jsonIgnoreDefaultValues);
  22. // {}

過濾值為null的成員只需要將NullValueHandling屬性設定為Ignore。

  1. public class Person
  2. {
  3. public string Name { get; set; }
  4. public int Age { get; set; }
  5. public Person Partner { get; set; }
  6. public decimal? Salary { get; set; }
  7. }
  8. Person person = new Person
  9. {
  10. Name = "Nigal Newborn",
  11. Age = 1
  12. };
  13. string jsonIncludeNullValues = JsonConvert.SerializeObject(person, Formatting.Indented);
  14. Console.WriteLine(jsonIncludeNullValues);
  15. // {
  16. // "Name": "Nigal Newborn",
  17. // "Age": 1,
  18. // "Partner": null,
  19. // "Salary": null
  20. // }
  21. string jsonIgnoreNullValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
  22. {
  23. NullValueHandling = NullValueHandling.Ignore
  24. });
  25. Console.WriteLine(jsonIgnoreNullValues);
  26. // {
  27. // "Name": "Nigal Newborn",
  28. // "Age": 1
  29. // }

2.10 型別缺少成員報錯

當我們將下面的json檔案:

  1. string json = @"{
  2. 'FullName': 'Dan Deleted',
  3. 'Deleted': true,
  4. 'DeletedDate': '2013-01-20T00:00:00'
  5. }";

解析為Account型別時:

  1. public class Account
  2. {
  3. public string FullName { get; set; }
  4. public bool Deleted { get; set; }
  5. }

是可以成功的,因為json中包含Accout類中所有成員屬性。但是如果我們要嚴格轉化(特別是在Accout漏掉屬性時),需要報錯,那麼就需要設定setting的MissingMemberHandling屬性。

  1. try
  2. {
  3. JsonConvert.DeserializeObject<Account>(json, new JsonSerializerSettings
  4. {
  5. MissingMemberHandling = MissingMemberHandling.Error
  6. });
  7. }
  8. catch (JsonSerializationException ex)
  9. {
  10. Console.WriteLine(ex.Message);
  11. // Could not find member 'DeletedDate' on object of type 'Account'. Path 'DeletedDate', line 4, position 23.
  12. }

此外還有很多設定用來解決各種問題,上面只是列出了一些常見的,有需要的還是要去官網檢視。


3. 使用Linq處理json

使用ling處理json最大的好處是擺脫了物件的束縛,使用NewtonJson自帶的一套體系。我們都知道JavaScript原生支援Json,直接可以將Json檔案轉化為一個物件,JObject的建立也是為了實現這樣一個類似的功能。

3.1 解析Json的基本操作

  • 解析文字為Json物件
    直接使用JObject.Parse(obj)即可
    1. string json = @"{
    2. CPU: 'Intel',
    3. Drives: [
    4. 'DVD read/writer',
    5. '500 gigabyte hard drive'
    6. ]
    7. }";
    8. JObject o = JObject.Parse(json);
    9. Console.WriteLine(o.ToString());
    10. // {
    11. // "CPU": "Intel",
    12. // "Drives": [
    13. // "DVD read/writer",
    14. // "500 gigabyte hard drive"
    15. // ]
    16. // }

  • 將陣列解析為Json
    直接使用JArray類
    1. string json = @"[
    2. 'Small',
    3. 'Medium',
    4. 'Large'
    5. ]";
    6. JArray a = JArray.Parse(json);
    7. Console.WriteLine(a.Tostring());
    8. // [
    9. // "Small",
    10. // "Medium",
    11. // "Large"
    12. // ]

  • 從本地Json檔案中解析
    讀操作

    1. using (StreamReader reader = File.OpenText(@"c:\person.json"))
    2. {
    3. JObject o = (JObject)JToken.ReadFrom(new JsonTextReader(reader));
    4. // do stuff
    5. }

    寫操作

    1. JObject videogameRatings = new JObject(
    2. new JProperty("Halo", 9),
    3. new JProperty("Starcraft", 9),
    4. new JProperty("Call of Duty", 7.5));
    5. File.WriteAllText(@"c:\videogames.json", videogameRatings.ToString());
    6. // write JSON directly to a file
    7. using (StreamWriter file = File.CreateText(@"c:\videogames.json"))
    8. using (JsonTextWriter writer = new JsonTextWriter(file))
    9. {
    10. videogameRatings.WriteTo(writer);
    11. }

  • 建立JObject物件,JArray物件
    1. JArray array = new JArray();
    2. array.Add("Manual text");
    3. array.Add(new DateTime(2000, 5, 23));
    4. JObject o = new JObject();
    5. o["MyArray"] = array;
    6. string json = o.ToString();
    7. // {
    8. // "MyArray": [
    9. // "Manual text",
    10. // "2000-05-23T00:00:00"
    11. // ]
    12. // }
  • 使用C#中的集合初始化語法建立複雜物件
    可以直接使用C#的初始化語法建立巢狀型別
    1. JObject o = new JObject
    2. {
    3. { "Cpu", "Intel" },
    4. { "Memory", 32 },
    5. {
    6. "Drives", new JArray
    7. {
    8. "DVD",
    9. "SSD"
    10. }
    11. }
    12. };
    13. Console.WriteLine(o.ToString());
    14. // {
    15. // "Cpu": "Intel",
    16. // "Memory": 32,
    17. // "Drives": [
    18. // "DVD",
    19. // "SSD"
    20. // ]
    21. // }

    當然也可以用傳統的物件建立方法,但是會覺得繁瑣,看下面的例子:

    1. public class Post
    2. {
    3. public string Title { get; set; }
    4. public string Description { get; set; }
    5. public string Link { get; set; }
    6. public IList<string> Categories { get; set; }
    7. }
    8. List<Post> posts = GetPosts();
    9. JObject rss =
    10. new JObject(
    11. new JProperty("channel",
    12. new JObject(
    13. new JProperty("title", "James Newton-King"),
    14. new JProperty("link", "http://james.newtonking.com"),
    15. new JProperty("description", "James Newton-King's blog."),
    16. new JProperty("item",
    17. new JArray(
    18. from p in posts
    19. orderby p.Title
    20. select new JObject(
    21. new JProperty("title", p.Title),
    22. new JProperty("description", p.Description),
    23. new JProperty("link", p.Link),
    24. new JProperty("category",
    25. new JArray(
    26. from c in p.Categories
    27. select new JValue(c)))))))));
    28. Console.WriteLine(rss.ToString());
    29. // {
    30. // "channel": {
    31. // "title": "James Newton-King",
    32. // "link": "http://james.newtonking.com",
    33. // "description": "James Newton-King's blog.",
    34. // "item": [
    35. // {
    36. // "title": "Json.NET 1.3 + New license + Now on CodePlex",
    37. // "description": "Announcing the release of Json.NET 1.3, the MIT license and being available on CodePlex",
    38. // "link": "http://james.newtonking.com/projects/json-net.aspx",
    39. // "category": [
    40. // "Json.NET",
    41. // "CodePlex"
    42. // ]
    43. // },
    44. // {
    45. // "title": "LINQ to JSON beta",
    46. // "description": "Announcing LINQ to JSON",
    47. // "link": "http://james.newtonking.com/projects/json-net.aspx",
    48. // "category": [
    49. // "Json.NET",
    50. // "LINQ"
    51. // ]
    52. // }
    53. // ]
    54. // }
    55. // }

  • 動態建立JObject和JArray物件
    使用C#的動態型別,可以動態的建立JObject和JArray物件
    1. dynamic product = new JObject();
    2. product.ProductName = "Elbow Grease";
    3. product.Enabled = true;
    4. product.Price = 4.90m;
    5. product.StockCount = 9000;
    6. product.StockValue = 44100;
    7. product.Tags = new JArray("Real", "OnSale");
    8. Console.WriteLine(product.ToString());
    9. // {
    10. // "ProductName": "Elbow Grease",
    11. // "Enabled": true,
    12. // "Price": 4.90,
    13. // "StockCount": 9000,
    14. // "StockValue": 44100,
    15. // "Tags": [
    16. // "Real",
    17. // "OnSale"
    18. // ]
    19. // }

  • 使用JTokenWriter動態建立JObject物件
    JTokenWriter類有一套完整的節點寫入方法,詳細文件在此,在寫入的過程,有點像在JS中新增Dom元素。
    1. JTokenWriter writer = new JTokenWriter();
    2. writer.WriteStartObject();
    3. writer.WritePropertyName("name1");
    4. writer.WriteValue("value1");
    5. writer.WritePropertyName("name2");
    6. writer.WriteStartArray();
    7. writer.WriteValue(1);
    8. writer.WriteValue(2);
    9. writer.WriteEndArray();
    10. writer.WriteEndObject();
    11. JObject o = (JObject)writer.Token;
    12. Console.WriteLine(o.ToString());
    13. // {
    14. // "name1": "value1",
    15. // "name2": [
    16. // 1,
    17. // 2
    18. // ]
    19. // }

  • 從自定義物件生成JObject物件
    使用FromObject函式可以從自定義的類中生產JObject物件,用法如下:
    1. public class Computer
    2. {
    3. public string Cpu { get; set; }
    4. public int Memory { get; set; }
    5. public IList<string> Drives { get; set; }
    6. }
    7. JValue i = (JValue)JToken.FromObject(12345);
    8. Console.WriteLine(i.Type);
    9. // Integer
    10. Console.WriteLine(i.ToString());
    11. // 12345
    12. JValue s = (JValue)JToken.FromObject("A string");
    13. Console.WriteLine(s.Type);
    14. // String
    15. Console.WriteLine(s.ToString());
    16. // A string
    17. Computer computer = new Computer
    18. {
    19. Cpu = "Intel",
    20. Memory = 32,
    21. Drives = new List<string>
    22. {
    23. "DVD",
    24. "SSD"
    25. }
    26. };
    27. JObject o = (JObject)JToken.FromObject(computer);
    28. Console.WriteLine(o.ToString());
    29. // {
    30. // "Cpu": "Intel",
    31. // "Memory": 32,
    32. // "Drives": [
    33. // "DVD",
    34. // "SSD"
    35. // ]
    36. // }
    37. JArray a = (JArray)JToken.FromObject(computer.Drives);
    38. Console.WriteLine(a.ToString());
    39. // [
    40. // "DVD",
    41. // "SSD"
    42. // ]

    這個函式還可以直接從匿名物件建立:

    1. List<Post> posts = new List<Post>
    2. {
    3. new Post
    4. {
    5. Title = "Episode VII",
    6. Description = "Episode VII production",
    7. Categories = new List<string>
    8. {
    9. "episode-vii",
    10. "movie"
    11. },
    12. Link = "episode-vii-production.aspx"
    13. }
    14. };
    15. JObject o = JObject.FromObject(new
    16. {
    17. channel = new
    18. {
    19. title = "Star Wars",
    20. link = "http://www.starwars.com",
    21. description = "Star Wars blog.",
    22. item =
    23. from p in posts
    24. orderby p.Title
    25. select new
    26. {
    27. title = p.Title,
    28. description = p.Description,
    29. link = p.Link,
    30. category = p.Categories
    31. }
    32. }
    33. });
    34. Console.WriteLine(o.ToString());
    35. // {
    36. // "channel": {
    37. // "title": "Star Wars",
    38. // "link": "http://www.starwars.com",
    39. // "description": "Star Wars blog.",
    40. // "item": [
    41. // {
    42. // "title": "Episode VII",
    43. // "description": "Episode VII production",
    44. // "link": "episode-vii-production.aspx",
    45. // "category": [
    46. // "episode-vii",
    47. // "movie"
    48. // ]
    49. // }
    50. // ]
    51. // }
    52. // }

從JObject恢復出類物件
這個實際上沒有什麼技巧,只是寫一個動態建立函式:

  1. public class BlogPost
  2. {
  3. public string Title { get; set; }
  4. public string AuthorName { get; set; }
  5. public string AuthorTwitter { get; set; }
  6. public string Body { get; set; }
  7. public DateTime PostedDate { get; set; }
  8. }
  9. string json = @"[
  10. {
  11. 'Title': 'Json.NET is awesome!',
  12. 'Author': {
  13. 'Name': 'James Newton-King',
  14. 'Twitter': '@JamesNK',
  15. 'Picture': '/jamesnk.png'
  16. },
  17. 'Date': '2013-01-23T19:30:00',
  18. 'BodyHtml': '&lt;h3&gt;Title!&lt;/h3&gt;\r\n&lt;p&gt;Content!&lt;/p&gt;'
  19. }
  20. ]";
  21. JArray blogPostArray = JArray.Parse(json);
  22. IList<BlogPost> blogPosts = blogPostArray.Select(p => new BlogPost
  23. {
  24. Title = (string)p["Title"],
  25. AuthorName = (string)p["Author"]["Name"],
  26. AuthorTwitter = (string)p["Author"]["Twitter"],
  27. PostedDate = (DateTime)p["Date"],
  28. Body = HttpUtility.HtmlDecode((string)p["BodyHtml"])
  29. }).ToList();
  30. Console.WriteLine(blogPosts[0].Body);
  31. // <h3>Title!</h3>
  32. // <p>Content!</p>

那麼反過來,也可以用這個方法將物件變為Jobject物件:

  1. IList<BlogPost> blogPosts = new List<BlogPost>
  2. {
  3. new BlogPost
  4. {
  5. Title = "Json.NET is awesome!",
  6. AuthorName = "James Newton-King",
  7. AuthorTwitter = "JamesNK",
  8. PostedDate = new DateTime(2013, 1, 23, 19, 30, 0),
  9. Body = @"<h3>Title!</h3><p>Content!</p>"
  10. }
  11. };
  12. JArray blogPostsArray = new JArray(
  13. blogPosts.Select(p => new JObject
  14. {
  15. { "Title", p.Title },
  16. {
  17. "Author", new JObject
  18. {
  19. { "Name", p.AuthorName },
  20. { "Twitter", p.AuthorTwitter }
  21. }
  22. },
  23. { "Date", p.PostedDate },
  24. { "BodyHtml", HttpUtility.HtmlEncode(p.Body) },
  25. })
  26. );
  27. Console.WriteLine(blogPostsArray.ToString());
  28. // [
  29. // {
  30. // "Title": "Json.NET is awesome!",
  31. // "Author": {
  32. // "Name": "James Newton-King",
  33. // "Twitter": "JamesNK"
  34. // },
  35. // "Date": "2013-01-23T19:30:00",
  36. // "BodyHtml": "&lt;h3&gt;Title!&lt;/h3&gt;&lt;p&gt;Content!&lt;/p&gt;"
  37. // }
  38. // ]

3.2 修改Json(使用JObject)

JObject物件很容易修改Json檔案,修改包括增,刪,重寫,等操作。

  1. string json = @"{
  2. 'channel': {
  3. 'title': 'Star Wars',
  4. 'link': 'http://www.starwars.com',
  5. 'description': 'Star Wars blog.',
  6. 'obsolete': 'Obsolete value',
  7. 'item': []
  8. }
  9. }";
  10. JObject rss = JObject.Parse(json);
  11. JObject channel = (JObject)rss["channel"];
  12. channel["title"] = ((string)channel["title"]).ToUpper();
  13. channel["description"] = ((string)channel["description"]).ToUpper();
  14. channel.Property("obsolete").Remove();
  15. channel.Property("description").AddAfterSelf(new JProperty("new", "New value"));
  16. JArray item = (JArray)channel["item"];
  17. item.Add("Item 1");
  18. item.Add("Item 2");
  19. Console.WriteLine(rss.ToString());
  20. // {
  21. // "channel": {
  22. // "title": "STAR WARS",
  23. // "link": "http://www.starwars.com",
  24. // "description": "STAR WARS BLOG.",
  25. // "new": "New value",
  26. // "item": [
  27. // "Item 1",
  28. // "Item 2"
  29. // ]
  30. // }
  31. // }

在上面的例子,依次進行了:使用函式修改值,刪除屬性,在某屬性後面新增屬性,在陣列內部新增成員。


3.3 合併Json檔案

合併Json檔案也很簡單,類似於兩個集合的操作,看下面的例子:

  1. JObject o1 = JObject.Parse(@"{
  2. 'FirstName': 'John',
  3. 'LastName': 'Smith',
  4. 'Enabled': false,
  5. 'Roles': [ 'User' ]
  6. }");
  7. JObject o2 = JObject.Parse(@"{
  8. 'Enabled': true,
  9. 'Roles': [ 'User', 'Admin' ]
  10. }");
  11. o1.Merge(o2, new JsonMergeSettings
  12. {
  13. // union array values together to avoid duplicates
  14. MergeArrayHandling = MergeArrayHandling.Union
  15. });
  16. string json = o1.ToString();
  17. // {
  18. // "FirstName": "John",
  19. // "LastName": "Smith",
  20. // "Enabled": true,
  21. // "Roles": [
  22. // "User",
  23. // "Admin"
  24. // ]
  25. // }

這裡要說的是,可以使用MergeArrayHandling物件來設定合併方式,上面使用的是合併模式:Union,即當前Json有時,只會出現一次,此外還有:

類似與數學中的並集,補集,疊加。

此外,MergeNullValueHandling屬性可以控制在合併是值為Null的要不要被忽略;


3.4 將Json型別轉為普通型別

JObject物件中的成員型別並不是C#中型別,要變成普通型別,你需要使用:ToObject<T>() 做最後一步轉換,其中T為你想轉換為的型別,不進行轉換直接列印可能會報錯。

  1. JValue v1 = new JValue(true);
  2. bool b = v1.ToObject<bool>();
  3. Console.WriteLine(b);
  4. // true
  5. int i = v1.ToObject<int>();
  6. Console.WriteLine(i);
  7. // 1
  8. string s = v1.ToObject<string>();
  9. Console.WriteLine(s);
  10. // "True"

雖然很簡單,但是剛接觸NewtonJson可能會遇到這個問題。


3.5 判斷Json檔案是否相等 &深度複製Json檔案

使用JToken.DeepEquals函式判斷兩個JObject物件是否相等,這個相等必須要所有屬性值一模一樣:

  1. JValue s1 = new JValue("A string");
  2. JValue s2 = new JValue("A string");
  3. JValue s3 = new JValue("A STRING");
  4. Console.WriteLine(JToken.DeepEquals(s1, s2));
  5. // true
  6. Console.WriteLine(JToken.DeepEquals(s2, s3));
  7. // false
  8. JObject o1 = new JObject
  9. {
  10. { "Integer", 12345 },
  11. { "String", "A string" },
  12. { "Items", new JArray(1, 2) }
  13. };
  14. JObject o2 = new JObject
  15. {
  16. { "Integer", 12345 },
  17. { "String", "A string" },
  18. { "Items", new JArray(1, 2) }
  19. };
  20. Console.WriteLine(JToken.DeepEquals(o1, o2));
  21. // true
  22. Console.WriteLine(JToken.DeepEquals(s1, o1["String"]));
  23. // true

注意:雖然兩個json問價的內容一樣,但是它們畢竟是2個不同的物件,使用JToken.RefernceEquals判斷會返回false。

對於拷貝,主要是對內容進行拷貝,但是建立的是新物件:

  1. JValue s1 = new JValue("A string");
  2. JValue s2 = new JValue("A string");
  3. JValue s3 = new JValue("A STRING");
  4. Console.WriteLine(JToken.DeepEquals(s1, s2));
  5. // true
  6. Console.WriteLine(JToken.DeepEquals(s2, s3));
  7. // false
  8. JObject o1 = new JObject
  9. {
  10. { "Integer", 12345 },
  11. { "String", "A string" },
  12. { "Items", new JArray(1, 2) }
  13. };
  14. JObject o2 = new JObject
  15. {
  16. { "Integer", 12345 },
  17. { "String", "A string" },
  18. { "Items", new JArray(1, 2) }
  19. };
  20. Console.WriteLine(JToken.DeepEquals(o1, o2));
  21. // true
  22. Console.WriteLine(JToken.DeepEquals(s1, o1["String"]));
  23. // true

4. 總結

以上就是對NewtonJson的一點總結,很多是參考官方文件,我只選了一些常見的知識點進行介紹,建議大家遇到不能解決的問題時還是看官方文件。

一般而言,我更傾向與使用JObject物件處理Json,因為你可以獲得JavaScript處理Json的90%的體驗,使用起來很靈活,而且結合Linq語法,可以很方便的處理它。

轉載地址:

https://blog.csdn.net/q__y__l/article/details/103566693#2.%E8%A7%A3%E6%9E%90Json%E6%96%87%E4%BB%B6