C# 解析Json檔案(使用NewtonJson庫)
C#中解析json檔案有很多種方法,在多種方法中一般都會提到一個十分優秀的庫:NewtonJson。使用NewtonJson處理Json檔案十分高效,而且在配置也十分簡單,直接在Nuget包中匯入即可。
目錄
1.匯入NewtonJson庫
編寫C#程式肯定是在visual studio中寫(暫不考慮unity等其他地方),那麼無論你是在windows下還是Mac OS X下,visual studio都自帶nuget包管理工具。本文以Mac OS X平臺為例,首先新建一個專案叫ParseJson,然後在專案的依賴項中彈出快捷選單,如下:
點選Nuget包管理,彈出一個視窗:
可以看到第一個就是我們想要的包,選中然後新增包即可。新增完成後在你的程式中新增下面兩行:
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
至此,基本配置就完成了。
2.解析Json檔案
NewtonJson官網有詳細的使用文件教程,有很多示例程式碼,這裡就不過多介紹了,遇到不懂的問題可以去文件裡面找資料。
2.1 最簡單的序列化與反序列化
假設你在C#中有一個定義好的類,然後你生成了一個物件,想把這個物件儲存為Json檔案,你可以用SerializeObject()函式處理。看下面的程式碼:
-
using System;
- using System.IO;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
- namespace ParseJson
- {
- class Product
- {
- public string Name;
- public DateTime ExpiryDate;
- public Decimal Price;
- public String[] Sizes;
- }
- class Program
- {
- static void Main(string[] args)
- {
- Product product = new Product()
- {
- Name = "Apple",
- ExpiryDate=new DateTime(2020,12,30),
- Price=2.99M,
- Sizes=new string[] {"small","medium","large"}
- };
- string output = JsonConvert.SerializeObject(product);
- //將Json檔案以字串的形式儲存
- StreamWriter sw = new StreamWriter(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product.dat");
- sw.Write(output);
- sw.Close();
- Console.WriteLine(output);
- }
- }
- }
這裡我們建立了一個Product類,並且例項化了一個物件,利用JsonConvert.SerializeObject(product)將其轉化為Json檔案,並以字串的形式儲存在變數output,我們很容易將字串儲存到本地。可以檢視儲存到本地後的檔案內容:
既然我們可以將物件以json檔案的形式儲存,自然我們也應該可以從Json格式恢復成物件,做法很簡單,假設我們已經讀取了本地檔案Product.dat,並儲存到了字串變數output中,我們要從中恢復成Product物件只需一句話:
- //恢復物件
- Product p = JsonConvert.DeserializeObject<Product>(output);
值得一提的的是,當你的物件裡面有集合物件時:
- public class Acount
- {
- public string Email {get;set;}
- public Ilist<string> Roles {get;set}
- }
也可以直接使用上面的方法,轉化為Json檔案後,集合物件會以陣列的形式儲存。
2.2 序列化集合和字典
除了序列化自定義的類,還可以將C#中的集合物件序列化,這裡我就不跑程式碼了,直接搬運官網的例子。
序列化字典:
- List<string> videogames = new List<string>
- {
- "Starcraft",
- "Halo",
- "Legend of Zelda"
- };
- string json = JsonConvert.SerializeObject(videogames);
- Console.WriteLine(json);
- // ["Starcraft","Halo","Legend of Zelda"]
List集合被轉化為了陣列,當然List裡面可以是複雜的型別,如使用我們之前定義的Product:
- Product product1 = new Product()
- {
- Name = "Apple",
- ExpiryDate=new DateTime(2020,12,30),
- Price=2.99M,
- Sizes=new string[] {"small","medium","large"}
- };
- Product product2 = new Product()
- {
- Name = "cup",
- ExpiryDate = new DateTime(2099, 1, 1),
- Price = 9.99M,
- Sizes = new string[] { "s", "L", "M", "xL" }
- };
- List<Product> list = new List<Product>() { product1, product2 };
- string json = JsonConvert.SerializeObject(list);
- 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"]}]
序列化字典例子如下:
- Dictionary<string, int> points = new Dictionary<string, int>
- {
- { "James", 9001 },
- { "Jo", 3474 },
- { "Jess", 11926 }
- };
- string json = JsonConvert.SerializeObject(points, Formatting.Indented);
- Console.WriteLine(json);
- // {
- // "James": 9001,
- // "Jo": 3474,
- // "Jess": 11926
- // }
這裡SerializeObject多了一個引數,Indented表示轉化為的Json檔案帶縮排,這樣輸出會更加直觀清晰。
2.3 反序列化集合和字典
反序列化和之前講的類似,反序列化需要給出轉化為物件的型別,反序列化集合:
- string json = @"[
- {
- 'Name': 'Product 1',
- 'ExpiryDate': '2000-12-29T00:00Z',
- 'Price': 99.95,
- 'Sizes': null
- },
- {
- 'Name': 'Product 2',
- 'ExpiryDate': '2009-07-31T00:00Z',
- 'Price': 12.50,
- 'Sizes': null
- }
- ]";
- List<Product> products = JsonConvert.DeserializeObject<List<Product>>(json);
- Console.WriteLine(products.Count);
- // 2
- Product p1 = products[0];
- Console.WriteLine(p1.Name);
- // Product 1
反序列化集合一般轉為List型別,如果你需要轉化為其它型別,你可以後續再處理。反序列化字典也是如此:
- string json = @"{""key1"":""value1"",""key2"":""value2""}";
- Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
- Console.WriteLine(values.Count);
- // 2
- Console.WriteLine(values["key1"]);
- // value1
2.4 將物件儲存為Json檔案&從檔案解析為json
在最開始的例子中,我們將物件先轉為字串,然後再寫入一個dat檔案中,事實上完全可以將檔案儲存為Json檔案。有兩種思路,一種還是先將json變為字串,然後儲存到自定義的json檔案中。
- Product product1 = new Product()
- {
- Name = "Apple",
- ExpiryDate=new DateTime(2020,12,30),
- Price=2.99M,
- Sizes=new string[] {"small","medium","large"}
- };
- Product product2 = new Product()
- {
- Name = "cup",
- ExpiryDate = new DateTime(2099, 1, 1),
- Price = 9.99M,
- Sizes = new string[] { "s", "L", "M", "xL" }
- };
- List<Product> list = new List<Product>() { product1, product2 };
- File.WriteAllText(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product1.json",
- JsonConvert.SerializeObject(list,Formatting.Indented));
我們用Vscode開啟product1.json如下:
另一種方法是直接將物件轉化為json檔案:
- using(StreamWriterfile=File.CreateText(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product2.json"))
- {
- JsonSerializer serializer = new JsonSerializer() { Formatting=Formatting.Indented};
- serializer.Serialize(file, list);
- }
得到的product2.json和前面是一樣的。
從json檔案中解析物件的操作幾乎是一模一樣的:只需要將SerializeObject函式換成DeserializeObject,WriteAllText換成ReadAllText,CreatText換成OpenText。
- // read file into a string and deserialize JSON to a type
- Movie movie1 = JsonConvert.DeserializeObject<Movie>(File.ReadAllText(@"c:\movie.json"));
- // deserialize JSON directly from a file
- using (StreamReader file = File.OpenText(@"c:\movie.json"))
- {
- JsonSerializer serializer = new JsonSerializer();
- Movie movie2 = (Movie)serializer.Deserialize(file, typeof(Movie));
- }
注意:直接從json轉為物件,除了提供物件型別引數,還需要強制轉化操作。
2.5 有條件的序列化物件成員
NewTonJson還支援有條件序列化物件,即對某些屬性進行判斷,如果不滿足要求,則忽略該屬性。
要實現部分屬性的條件序列化,需要新增一些函式,這個函式和屬性一一對應,函式名為:ShouldSerialize+屬性名,函式的返回值為bool型別,當返回為True時,該屬性將被序列化,為False則被忽略。看一個官方例子:
首先你有這樣一個簡單類:
- public class Employee
- {
- public string Name { get; set; }
- public Employee Manager { get; set; }
- public bool ShouldSerializeManager()
- {
- // don't serialize the Manager property if an employee is their own manager
- return (Manager != this);
- }
- }
這裡已經添加了Manager這個篩選函式,所以當Manage就是自己時,這個Manage會被忽略。
- Employee joe = new Employee();
- joe.Name = "Joe Employee";
- Employee mike = new Employee();
- mike.Name = "Mike Manager";
- joe.Manager = mike;
- // mike is his own manager
- // ShouldSerialize will skip this property
- mike.Manager = mike;
- string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented);
- Console.WriteLine(json);
- // [
- // {
- // "Name": "Joe Employee",
- // "Manager": {
- // "Name": "Mike Manager"
- // }
- // },
- // {
- // "Name": "Mike Manager"
- // }
- // ]
不過一般而言,當資料量不是很大時,你可以有條件的使用Json檔案的資料,也就是我不用這個屬性,我就可以假設它不存在。
上面的例子固然可行,但是有個很麻煩的問題:你必須在設計類的時候就確定好條件序列化屬性。
那麼有沒有更好的控制辦法呢?答案是肯定的。
通過派生介面:IContractResolver,生產一個篩選物件可以實現定製化篩選,你不用在你的類裡面新增函式。一般我們不用直接派生自IContractResolver,而是派生自它的一個派生類:
DefaultContractResolver
看下面這個例子,我們假設有這樣一個圖書類:
- public class Book
- {
- public string BookName { get; set; }
- public decimal BookPrice { get; set; }
- public string AuthorName { get; set; }
- public int AuthorAge { get; set; }
- public string AuthorCountry { get; set; }
- }
我們想實現序列化以字母“A”開頭或者“B”開頭的屬性,顯然用之前的方法會很麻煩,我們用剛才的方法,派生一個篩選類:
- public class DynamicContractResolver : DefaultContractResolver
- {
- private readonly char _startingWithChar;
- public DynamicContractResolver(char startingWithChar)
- {
- _startingWithChar = startingWithChar;
- }
- protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
- {
- IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
- // only serializer properties that start with the specified character
- //只需要在這裡新增屬性篩選
- properties =
- properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList();
- return properties;
- }
- }
這個類有一個成員變數:_startingWithChar,用來接受篩選引數;一個建構函式;一個篩選函式:CreateProperties,這個函式返回一個屬性集合,函式首先獲得類的所有屬性,儲存帶properties中,然後你根據需求對properties進行處理。上面的函式實現了根據屬性名的首字母進行篩選。所以我們就可以用這個類的物件作為引數去實現我們的需求:
- Book book = new Book
- {
- BookName = "The Gathering Storm",
- BookPrice = 16.19m,
- AuthorName = "Brandon Sanderson",
- AuthorAge = 34,
- AuthorCountry = "United States of America"
- };
- string startingWithA = JsonConvert.SerializeObject(book, Formatting.Indented,
- new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('A') });
- // {
- // "AuthorName": "Brandon Sanderson",
- // "AuthorAge": 34,
- // "AuthorCountry": "United States of America"
- // }
- string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented,
- new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') });
- // {
- // "BookName": "The Gathering Storm",
- // "BookPrice": 16.19
- // }
DefaultContractResolver中的CreateProperties是對多個屬性篩選而言的,回到本節最開始的問題,我只想對Manage成員進行篩選,那麼可以用CreateProperty這個函式,具體用法如下:
- public class ShouldSerializeContractResolver : DefaultContractResolver
- {
- public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
- protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
- {
- JsonProperty property = base.CreateProperty(member, memberSerialization);
- if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
- {
- property.ShouldSerialize =
- instance =>
- {
- Employee e = (Employee)instance;
- return e.Manager != e;
- };
- }
- return property;
- }
- }
這個的用法和前面類似,不過這裡直接聲明瞭一個類的靜態成員,所以不需要new一個物件:
- string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented,
- new JsonSerializerSettings { ContractResolver=ShouldSerializeContractResolver.Instance});
順便要說明的是,為了成功執行需要新增新的名稱空間:
- using Newtonsoft.Json.Linq;
- using Newtonsoft.Json.Serialization;
這裡給出官網關於DefaultContrctResolver的連結。
2.6 解析匿名類
對於匿名類,由於序列化不需要給出物件型別,所以可以依然使用前面序列化自定義類的方法,但是反序列是需要提供型別的,那對於匿名類怎麼辦?,這個NewtonJson也替我們考慮了,例子如下:
- var definition = new { Name = "" };
- string json1 = @"{'Name':'James'}";
- var customer1 = JsonConvert.DeserializeAnonymousType(json1, definition);
- Console.WriteLine(customer1.Name);
- // James
- string json2 = @"{'Name':'Mike'}";
- var customer2 = JsonConvert.DeserializeAnonymousType(json2, definition);
- Console.WriteLine(customer2.Name);
- // Mike
辦法很簡單,直接給一個匿名類的定義物件,傳入引數即可。
2.7 將派生類解析為基類
將派生類解析為基類,需要一個派生自CustomeCreationConverter的物件,操作起來其實很簡單,看一下官方的例子:
首先你有一個Person基類,然後派生了Employee類,並寫好了派生自CustomeCreationConverter類的類PersonConverter:
- public class Person
- {
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public DateTime BirthDate { get; set; }
- }
- public class Employee : Person
- {
- public string Department { get; set; }
- public string JobTitle { get; set; }
- }
- public class PersonConverter : CustomCreationConverter<Person>
- {
- public override Person Create(Type objectType)
- {
- return new Employee();
- }
- }
然後你想將Employee物件解析為Person,只需要傳入一個PersonConvrter物件:
- string json = @"{
- 'Department': 'Furniture',
- 'JobTitle': 'Carpenter',
- 'FirstName': 'John',
- 'LastName': 'Joinery',
- 'BirthDate': '1983-02-02T00:00:00'
- }";
- Person person = JsonConvert.DeserializeObject<Person>(json, new PersonConverter());
- Console.WriteLine(person.GetType().Name);
- // Employee
- Employee employee = (Employee)person;
- Console.WriteLine(employee.JobTitle);
- // Carpenter
從結果可以看出,雖然是以Person解析的,但是實際上仍然是Employee型別。
2.8 防止重複寫值
如果一個類的建構函式本身就對成員進行了賦值,那麼在反序列化時,可能會呼叫一次建構函式容易造成重複寫入,看下面的例子:
- public class UserViewModel
- {
- public string Name { get; set; }
- public IList<string> Offices { get; private set; }
- public UserViewModel()
- {
- Offices = new List<string>
- {
- "Auckland",
- "Wellington",
- "Christchurch"
- };
- }
- }
建構函式對成員Offices進行了賦值:
- string json = @"{
- 'Name': 'James',
- 'Offices': [
- 'Auckland',
- 'Wellington',
- 'Christchurch'
- ]
- }";
- UserViewModel model1 = JsonConvert.DeserializeObject<UserViewModel>(json);
- foreach (string office in model1.Offices)
- {
- Console.WriteLine(office);
- }
- // Auckland
- // Wellington
- // Christchurch
- // Auckland
- // Wellington
- // Christchurch
- UserViewModel model2 = JsonConvert.DeserializeObject<UserViewModel>(json, new JsonSerializerSettings
- {
- ObjectCreationHandling = ObjectCreationHandling.Replace
- });
- foreach (string office in model2.Offices)
- {
- Console.WriteLine(office);
- }
- // Auckland
- // Wellington
- // Christchurch
如果不新增設定,Offices就是存在2遍初始值,為例避免這種情況,在反序列化的時候傳入了一個setting物件,其ObejctCreationHandling屬性為Replcae。
2.9 取消C#預設引數賦值& 過濾值為null的屬性
C#對於沒有賦值的型別提供一個預設值,如Int型別預設值為0,string型別預設值為null,如果當一個物件的成員沒有被賦值,我們希望得到的是一個空json,那麼需要將setting的DefaultValueHandleling設定為Ignore。
- public class Person
- {
- public string Name { get; set; }
- public int Age { get; set; }
- public Person Partner { get; set; }
- public decimal? Salary { get; set; }
- }
- Person person = new Person();
- string jsonIncludeDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented);
- Console.WriteLine(jsonIncludeDefaultValues);
- // {
- // "Name": null,
- // "Age": 0,
- // "Partner": null,
- // "Salary": null
- // }
- string jsonIgnoreDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
- {
- DefaultValueHandling = DefaultValueHandling.Ignore
- });
- Console.WriteLine(jsonIgnoreDefaultValues);
- // {}
過濾值為null的成員只需要將NullValueHandling屬性設定為Ignore。
- public class Person
- {
- public string Name { get; set; }
- public int Age { get; set; }
- public Person Partner { get; set; }
- public decimal? Salary { get; set; }
- }
- Person person = new Person
- {
- Name = "Nigal Newborn",
- Age = 1
- };
- string jsonIncludeNullValues = JsonConvert.SerializeObject(person, Formatting.Indented);
- Console.WriteLine(jsonIncludeNullValues);
- // {
- // "Name": "Nigal Newborn",
- // "Age": 1,
- // "Partner": null,
- // "Salary": null
- // }
- string jsonIgnoreNullValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
- {
- NullValueHandling = NullValueHandling.Ignore
- });
- Console.WriteLine(jsonIgnoreNullValues);
- // {
- // "Name": "Nigal Newborn",
- // "Age": 1
- // }
2.10 型別缺少成員報錯
當我們將下面的json檔案:
- string json = @"{
- 'FullName': 'Dan Deleted',
- 'Deleted': true,
- 'DeletedDate': '2013-01-20T00:00:00'
- }";
解析為Account型別時:
- public class Account
- {
- public string FullName { get; set; }
- public bool Deleted { get; set; }
- }
是可以成功的,因為json中包含Accout類中所有成員屬性。但是如果我們要嚴格轉化(特別是在Accout漏掉屬性時),需要報錯,那麼就需要設定setting的MissingMemberHandling屬性。
- try
- {
- JsonConvert.DeserializeObject<Account>(json, new JsonSerializerSettings
- {
- MissingMemberHandling = MissingMemberHandling.Error
- });
- }
- catch (JsonSerializationException ex)
- {
- Console.WriteLine(ex.Message);
- // Could not find member 'DeletedDate' on object of type 'Account'. Path 'DeletedDate', line 4, position 23.
- }
此外還有很多設定用來解決各種問題,上面只是列出了一些常見的,有需要的還是要去官網檢視。
3. 使用Linq處理json
使用ling處理json最大的好處是擺脫了物件的束縛,使用NewtonJson自帶的一套體系。我們都知道JavaScript原生支援Json,直接可以將Json檔案轉化為一個物件,JObject的建立也是為了實現這樣一個類似的功能。
3.1 解析Json的基本操作
- 解析文字為Json物件
直接使用JObject.Parse(obj)即可- string json = @"{
- CPU: 'Intel',
- Drives: [
- 'DVD read/writer',
- '500 gigabyte hard drive'
- ]
- }";
- JObject o = JObject.Parse(json);
- Console.WriteLine(o.ToString());
- // {
- // "CPU": "Intel",
- // "Drives": [
- // "DVD read/writer",
- // "500 gigabyte hard drive"
- // ]
- // }
- 將陣列解析為Json
直接使用JArray類- string json = @"[
- 'Small',
- 'Medium',
- 'Large'
- ]";
- JArray a = JArray.Parse(json);
- Console.WriteLine(a.Tostring());
- // [
- // "Small",
- // "Medium",
- // "Large"
- // ]
-
從本地Json檔案中解析
讀操作- using (StreamReader reader = File.OpenText(@"c:\person.json"))
- {
- JObject o = (JObject)JToken.ReadFrom(new JsonTextReader(reader));
- // do stuff
- }
寫操作
- JObject videogameRatings = new JObject(
- new JProperty("Halo", 9),
- new JProperty("Starcraft", 9),
- new JProperty("Call of Duty", 7.5));
- File.WriteAllText(@"c:\videogames.json", videogameRatings.ToString());
- // write JSON directly to a file
- using (StreamWriter file = File.CreateText(@"c:\videogames.json"))
- using (JsonTextWriter writer = new JsonTextWriter(file))
- {
- videogameRatings.WriteTo(writer);
- }
- 建立JObject物件,JArray物件
- JArray array = new JArray();
- array.Add("Manual text");
- array.Add(new DateTime(2000, 5, 23));
- JObject o = new JObject();
- o["MyArray"] = array;
- string json = o.ToString();
- // {
- // "MyArray": [
- // "Manual text",
- // "2000-05-23T00:00:00"
- // ]
- // }
- 使用C#中的集合初始化語法建立複雜物件
可以直接使用C#的初始化語法建立巢狀型別- JObject o = new JObject
- {
- { "Cpu", "Intel" },
- { "Memory", 32 },
- {
- "Drives", new JArray
- {
- "DVD",
- "SSD"
- }
- }
- };
- Console.WriteLine(o.ToString());
- // {
- // "Cpu": "Intel",
- // "Memory": 32,
- // "Drives": [
- // "DVD",
- // "SSD"
- // ]
- // }
當然也可以用傳統的物件建立方法,但是會覺得繁瑣,看下面的例子:
- public class Post
- {
- public string Title { get; set; }
- public string Description { get; set; }
- public string Link { get; set; }
- public IList<string> Categories { get; set; }
- }
- List<Post> posts = GetPosts();
- JObject rss =
- new JObject(
- new JProperty("channel",
- new JObject(
- new JProperty("title", "James Newton-King"),
- new JProperty("link", "http://james.newtonking.com"),
- new JProperty("description", "James Newton-King's blog."),
- new JProperty("item",
- new JArray(
- from p in posts
- orderby p.Title
- select new JObject(
- new JProperty("title", p.Title),
- new JProperty("description", p.Description),
- new JProperty("link", p.Link),
- new JProperty("category",
- new JArray(
- from c in p.Categories
- select new JValue(c)))))))));
- Console.WriteLine(rss.ToString());
- // {
- // "channel": {
- // "title": "James Newton-King",
- // "link": "http://james.newtonking.com",
- // "description": "James Newton-King's blog.",
- // "item": [
- // {
- // "title": "Json.NET 1.3 + New license + Now on CodePlex",
- // "description": "Announcing the release of Json.NET 1.3, the MIT license and being available on CodePlex",
- // "link": "http://james.newtonking.com/projects/json-net.aspx",
- // "category": [
- // "Json.NET",
- // "CodePlex"
- // ]
- // },
- // {
- // "title": "LINQ to JSON beta",
- // "description": "Announcing LINQ to JSON",
- // "link": "http://james.newtonking.com/projects/json-net.aspx",
- // "category": [
- // "Json.NET",
- // "LINQ"
- // ]
- // }
- // ]
- // }
- // }
- 動態建立JObject和JArray物件
使用C#的動態型別,可以動態的建立JObject和JArray物件- dynamic product = new JObject();
- product.ProductName = "Elbow Grease";
- product.Enabled = true;
- product.Price = 4.90m;
- product.StockCount = 9000;
- product.StockValue = 44100;
- product.Tags = new JArray("Real", "OnSale");
- Console.WriteLine(product.ToString());
- // {
- // "ProductName": "Elbow Grease",
- // "Enabled": true,
- // "Price": 4.90,
- // "StockCount": 9000,
- // "StockValue": 44100,
- // "Tags": [
- // "Real",
- // "OnSale"
- // ]
- // }
- 使用JTokenWriter動態建立JObject物件
JTokenWriter類有一套完整的節點寫入方法,詳細文件在此,在寫入的過程,有點像在JS中新增Dom元素。- JTokenWriter writer = new JTokenWriter();
- writer.WriteStartObject();
- writer.WritePropertyName("name1");
- writer.WriteValue("value1");
- writer.WritePropertyName("name2");
- writer.WriteStartArray();
- writer.WriteValue(1);
- writer.WriteValue(2);
- writer.WriteEndArray();
- writer.WriteEndObject();
- JObject o = (JObject)writer.Token;
- Console.WriteLine(o.ToString());
- // {
- // "name1": "value1",
- // "name2": [
- // 1,
- // 2
- // ]
- // }
- 從自定義物件生成JObject物件
使用FromObject函式可以從自定義的類中生產JObject物件,用法如下:- public class Computer
- {
- public string Cpu { get; set; }
- public int Memory { get; set; }
- public IList<string> Drives { get; set; }
- }
- JValue i = (JValue)JToken.FromObject(12345);
- Console.WriteLine(i.Type);
- // Integer
- Console.WriteLine(i.ToString());
- // 12345
- JValue s = (JValue)JToken.FromObject("A string");
- Console.WriteLine(s.Type);
- // String
- Console.WriteLine(s.ToString());
- // A string
- Computer computer = new Computer
- {
- Cpu = "Intel",
- Memory = 32,
- Drives = new List<string>
- {
- "DVD",
- "SSD"
- }
- };
- JObject o = (JObject)JToken.FromObject(computer);
- Console.WriteLine(o.ToString());
- // {
- // "Cpu": "Intel",
- // "Memory": 32,
- // "Drives": [
- // "DVD",
- // "SSD"
- // ]
- // }
- JArray a = (JArray)JToken.FromObject(computer.Drives);
- Console.WriteLine(a.ToString());
- // [
- // "DVD",
- // "SSD"
- // ]
這個函式還可以直接從匿名物件建立:
- List<Post> posts = new List<Post>
- {
- new Post
- {
- Title = "Episode VII",
- Description = "Episode VII production",
- Categories = new List<string>
- {
- "episode-vii",
- "movie"
- },
- Link = "episode-vii-production.aspx"
- }
- };
- JObject o = JObject.FromObject(new
- {
- channel = new
- {
- title = "Star Wars",
- link = "http://www.starwars.com",
- description = "Star Wars blog.",
- item =
- from p in posts
- orderby p.Title
- select new
- {
- title = p.Title,
- description = p.Description,
- link = p.Link,
- category = p.Categories
- }
- }
- });
- Console.WriteLine(o.ToString());
- // {
- // "channel": {
- // "title": "Star Wars",
- // "link": "http://www.starwars.com",
- // "description": "Star Wars blog.",
- // "item": [
- // {
- // "title": "Episode VII",
- // "description": "Episode VII production",
- // "link": "episode-vii-production.aspx",
- // "category": [
- // "episode-vii",
- // "movie"
- // ]
- // }
- // ]
- // }
- // }
從JObject恢復出類物件
這個實際上沒有什麼技巧,只是寫一個動態建立函式:
- public class BlogPost
- {
- public string Title { get; set; }
- public string AuthorName { get; set; }
- public string AuthorTwitter { get; set; }
- public string Body { get; set; }
- public DateTime PostedDate { get; set; }
- }
- string json = @"[
- {
- 'Title': 'Json.NET is awesome!',
- 'Author': {
- 'Name': 'James Newton-King',
- 'Twitter': '@JamesNK',
- 'Picture': '/jamesnk.png'
- },
- 'Date': '2013-01-23T19:30:00',
- 'BodyHtml': '<h3>Title!</h3>\r\n<p>Content!</p>'
- }
- ]";
- JArray blogPostArray = JArray.Parse(json);
- IList<BlogPost> blogPosts = blogPostArray.Select(p => new BlogPost
- {
- Title = (string)p["Title"],
- AuthorName = (string)p["Author"]["Name"],
- AuthorTwitter = (string)p["Author"]["Twitter"],
- PostedDate = (DateTime)p["Date"],
- Body = HttpUtility.HtmlDecode((string)p["BodyHtml"])
- }).ToList();
- Console.WriteLine(blogPosts[0].Body);
- // <h3>Title!</h3>
- // <p>Content!</p>
那麼反過來,也可以用這個方法將物件變為Jobject物件:
- IList<BlogPost> blogPosts = new List<BlogPost>
- {
- new BlogPost
- {
- Title = "Json.NET is awesome!",
- AuthorName = "James Newton-King",
- AuthorTwitter = "JamesNK",
- PostedDate = new DateTime(2013, 1, 23, 19, 30, 0),
- Body = @"<h3>Title!</h3><p>Content!</p>"
- }
- };
- JArray blogPostsArray = new JArray(
- blogPosts.Select(p => new JObject
- {
- { "Title", p.Title },
- {
- "Author", new JObject
- {
- { "Name", p.AuthorName },
- { "Twitter", p.AuthorTwitter }
- }
- },
- { "Date", p.PostedDate },
- { "BodyHtml", HttpUtility.HtmlEncode(p.Body) },
- })
- );
- Console.WriteLine(blogPostsArray.ToString());
- // [
- // {
- // "Title": "Json.NET is awesome!",
- // "Author": {
- // "Name": "James Newton-King",
- // "Twitter": "JamesNK"
- // },
- // "Date": "2013-01-23T19:30:00",
- // "BodyHtml": "<h3>Title!</h3><p>Content!</p>"
- // }
- // ]
3.2 修改Json(使用JObject)
JObject物件很容易修改Json檔案,修改包括增,刪,重寫,等操作。
- string json = @"{
- 'channel': {
- 'title': 'Star Wars',
- 'link': 'http://www.starwars.com',
- 'description': 'Star Wars blog.',
- 'obsolete': 'Obsolete value',
- 'item': []
- }
- }";
- JObject rss = JObject.Parse(json);
- JObject channel = (JObject)rss["channel"];
- channel["title"] = ((string)channel["title"]).ToUpper();
- channel["description"] = ((string)channel["description"]).ToUpper();
- channel.Property("obsolete").Remove();
- channel.Property("description").AddAfterSelf(new JProperty("new", "New value"));
- JArray item = (JArray)channel["item"];
- item.Add("Item 1");
- item.Add("Item 2");
- Console.WriteLine(rss.ToString());
- // {
- // "channel": {
- // "title": "STAR WARS",
- // "link": "http://www.starwars.com",
- // "description": "STAR WARS BLOG.",
- // "new": "New value",
- // "item": [
- // "Item 1",
- // "Item 2"
- // ]
- // }
- // }
在上面的例子,依次進行了:使用函式修改值,刪除屬性,在某屬性後面新增屬性,在陣列內部新增成員。
3.3 合併Json檔案
合併Json檔案也很簡單,類似於兩個集合的操作,看下面的例子:
- JObject o1 = JObject.Parse(@"{
- 'FirstName': 'John',
- 'LastName': 'Smith',
- 'Enabled': false,
- 'Roles': [ 'User' ]
- }");
- JObject o2 = JObject.Parse(@"{
- 'Enabled': true,
- 'Roles': [ 'User', 'Admin' ]
- }");
- o1.Merge(o2, new JsonMergeSettings
- {
- // union array values together to avoid duplicates
- MergeArrayHandling = MergeArrayHandling.Union
- });
- string json = o1.ToString();
- // {
- // "FirstName": "John",
- // "LastName": "Smith",
- // "Enabled": true,
- // "Roles": [
- // "User",
- // "Admin"
- // ]
- // }
這裡要說的是,可以使用MergeArrayHandling物件來設定合併方式,上面使用的是合併模式:Union,即當前Json有時,只會出現一次,此外還有:
類似與數學中的並集,補集,疊加。
此外,MergeNullValueHandling屬性可以控制在合併是值為Null的要不要被忽略;
3.4 將Json型別轉為普通型別
JObject物件中的成員型別並不是C#中型別,要變成普通型別,你需要使用:ToObject<T>() 做最後一步轉換,其中T為你想轉換為的型別,不進行轉換直接列印可能會報錯。
- JValue v1 = new JValue(true);
- bool b = v1.ToObject<bool>();
- Console.WriteLine(b);
- // true
- int i = v1.ToObject<int>();
- Console.WriteLine(i);
- // 1
- string s = v1.ToObject<string>();
- Console.WriteLine(s);
- // "True"
雖然很簡單,但是剛接觸NewtonJson可能會遇到這個問題。
3.5 判斷Json檔案是否相等 &深度複製Json檔案
使用JToken.DeepEquals函式判斷兩個JObject物件是否相等,這個相等必須要所有屬性值一模一樣:
- JValue s1 = new JValue("A string");
- JValue s2 = new JValue("A string");
- JValue s3 = new JValue("A STRING");
- Console.WriteLine(JToken.DeepEquals(s1, s2));
- // true
- Console.WriteLine(JToken.DeepEquals(s2, s3));
- // false
- JObject o1 = new JObject
- {
- { "Integer", 12345 },
- { "String", "A string" },
- { "Items", new JArray(1, 2) }
- };
- JObject o2 = new JObject
- {
- { "Integer", 12345 },
- { "String", "A string" },
- { "Items", new JArray(1, 2) }
- };
- Console.WriteLine(JToken.DeepEquals(o1, o2));
- // true
- Console.WriteLine(JToken.DeepEquals(s1, o1["String"]));
- // true
注意:雖然兩個json問價的內容一樣,但是它們畢竟是2個不同的物件,使用JToken.RefernceEquals判斷會返回false。
對於拷貝,主要是對內容進行拷貝,但是建立的是新物件:
- JValue s1 = new JValue("A string");
- JValue s2 = new JValue("A string");
- JValue s3 = new JValue("A STRING");
- Console.WriteLine(JToken.DeepEquals(s1, s2));
- // true
- Console.WriteLine(JToken.DeepEquals(s2, s3));
- // false
- JObject o1 = new JObject
- {
- { "Integer", 12345 },
- { "String", "A string" },
- { "Items", new JArray(1, 2) }
- };
- JObject o2 = new JObject
- {
- { "Integer", 12345 },
- { "String", "A string" },
- { "Items", new JArray(1, 2) }
- };
- Console.WriteLine(JToken.DeepEquals(o1, o2));
- // true
- Console.WriteLine(JToken.DeepEquals(s1, o1["String"]));
- // 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