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.  
     
  6.  
    namespace ParseJson
  7.  
    {
  8.  
    class Product
  9.  
    {
  10.  
    public string Name;
  11.  
    public DateTime ExpiryDate;
  12.  
    public Decimal Price;
  13.  
    public String[] Sizes;
  14.  
    }
  15.  
     
  16.  
    class Program
  17.  
    {
  18.  
    static void Main(string[] args)
  19.  
    {
  20.  
    Product product = new Product()
  21.  
    {
  22.  
    Name = "Apple",
  23.  
    ExpiryDate= new DateTime(2020,12,30),
  24.  
    Price= 2.99M,
  25.  
    Sizes= new string[] {"small","medium","large"}
  26.  
     
  27.  
    };
  28.  
     
  29.  
    string output = JsonConvert.SerializeObject(product);
  30.  
     
  31.  
     
  32.  
    //將Json文件以字符串的形式保存
  33.  
    StreamWriter sw = new StreamWriter(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product.dat");
  34.  
    sw.Write(output);
  35.  
    sw.Close();
  36.  
    Console.WriteLine(output);
  37.  
    }
  38.  
    }
  39.  
    }

這里我們創建了一個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.  
     
  8.  
    string json = JsonConvert.SerializeObject(videogames);
  9.  
     
  10.  
    Console.WriteLine(json);
  11.  
    // ["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.  
    };
  9.  
    Product product2 = new Product()
  10.  
    {
  11.  
    Name = "cup",
  12.  
    ExpiryDate = new DateTime(2099, 1, 1),
  13.  
    Price = 9.99M,
  14.  
    Sizes = new string[] { "s", "L", "M", "xL" }
  15.  
     
  16.  
    };
  17.  
     
  18.  
    List<Product> list = new List<Product>() { product1, product2 };
  19.  
    string json = JsonConvert.SerializeObject(list);
  20.  
    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.  
     
  8.  
    string json = JsonConvert.SerializeObject(points, Formatting.Indented);
  9.  
     
  10.  
    Console.WriteLine(json);
  11.  
    // {
  12.  
    // "James": 9001,
  13.  
    // "Jo": 3474,
  14.  
    // "Jess": 11926
  15.  
    // }

這里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.  
     
  16.  
    List<Product> products = JsonConvert.DeserializeObject<List<Product>>(json);
  17.  
     
  18.  
    Console.WriteLine(products.Count);
  19.  
    // 2
  20.  
     
  21.  
    Product p1 = products[ 0];
  22.  
     
  23.  
    Console.WriteLine(p1.Name);
  24.  
    // Product 1

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

  1.  
    string json = @"{""key1"":""value1"",""key2"":""value2""}";
  2.  
     
  3.  
    Dictionary< string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
  4.  
     
  5.  
    Console.WriteLine(values.Count);
  6.  
    // 2
  7.  
     
  8.  
    Console.WriteLine(values[ "key1"]);
  9.  
    // 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.  
    };
  9.  
    Product product2 = new Product()
  10.  
    {
  11.  
    Name = "cup",
  12.  
    ExpiryDate = new DateTime(2099, 1, 1),
  13.  
    Price = 9.99M,
  14.  
    Sizes = new string[] { "s", "L", "M", "xL" }
  15.  
     
  16.  
    };
  17.  
     
  18.  
    List<Product> list = new List<Product>() { product1, product2 };
  19.  
    File.WriteAllText( @"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product1.json",
  20.  
    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.  
     
  5.  
    serializer.Serialize(file, list);
  6.  
    }

得到的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.  
     
  4.  
    // deserialize JSON directly from a file
  5.  
    using (StreamReader file = File.OpenText(@"c:\movie.json"))
  6.  
    {
  7.  
    JsonSerializer serializer = new JsonSerializer();
  8.  
    Movie movie2 = (Movie)serializer.Deserialize(file, typeof(Movie));
  9.  
    }

 注意:直接從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.  
     
  6.  
    public bool ShouldSerializeManager()
  7.  
    {
  8.  
    // don't serialize the Manager property if an employee is their own manager
  9.  
    return (Manager != this);
  10.  
    }
  11.  
    }

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

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

不過一般而言,當數據量不是很大時,你可以有條件的使用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.  
     
  5.  
    public DynamicContractResolver(char startingWithChar)
  6.  
    {
  7.  
    _startingWithChar = startingWithChar;
  8.  
    }
  9.  
     
  10.  
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
  11.  
    {
  12.  
    IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
  13.  
     
  14.  
    // only serializer properties that start with the specified character
  15.  
    //只需要在這里添加屬性篩選
  16.  
    properties =
  17.  
    properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList();
  18.  
     
  19.  
    return properties;
  20.  
    }
  21.  
    }

這個類有一個成員變量:_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.  
     
  10.  
    string startingWithA = JsonConvert.SerializeObject(book, Formatting.Indented,
  11.  
    new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('A') });
  12.  
     
  13.  
    // {
  14.  
    // "AuthorName": "Brandon Sanderson",
  15.  
    // "AuthorAge": 34,
  16.  
    // "AuthorCountry": "United States of America"
  17.  
    // }
  18.  
     
  19.  
    string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented,
  20.  
    new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') });
  21.  
     
  22.  
    // {
  23.  
    // "BookName": "The Gathering Storm",
  24.  
    // "BookPrice": 16.19
  25.  
    // }

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

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

這個的用法和前面類似,不過這里直接聲明了一個類的靜態成員,所以不需要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.  
     
  3.  
    string json1 = @"{'Name':'James'}";
  4.  
    var customer1 = JsonConvert.DeserializeAnonymousType(json1, definition);
  5.  
     
  6.  
    Console.WriteLine(customer1.Name);
  7.  
    // James
  8.  
     
  9.  
    string json2 = @"{'Name':'Mike'}";
  10.  
    var customer2 = JsonConvert.DeserializeAnonymousType(json2, definition);
  11.  
     
  12.  
    Console.WriteLine(customer2.Name);
  13.  
    // 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.  
     
  8.  
    public class Employee : Person
  9.  
    {
  10.  
    public string Department { get; set; }
  11.  
    public string JobTitle { get; set; }
  12.  
    }
  13.  
     
  14.  
    public class PersonConverter : CustomCreationConverter<Person>
  15.  
    {
  16.  
    public override Person Create(Type objectType)
  17.  
    {
  18.  
    return new Employee();
  19.  
    }
  20.  
    }

然后你想將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.  
     
  9.  
    Person person = JsonConvert.DeserializeObject<Person>(json, new PersonConverter());
  10.  
     
  11.  
    Console.WriteLine(person.GetType().Name);
  12.  
    // Employee
  13.  
     
  14.  
    Employee employee = (Employee)person;
  15.  
     
  16.  
    Console.WriteLine(employee.JobTitle);
  17.  
    // 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.  
     
  6.  
    public UserViewModel()
  7.  
    {
  8.  
    Offices = new List<string>
  9.  
    {
  10.  
    "Auckland",
  11.  
    "Wellington",
  12.  
    "Christchurch"
  13.  
    };
  14.  
    }
  15.  
    }

構造函數對成員Offices進行了賦值:

  1.  
    string json = @"{
  2.  
    'Name': 'James',
  3.  
    'Offices': [
  4.  
    'Auckland',
  5.  
    'Wellington',
  6.  
    'Christchurch'
  7.  
    ]
  8.  
    }";
  9.  
     
  10.  
    UserViewModel model1 = JsonConvert.DeserializeObject<UserViewModel>(json);
  11.  
     
  12.  
    foreach (string office in model1.Offices)
  13.  
    {
  14.  
    Console.WriteLine(office);
  15.  
    }
  16.  
    // Auckland
  17.  
    // Wellington
  18.  
    // Christchurch
  19.  
    // Auckland
  20.  
    // Wellington
  21.  
    // Christchurch
  22.  
     
  23.  
    UserViewModel model2 = JsonConvert.DeserializeObject<UserViewModel>(json, new JsonSerializerSettings
  24.  
    {
  25.  
    ObjectCreationHandling = ObjectCreationHandling.Replace
  26.  
    });
  27.  
     
  28.  
    foreach (string office in model2.Offices)
  29.  
    {
  30.  
    Console.WriteLine(office);
  31.  
    }
  32.  
    // Auckland
  33.  
    // Wellington
  34.  
    // 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.  
     
  9.  
    Person person = new Person();
  10.  
     
  11.  
    string jsonIncludeDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented);
  12.  
     
  13.  
    Console.WriteLine(jsonIncludeDefaultValues);
  14.  
    // {
  15.  
    // "Name": null,
  16.  
    // "Age": 0,
  17.  
    // "Partner": null,
  18.  
    // "Salary": null
  19.  
    // }
  20.  
     
  21.  
    string jsonIgnoreDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
  22.  
    {
  23.  
    DefaultValueHandling = DefaultValueHandling.Ignore
  24.  
    });
  25.  
     
  26.  
    Console.WriteLine(jsonIgnoreDefaultValues);
  27.  
    // {}

過濾值為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.  
     
  9.  
    Person person = new Person
  10.  
    {
  11.  
    Name = "Nigal Newborn",
  12.  
    Age = 1
  13.  
    };
  14.  
     
  15.  
    string jsonIncludeNullValues = JsonConvert.SerializeObject(person, Formatting.Indented);
  16.  
     
  17.  
    Console.WriteLine(jsonIncludeNullValues);
  18.  
    // {
  19.  
    // "Name": "Nigal Newborn",
  20.  
    // "Age": 1,
  21.  
    // "Partner": null,
  22.  
    // "Salary": null
  23.  
    // }
  24.  
     
  25.  
    string jsonIgnoreNullValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
  26.  
    {
  27.  
    NullValueHandling = NullValueHandling.Ignore
  28.  
    });
  29.  
     
  30.  
    Console.WriteLine(jsonIgnoreNullValues);
  31.  
    // {
  32.  
    // "Name": "Nigal Newborn",
  33.  
    // "Age": 1
  34.  
    // }

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.  
       
    9.  
      JObject o = JObject.Parse(json);
    10.  
      Console.WriteLine(o.ToString());
    11.  
      // {
    12.  
      // "CPU": "Intel",
    13.  
      // "Drives": [
    14.  
      // "DVD read/writer",
    15.  
      // "500 gigabyte hard drive"
    16.  
      // ]
    17.  
      // }

     

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

     

  • 從本地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.  
       
    6.  
      File.WriteAllText( @"c:\videogames.json", videogameRatings.ToString());
    7.  
       
    8.  
      // write JSON directly to a file
    9.  
      using (StreamWriter file = File.CreateText(@"c:\videogames.json"))
    10.  
      using (JsonTextWriter writer = new JsonTextWriter(file))
    11.  
      {
    12.  
      videogameRatings.WriteTo(writer);
    13.  
      }

     

  • 創建JObject對象,JArray對象
    1.  
      JArray array = new JArray();
    2.  
      array.Add( "Manual text");
    3.  
      array.Add( new DateTime(2000, 5, 23));
    4.  
       
    5.  
      JObject o = new JObject();
    6.  
      o[ "MyArray"] = array;
    7.  
       
    8.  
      string json = o.ToString();
    9.  
      // {
    10.  
      // "MyArray": [
    11.  
      // "Manual text",
    12.  
      // "2000-05-23T00:00:00"
    13.  
      // ]
    14.  
      // }
  • 使用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.  
       
    14.  
      Console.WriteLine(o.ToString());
    15.  
      // {
    16.  
      // "Cpu": "Intel",
    17.  
      // "Memory": 32,
    18.  
      // "Drives": [
    19.  
      // "DVD",
    20.  
      // "SSD"
    21.  
      // ]
    22.  
      // }

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

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

     

  • 動態創建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.  
       
    9.  
      Console.WriteLine(product.ToString());
    10.  
      // {
    11.  
      // "ProductName": "Elbow Grease",
    12.  
      // "Enabled": true,
    13.  
      // "Price": 4.90,
    14.  
      // "StockCount": 9000,
    15.  
      // "StockValue": 44100,
    16.  
      // "Tags": [
    17.  
      // "Real",
    18.  
      // "OnSale"
    19.  
      // ]
    20.  
      // }

     

  • 使用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.  
       
    12.  
      JObject o = (JObject)writer.Token;
    13.  
       
    14.  
      Console.WriteLine(o.ToString());
    15.  
      // {
    16.  
      // "name1": "value1",
    17.  
      // "name2": [
    18.  
      // 1,
    19.  
      // 2
    20.  
      // ]
    21.  
      // }

     

  • 從自定義對象生成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.  
       
    8.  
      JValue i = (JValue)JToken.FromObject( 12345);
    9.  
       
    10.  
      Console.WriteLine(i.Type);
    11.  
      // Integer
    12.  
      Console.WriteLine(i.ToString());
    13.  
      // 12345
    14.  
       
    15.  
      JValue s = (JValue)JToken.FromObject( "A string");
    16.  
       
    17.  
      Console.WriteLine(s.Type);
    18.  
      // String
    19.  
      Console.WriteLine(s.ToString());
    20.  
      // A string
    21.  
       
    22.  
      Computer computer = new Computer
    23.  
      {
    24.  
      Cpu = "Intel",
    25.  
      Memory = 32,
    26.  
      Drives = new List<string>
    27.  
      {
    28.  
      "DVD",
    29.  
      "SSD"
    30.  
      }
    31.  
      };
    32.  
       
    33.  
      JObject o = (JObject)JToken.FromObject(computer);
    34.  
       
    35.  
      Console.WriteLine(o.ToString());
    36.  
      // {
    37.  
      // "Cpu": "Intel",
    38.  
      // "Memory": 32,
    39.  
      // "Drives": [
    40.  
      // "DVD",
    41.  
      // "SSD"
    42.  
      // ]
    43.  
      // }
    44.  
       
    45.  
      JArray a = (JArray)JToken.FromObject(computer.Drives);
    46.  
       
    47.  
      Console.WriteLine(a.ToString());
    48.  
      // [
    49.  
      // "DVD",
    50.  
      // "SSD"
    51.  
      // ]

    這個函數還可以直接從匿名對象創建:
     

    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.  
       
    16.  
      JObject o = JObject.FromObject( new
    17.  
      {
    18.  
      channel = new
    19.  
      {
    20.  
      title = "Star Wars",
    21.  
      link = "http://www.starwars.com",
    22.  
      description = "Star Wars blog.",
    23.  
      item =
    24.  
      from p in posts
    25.  
      orderby p.Title
    26.  
      select new
    27.  
      {
    28.  
      title = p.Title,
    29.  
      description = p.Description,
    30.  
      link = p.Link,
    31.  
      category = p.Categories
    32.  
      }
    33.  
      }
    34.  
      });
    35.  
       
    36.  
      Console.WriteLine(o.ToString());
    37.  
      // {
    38.  
      // "channel": {
    39.  
      // "title": "Star Wars",
    40.  
      // "link": "http://www.starwars.com",
    41.  
      // "description": "Star Wars blog.",
    42.  
      // "item": [
    43.  
      // {
    44.  
      // "title": "Episode VII",
    45.  
      // "description": "Episode VII production",
    46.  
      // "link": "episode-vii-production.aspx",
    47.  
      // "category": [
    48.  
      // "episode-vii",
    49.  
      // "movie"
    50.  
      // ]
    51.  
      // }
    52.  
      // ]
    53.  
      // }
    54.  
      // }

     

從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.  
     
  22.  
    JArray blogPostArray = JArray.Parse(json);
  23.  
     
  24.  
    IList<BlogPost> blogPosts = blogPostArray.Select(p => new BlogPost
  25.  
    {
  26.  
    Title = ( string)p["Title"],
  27.  
    AuthorName = ( string)p["Author"]["Name"],
  28.  
    AuthorTwitter = ( string)p["Author"]["Twitter"],
  29.  
    PostedDate = (DateTime)p[ "Date"],
  30.  
    Body = HttpUtility.HtmlDecode(( string)p["BodyHtml"])
  31.  
    }).ToList();
  32.  
     
  33.  
    Console.WriteLine(blogPosts[ 0].Body);
  34.  
    // <h3>Title!</h3>
  35.  
    // <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.  
     
  13.  
    JArray blogPostsArray = new JArray(
  14.  
    blogPosts.Select(p => new JObject
  15.  
    {
  16.  
    { "Title", p.Title },
  17.  
    {
  18.  
    "Author", new JObject
  19.  
    {
  20.  
    { "Name", p.AuthorName },
  21.  
    { "Twitter", p.AuthorTwitter }
  22.  
    }
  23.  
    },
  24.  
    { "Date", p.PostedDate },
  25.  
    { "BodyHtml", HttpUtility.HtmlEncode(p.Body) },
  26.  
    })
  27.  
    );
  28.  
     
  29.  
    Console.WriteLine(blogPostsArray.ToString());
  30.  
    // [
  31.  
    // {
  32.  
    // "Title": "Json.NET is awesome!",
  33.  
    // "Author": {
  34.  
    // "Name": "James Newton-King",
  35.  
    // "Twitter": "JamesNK"
  36.  
    // },
  37.  
    // "Date": "2013-01-23T19:30:00",
  38.  
    // "BodyHtml": "&lt;h3&gt;Title!&lt;/h3&gt;&lt;p&gt;Content!&lt;/p&gt;"
  39.  
    // }
  40.  
    // ]

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.  
     
  11.  
    JObject rss = JObject.Parse(json);
  12.  
     
  13.  
    JObject channel = (JObject)rss[ "channel"];
  14.  
     
  15.  
    channel[ "title"] = ((string)channel["title"]).ToUpper();
  16.  
    channel[ "description"] = ((string)channel["description"]).ToUpper();
  17.  
     
  18.  
    channel.Property( "obsolete").Remove();
  19.  
     
  20.  
    channel.Property( "description").AddAfterSelf(new JProperty("new", "New value"));
  21.  
     
  22.  
    JArray item = (JArray)channel[ "item"];
  23.  
    item.Add( "Item 1");
  24.  
    item.Add( "Item 2");
  25.  
     
  26.  
    Console.WriteLine(rss.ToString());
  27.  
    // {
  28.  
    // "channel": {
  29.  
    // "title": "STAR WARS",
  30.  
    // "link": "http://www.starwars.com",
  31.  
    // "description": "STAR WARS BLOG.",
  32.  
    // "new": "New value",
  33.  
    // "item": [
  34.  
    // "Item 1",
  35.  
    // "Item 2"
  36.  
    // ]
  37.  
    // }
  38.  
    // }

