• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

C#解析Json文件(使用NewtonJson库)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

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. 鲜花

    握手

    雷人

    路过

    鸡蛋
    该文章已有0人参与评论

    请发表评论

    全部评论

    专题导读
    上一篇:
    C#初入串口通信(串行通信)总结发布时间:2022-07-10
    下一篇:
    C#NameValueCollection发布时间:2022-07-10
    热门推荐
    阅读排行榜

    扫描微信二维码

    查看手机版网站

    随时了解更新最新资讯

    139-2527-9053

    在线客服(服务时间 9:00~18:00)

    在线QQ客服
    地址:深圳市南山区西丽大学城创智工业园
    电邮:jeky_zhao#qq.com
    移动电话:139-2527-9053

    Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap