zoukankan      html  css  js  c++  java
  • 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; &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

  • 相关阅读:
    ubuntu11.04解决root不能登录的问题
    应用C预处理命令
    WINCE6.0在控制面板添加控制面板应用程序
    嵌入式系统开发
    WINCE6.0下开始菜单的“挂起(suspend)”是否可见及阻止系统进入睡眠模式
    WINCE6.0更换桌面壁纸和图标
    ubuntun_11.04安装
    WINCE开发更安全可靠设备驱动的最佳实践
    WINCE源代码配置文件
    TS2003基于触摸屏的应用
  • 原文地址:https://www.cnblogs.com/BluceLee/p/13813796.html
Copyright © 2011-2022 走看看