在上面的例子,依次進行了:使用函數修改值,刪除屬性,在某屬性后面添加屬性,在數組內部添加成員。


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.  
     
  12.  
    o1.Merge(o2, new JsonMergeSettings
  13.  
    {
  14.  
    // union array values together to avoid duplicates
  15.  
    MergeArrayHandling = MergeArrayHandling.Union
  16.  
    });
  17.  
     
  18.  
    string json = o1.ToString();
  19.  
    // {
  20.  
    // "FirstName": "John",
  21.  
    // "LastName": "Smith",
  22.  
    // "Enabled": true,
  23.  
    // "Roles": [
  24.  
    // "User",
  25.  
    // "Admin"
  26.  
    // ]
  27.  
    // }

這里要說的是,可以使用MergeArrayHandling對象來設置合並方式,上面使用的是合並模式:Union,即當前Json有時,只會出現一次,此外還有:

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

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


3.4 將Json類型轉為普通類型

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

  1.  
    JValue v1 = new JValue(true);
  2.  
     
  3.  
    bool b = v1.ToObject<bool>();
  4.  
     
  5.  
    Console.WriteLine(b);
  6.  
    // true
  7.  
     
  8.  
    int i = v1.ToObject<int>();
  9.  
     
  10.  
    Console.WriteLine(i);
  11.  
    // 1
  12.  
     
  13.  
    string s = v1.ToObject<string>();
  14.  
     
  15.  
    Console.WriteLine(s);
  16.  
    // "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.  
     
  5.  
    Console.WriteLine(JToken.DeepEquals(s1, s2));
  6.  
    // true
  7.  
     
  8.  
    Console.WriteLine(JToken.DeepEquals(s2, s3));
  9.  
    // false
  10.  
     
  11.  
    JObject o1 = new JObject
  12.  
    {
  13.  
    { "Integer", 12345 },
  14.  
    { "String", "A string" },
  15.  
    { "Items", new JArray(1, 2) }
  16.  
    };
  17.  
     
  18.  
    JObject o2 = new JObject
  19.  
    {
  20.  
    { "Integer", 12345 },
  21.  
    { "String", "A string" },
  22.  
    { "Items", new JArray(1, 2) }
  23.  
    };
  24.  
     
  25.  
    Console.WriteLine(JToken.DeepEquals(o1, o2));
  26.  
    // true
  27.  
     
  28.  
    Console.WriteLine(JToken.DeepEquals(s1, o1[ "String"]));
  29.  
    // 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.  
     
  5.  
    Console.WriteLine(JToken.DeepEquals(s1, s2));
  6.  
    // true
  7.  
     
  8.  
    Console.WriteLine(JToken.DeepEquals(s2, s3));
  9.  
    // false
  10.  
     
  11.  
    JObject o1 = new JObject
  12.  
    {
  13.  
    { "Integer", 12345 },
  14.  
    { "String", "A string" },
  15.  
    { "Items", new JArray(1, 2) }
  16.  
    };
  17.  
     
  18.  
    JObject o2 = new JObject
  19.  
    {
  20.  
    { "Integer", 12345 },
  21.  
    { "String", "A string" },
  22.  
    { "Items", new JArray(1, 2) }
  23.  
    };
  24.  
     
  25.  
    Console.WriteLine(JToken.DeepEquals(o1, o2));
  26.  
    // true
  27.  
     
  28.  
    Console.WriteLine(JToken.DeepEquals(s1, o1[ "String"]));
  29.  
    // 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM