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. 总结
编写C#程序肯定是在visual studio中写(暂不考虑unity等其他地方),那么无论你是在windows下还是Mac OS X下,visual studio都自带nuget包管理工具。本文以Mac OS X平台为例,首先新建一个项目叫ParseJson,然后在项目的依赖项中弹出快捷菜单,如下:
点击Nuget包管理,弹出一个窗口:
可以看到第一个就是我们想要的包,选中然后添加包即可。添加完成后在你的程序中添加下面两行:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
至此,基本配置就完成了。
NewtonJson官网有详细的使用文档教程,有很多示例代码,这里就不过多介绍了,遇到不懂的问题可以去文档里面找资料。
假设你在C#中有一个定义好的类,然后你生成了一个对象,想把这个对象保存为Json文件,你可以用SerializeObject()函数处理。看下面的代码:
using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ParseJson
{
class Product
{
public string Name;
public DateTime ExpiryDate;
public Decimal Price;
public String[] Sizes;
}
class Program
{
static void Main(string[] args)
{
Product product = new Product()
{
Name = "Apple",
ExpiryDate=new DateTime(2020,12,30),
Price=2.99M,
Sizes=new string[] {"small","medium","large"}
};
string output = JsonConvert.SerializeObject(product);
//将Json文件以字符串的形式保存
StreamWriter sw = new StreamWriter(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product.dat");
sw.Write(output);
sw.Close();
Console.WriteLine(output);
}
}
}
这里我们创建了一个Product类,并且实例化了一个对象,利用JsonConvert.SerializeObject(product)将其转化为Json文件,并以字符串的形式存储在变量output,我们很容易将字符串保存到本地。可以查看保存到本地后的文件内容:
既然我们可以将对象以json文件的形式保存,自然我们也应该可以从Json格式恢复成对象,做法很简单,假设我们已经读取了本地文件Product.dat,并保存到了字符串变量output中,我们要从中恢复成Product对象只需一句话:
//恢复对象
Product p = JsonConvert.DeserializeObject(output);
值得一提的的是,当你的对象里面有集合对象时:
public class Acount
{
public string Email {get;set;}
public Ilist Roles {get;set}
}
也可以直接使用上面的方法,转化为Json文件后,集合对象会以数组的形式存储。
除了序列化自定义的类,还可以将C#中的集合对象序列化,这里我就不跑代码了,直接搬运官网的例子。
序列化字典:
List videogames = new List
{
"Starcraft",
"Halo",
"Legend of Zelda"
};
string json = JsonConvert.SerializeObject(videogames);
Console.WriteLine(json);
// ["Starcraft","Halo","Legend of Zelda"]
List集合被转化为了数组,当然List里面可以是复杂的类型,如使用我们之前定义的Product:
Product product1 = new Product()
{
Name = "Apple",
ExpiryDate=new DateTime(2020,12,30),
Price=2.99M,
Sizes=new string[] {"small","medium","large"}
};
Product product2 = new Product()
{
Name = "cup",
ExpiryDate = new DateTime(2099, 1, 1),
Price = 9.99M,
Sizes = new string[] { "s", "L", "M", "xL" }
};
List list = new List() { product1, product2 };
string json = JsonConvert.SerializeObject(list);
Console.WriteLine(json);
输出为:
[{"Name":"Apple","ExpiryDate":"2020-12-30T00:00:00","Price":2.99,"Sizes":["small","medium","large"]},{"Name":"cup","ExpiryDate":"2099-01-01T00:00:00","Price":9.99,"Sizes":["s","L","M","xL"]}]
序列化字典例子如下:
Dictionary points = new Dictionary
{
{ "James", 9001 },
{ "Jo", 3474 },
{ "Jess", 11926 }
};
string json = JsonConvert.SerializeObject(points, Formatting.Indented);
Console.WriteLine(json);
// {
// "James": 9001,
// "Jo": 3474,
// "Jess": 11926
// }
这里SerializeObject多了一个参数,Indented表示转化为的Json文件带缩进,这样输出会更加直观清晰。
反序列化和之前讲的类似,反序列化需要给出转化为对象的类型,反序列化集合:
string json = @"[
{
'Name': 'Product 1',
'ExpiryDate': '2000-12-29T00:00Z',
'Price': 99.95,
'Sizes': null
},
{
'Name': 'Product 2',
'ExpiryDate': '2009-07-31T00:00Z',
'Price': 12.50,
'Sizes': null
}
]";
List products = JsonConvert.DeserializeObject>(json);
Console.WriteLine(products.Count);
// 2
Product p1 = products[0];
Console.WriteLine(p1.Name);
// Product 1
反序列化集合一般转为List类型,如果你需要转化为其它类型,你可以后续再处理。反序列化字典也是如此:
string json = @"{""key1"":""value1"",""key2"":""value2""}";
Dictionary values = JsonConvert.DeserializeObject>(json);
Console.WriteLine(values.Count);
// 2
Console.WriteLine(values["key1"]);
// value1
在最开始的例子中,我们将对象先转为字符串,然后再写入一个dat文件中,事实上完全可以将文件保存为Json文件。有两种思路,一种还是先将json变为字符串,然后保存到自定义的json文件中。
Product product1 = new Product()
{
Name = "Apple",
ExpiryDate=new DateTime(2020,12,30),
Price=2.99M,
Sizes=new string[] {"small","medium","large"}
};
Product product2 = new Product()
{
Name = "cup",
ExpiryDate = new DateTime(2099, 1, 1),
Price = 9.99M,
Sizes = new string[] { "s", "L", "M", "xL" }
};
List list = new List() { product1, product2 };
File.WriteAllText(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product1.json",
JsonConvert.SerializeObject(list,Formatting.Indented));
我们用Vscode打开product1.json如下:
另一种方法是直接将对象转化为json文件:
using(StreamWriterfile=File.CreateText(@"/Users/qinyuanlong/Projects/SimpleTest/ParseJson/product2.json"))
{
JsonSerializer serializer = new JsonSerializer() { Formatting=Formatting.Indented};
serializer.Serialize(file, list);
}
得到的product2.json和前面是一样的。
从json文件中解析对象的操作几乎是一模一样的:只需要将SerializeObject函数换成DeserializeObject,WriteAllText换成ReadAllText,CreatText换成OpenText。
// read file into a string and deserialize JSON to a type
Movie movie1 = JsonConvert.DeserializeObject(File.ReadAllText(@"c:\movie.json"));
// deserialize JSON directly from a file
using (StreamReader file = File.OpenText(@"c:\movie.json"))
{
JsonSerializer serializer = new JsonSerializer();
Movie movie2 = (Movie)serializer.Deserialize(file, typeof(Movie));
}
注意:直接从json转为对象,除了提供对象类型参数,还需要强制转化操作。
NewTonJson还支持有条件序列化对象,即对某些属性进行判断,如果不满足要求,则忽略该属性。
要实现部分属性的条件序列化,需要添加一些函数,这个函数和属性一一对应,函数名为:ShouldSerialize+属性名,函数的返回值为bool类型,当返回为True时,该属性将被序列化,为False则被忽略。看一个官方例子:
首先你有这样一个简单类:
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
这里已经添加了Manager这个筛选函数,所以当Manage就是自己时,这个Manage会被忽略。
Employee joe = new Employee();
joe.Name = "Joe Employee";
Employee mike = new Employee();
mike.Name = "Mike Manager";
joe.Manager = mike;
// mike is his own manager
// ShouldSerialize will skip this property
mike.Manager = mike;
string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented);
Console.WriteLine(json);
// [
// {
// "Name": "Joe Employee",
// "Manager": {
// "Name": "Mike Manager"
// }
// },
// {
// "Name": "Mike Manager"
// }
// ]
不过一般而言,当数据量不是很大时,你可以有条件的使用Json文件的数据,也就是我不用这个属性,我就可以假设它不存在。
上面的例子固然可行,但是有个很麻烦的问题:你必须在设计类的时候就确定好条件序列化属性。
那么有没有更好的控制办法呢?答案是肯定的。
通过派生接口:IContractResolver,生产一个筛选对象可以实现定制化筛选,你不用在你的类里面添加函数。一般我们不用直接派生自IContractResolver,而是派生自它的一个派生类:
DefaultContractResolver
看下面这个例子,我们假设有这样一个图书类:
public class Book
{
public string BookName { get; set; }
public decimal BookPrice { get; set; }
public string AuthorName { get; set; }
public int AuthorAge { get; set; }
public string AuthorCountry { get; set; }
}
我们想实现序列化以字母“A”开头或者“B”开头的属性,显然用之前的方法会很麻烦,我们用刚才的方法,派生一个筛选类:
public class DynamicContractResolver : DefaultContractResolver
{
private readonly char _startingWithChar;
public DynamicContractResolver(char startingWithChar)
{
_startingWithChar = startingWithChar;
}
protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList properties = base.CreateProperties(type, memberSerialization);
// only serializer properties that start with the specified character
//只需要在这里添加属性筛选
properties =
properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList();
return properties;
}
}
这个类有一个成员变量:_startingWithChar,用来接受筛选参数;一个构造函数;一个筛选函数:CreateProperties,这个函数返回一个属性集合,函数首先获得类的所有属性,保存带properties中,然后你根据需求对properties进行处理。上面的函数实现了根据属性名的首字母进行筛选。所以我们就可以用这个类的对象作为参数去实现我们的需求:
Book book = new Book
{
BookName = "The Gathering Storm",
BookPrice = 16.19m,
AuthorName = "Brandon Sanderson",
AuthorAge = 34,
AuthorCountry = "United States of America"
};
string startingWithA = JsonConvert.SerializeObject(book, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('A') });
// {
// "AuthorName": "Brandon Sanderson",
// "AuthorAge": 34,
// "AuthorCountry": "United States of America"
// }
string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') });
// {
// "BookName": "The Gathering Storm",
// "BookPrice": 16.19
// }
DefaultContractResolver中的CreateProperties是对多个属性筛选而言的,回到本节最开始的问题,我只想对Manage成员进行筛选,那么可以用CreateProperty这个函数,具体用法如下:
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
这个的用法和前面类似,不过这里直接声明了一个类的静态成员,所以不需要new一个对象:
string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented,
new JsonSerializerSettings { ContractResolver=ShouldSerializeContractResolver.Instance});
顺便要说明的是,为了成功运行需要添加新的命名空间:
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
这里给出官网关于DefaultContrctResolver的链接。
对于匿名类,由于序列化不需要给出对象类型,所以可以依然使用前面序列化自定义类的方法,但是反序列是需要提供类型的,那对于匿名类怎么办?,这个NewtonJson也替我们考虑了,例子如下:
var definition = new { Name = "" };
string json1 = @"{'Name':'James'}";
var customer1 = JsonConvert.DeserializeAnonymousType(json1, definition);
Console.WriteLine(customer1.Name);
// James
string json2 = @"{'Name':'Mike'}";
var customer2 = JsonConvert.DeserializeAnonymousType(json2, definition);
Console.WriteLine(customer2.Name);
// Mike
办法很简单,直接给一个匿名类的定义对象,传入参数即可。
将派生类解析为基类,需要一个派生自CustomeCreationConverter的对象,操作起来其实很简单,看一下官方的例子:
首先你有一个Person基类,然后派生了Employee类,并写好了派生自CustomeCreationConverter类的类PersonConverter:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class PersonConverter : CustomCreationConverter
{
public override Person Create(Type objectType)
{
return new Employee();
}
}
然后你想将Employee对象解析为Person,只需要传入一个PersonConvrter对象:
string json = @"{
'Department': 'Furniture',
'JobTitle': 'Carpenter',
'FirstName': 'John',
'LastName': 'Joinery',
'BirthDate': '1983-02-02T00:00:00'
}";
Person person = JsonConvert.DeserializeObject(json, new PersonConverter());
Console.WriteLine(person.GetType().Name);
// Employee
Employee employee = (Employee)person;
Console.WriteLine(employee.JobTitle);
// Carpenter
从结果可以看出,虽然是以Person解析的,但是实际上仍然是Employee类型。
如果一个类的构造函数本身就对成员进行了赋值,那么在反序列化时,可能会调用一次构造函数容易造成重复写入,看下面的例子:
public class UserViewModel
{
public string Name { get; set; }
public IList Offices { get; private set; }
public UserViewModel()
{
Offices = new List
{
"Auckland",
"Wellington",
"Christchurch"
};
}
}
构造函数对成员Offices进行了赋值:
string json = @"{
'Name': 'James',
'Offices': [
'Auckland',
'Wellington',
'Christchurch'
]
}";
UserViewModel model1 = JsonConvert.DeserializeObject(json);
foreach (string office in model1.Offices)
{
Console.WriteLine(office);
}
// Auckland
// Wellington
// Christchurch
// Auckland
// Wellington
// Christchurch
UserViewModel model2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
ObjectCreationHandling = ObjectCreationHandling.Replace
});
foreach (string office in model2.Offices)
{
Console.WriteLine(office);
}
// Auckland
// Wellington
// Christchurch
如果不添加设定,Offices就是存在2遍初始值,为例避免这种情况,在反序列化的时候传入了一个setting对象,其ObejctCreationHandling属性为Replcae。
C#对于没有赋值的类型提供一个默认值,如Int类型默认值为0,string类型默认值为null,如果当一个对象的成员没有被赋值,我们希望得到的是一个空json,那么需要将setting的DefaultValueHandleling设置为Ignore。
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person Partner { get; set; }
public decimal? Salary { get; set; }
}
Person person = new Person();
string jsonIncludeDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(jsonIncludeDefaultValues);
// {
// "Name": null,
// "Age": 0,
// "Partner": null,
// "Salary": null
// }
string jsonIgnoreDefaultValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Ignore
});
Console.WriteLine(jsonIgnoreDefaultValues);
// {}
过滤值为null的成员只需要将NullValueHandling属性设置为Ignore。
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person Partner { get; set; }
public decimal? Salary { get; set; }
}
Person person = new Person
{
Name = "Nigal Newborn",
Age = 1
};
string jsonIncludeNullValues = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(jsonIncludeNullValues);
// {
// "Name": "Nigal Newborn",
// "Age": 1,
// "Partner": null,
// "Salary": null
// }
string jsonIgnoreNullValues = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
Console.WriteLine(jsonIgnoreNullValues);
// {
// "Name": "Nigal Newborn",
// "Age": 1
// }
当我们将下面的json文件:
string json = @"{
'FullName': 'Dan Deleted',
'Deleted': true,
'DeletedDate': '2013-01-20T00:00:00'
}";
解析为Account类型时:
public class Account
{
public string FullName { get; set; }
public bool Deleted { get; set; }
}
是可以成功的,因为json中包含Accout类中所有成员属性。但是如果我们要严格转化(特别是在Accout漏掉属性时),需要报错,那么就需要设置setting的MissingMemberHandling属性。
try
{
JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Error
});
}
catch (JsonSerializationException ex)
{
Console.WriteLine(ex.Message);
// Could not find member 'DeletedDate' on object of type 'Account'. Path 'DeletedDate', line 4, position 23.
}
此外还有很多设置用来解决各种问题,上面只是列出了一些常见的,有需要的还是要去官网查看。
使用ling处理json最大的好处是摆脱了对象的束缚,使用NewtonJson自带的一套体系。我们都知道JavaScript原生支持Json,直接可以将Json文件转化为一个对象,JObject的创建也是为了实现这样一个类似的功能。
string json = @"{
CPU: 'Intel',
Drives: [
'DVD read/writer',
'500 gigabyte hard drive'
]
}";
JObject o = JObject.Parse(json);
Console.WriteLine(o.ToString());
// {
// "CPU": "Intel",
// "Drives": [
// "DVD read/writer",
// "500 gigabyte hard drive"
// ]
// }
string json = @"[
'Small',
'Medium',
'Large'
]";
JArray a = JArray.Parse(json);
Console.WriteLine(a.Tostring());
// [
// "Small",
// "Medium",
// "Large"
// ]
从本地Json文件中解析
读操作
using (StreamReader reader = File.OpenText(@"c:\person.json"))
{
JObject o = (JObject)JToken.ReadFrom(new JsonTextReader(reader));
// do stuff
}
写操作
JObject videogameRatings = new JObject(
new JProperty("Halo", 9),
new JProperty("Starcraft", 9),
new JProperty("Call of Duty", 7.5));
File.WriteAllText(@"c:\videogames.json", videogameRatings.ToString());
// write JSON directly to a file
using (StreamWriter file = File.CreateText(@"c:\videogames.json"))
using (JsonTextWriter writer = new JsonTextWriter(file))
{
videogameRatings.WriteTo(writer);
}
JArray array = new JArray();
array.Add("Manual text");
array.Add(new DateTime(2000, 5, 23));
JObject o = new JObject();
o["MyArray"] = array;
string json = o.ToString();
// {
// "MyArray": [
// "Manual text",
// "2000-05-23T00:00:00"
// ]
// }
JObject o = new JObject
{
{ "Cpu", "Intel" },
{ "Memory", 32 },
{
"Drives", new JArray
{
"DVD",
"SSD"
}
}
};
Console.WriteLine(o.ToString());
// {
// "Cpu": "Intel",
// "Memory": 32,
// "Drives": [
// "DVD",
// "SSD"
// ]
// }
当然也可以用传统的对象创建方法,但是会觉得繁琐,看下面的例子:
public class Post
{
public string Title { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public IList Categories { get; set; }
}
List posts = GetPosts();
JObject rss =
new JObject(
new JProperty("channel",
new JObject(
new JProperty("title", "James Newton-King"),
new JProperty("link", "http://james.newtonking.com"),
new JProperty("description", "James Newton-King's blog."),
new JProperty("item",
new JArray(
from p in posts
orderby p.Title
select new JObject(
new JProperty("title", p.Title),
new JProperty("description", p.Description),
new JProperty("link", p.Link),
new JProperty("category",
new JArray(
from c in p.Categories
select new JValue(c)))))))));
Console.WriteLine(rss.ToString());
// {
// "channel": {
// "title": "James Newton-King",
// "link": "http://james.newtonking.com",
// "description": "James Newton-King's blog.",
// "item": [
// {
// "title": "Json.NET 1.3 + New license + Now on CodePlex",
// "description": "Announcing the release of Json.NET 1.3, the MIT license and being available on CodePlex",
// "link": "http://james.newtonking.com/projects/json-net.aspx",
// "category": [
// "Json.NET",
// "CodePlex"
// ]
// },
// {
// "title": "LINQ to JSON beta",
// "description": "Announcing LINQ to JSON",
// "link": "http://james.newtonking.com/projects/json-net.aspx",
// "category": [
// "Json.NET",
// "LINQ"
// ]
// }
// ]
// }
// }
dynamic product = new JObject();
product.ProductName = "Elbow Grease";
product.Enabled = true;
product.Price = 4.90m;
product.StockCount = 9000;
product.StockValue = 44100;
product.Tags = new JArray("Real", "OnSale");
Console.WriteLine(product.ToString());
// {
// "ProductName": "Elbow Grease",
// "Enabled": true,
// "Price": 4.90,
// "StockCount": 9000,
// "StockValue": 44100,
// "Tags": [
// "Real",
// "OnSale"
// ]
// }
JTokenWriter writer = new JTokenWriter();
writer.WriteStartObject();
writer.WritePropertyName("name1");
writer.WriteValue("value1");
writer.WritePropertyName("name2");
writer.WriteStartArray();
writer.WriteValue(1);
writer.WriteValue(2);
writer.WriteEndArray();
writer.WriteEndObject();
JObject o = (JObject)writer.Token;
Console.WriteLine(o.ToString());
// {
// "name1": "value1",
// "name2": [
// 1,
// 2
// ]
// }
public class Computer
{
public string Cpu { get; set; }
public int Memory { get; set; }
public IList Drives { get; set; }
}
JValue i = (JValue)JToken.FromObject(12345);
Console.WriteLine(i.Type);
// Integer
Console.WriteLine(i.ToString());
// 12345
JValue s = (JValue)JToken.FromObject("A string");
Console.WriteLine(s.Type);
// String
Console.WriteLine(s.ToString());
// A string
Computer computer = new Computer
{
Cpu = "Intel",
Memory = 32,
Drives = new List
{
"DVD",
"SSD"
}
};
JObject o = (JObject)JToken.FromObject(computer);
Console.WriteLine(o.ToString());
// {
// "Cpu": "Intel",
// "Memory": 32,
// "Drives": [
// "DVD",
// "SSD"
// ]
// }
JArray a = (JArray)JToken.FromObject(computer.Drives);
Console.WriteLine(a.ToString());
// [
// "DVD",
// "SSD"
// ]
这个函数还可以直接从匿名对象创建:
List posts = new List
{
new Post
{
Title = "Episode VII",
Description = "Episode VII production",
Categories = new List
{
"episode-vii",
"movie"
},
Link = "episode-vii-production.aspx"
}
};
JObject o = JObject.FromObject(new
{
channel = new
{
title = "Star Wars",
link = "http://www.starwars.com",
description = "Star Wars blog.",
item =
from p in posts
orderby p.Title
select new
{
title = p.Title,
description = p.Description,
link = p.Link,
category = p.Categories
}
}
});
Console.WriteLine(o.ToString());
// {
// "channel": {
// "title": "Star Wars",
// "link": "http://www.starwars.com",
// "description": "Star Wars blog.",
// "item": [
// {
// "title": "Episode VII",
// "description": "Episode VII production",
// "link": "episode-vii-production.aspx",
// "category": [
// "episode-vii",
// "movie"
// ]
// }
// ]
// }
// }
从JObject恢复出类对象
这个实际上没有什么技巧,只是写一个动态创建函数:
public class BlogPost
{
public string Title { get; set; }
public string AuthorName { get; set; }
public string AuthorTwitter { get; set; }
public string Body { get; set; }
public DateTime PostedDate { get; set; }
}
string json = @"[
{
'Title': 'Json.NET is awesome!',
'Author': {
'Name': 'James Newton-King',
'Twitter': '@JamesNK',
'Picture': '/jamesnk.png'
},
'Date': '2013-01-23T19:30:00',
'BodyHtml': '<h3>Title!</h3>\r\n<p>Content!</p>'
}
]";
JArray blogPostArray = JArray.Parse(json);
IList blogPosts = blogPostArray.Select(p => new BlogPost
{
Title = (string)p["Title"],
AuthorName = (string)p["Author"]["Name"],
AuthorTwitter = (string)p["Author"]["Twitter"],
PostedDate = (DateTime)p["Date"],
Body = HttpUtility.HtmlDecode((string)p["BodyHtml"])
}).ToList();
Console.WriteLine(blogPosts[0].Body);
// Title!
// Content!
那么反过来,也可以用这个方法将对象变为Jobject对象:
IList blogPosts = new List
{
new BlogPost
{
Title = "Json.NET is awesome!",
AuthorName = "James Newton-King",
AuthorTwitter = "JamesNK",
PostedDate = new DateTime(2013, 1, 23, 19, 30, 0),
Body = @"Title!
Content!
"
}
};
JArray blogPostsArray = new JArray(
blogPosts.Select(p => new JObject
{
{ "Title", p.Title },
{
"Author", new JObject
{
{ "Name", p.AuthorName },
{ "Twitter", p.AuthorTwitter }
}
},
{ "Date", p.PostedDate },
{ "BodyHtml", HttpUtility.HtmlEncode(p.Body) },
})
);
Console.WriteLine(blogPostsArray.ToString());
// [
// {
// "Title": "Json.NET is awesome!",
// "Author": {
// "Name": "James Newton-King",
// "Twitter": "JamesNK"
// },
// "Date": "2013-01-23T19:30:00",
// "BodyHtml": "<h3>Title!</h3><p>Content!</p>"
// }
// ]
JObject对象很容易修改Json文件,修改包括增,删,重写,等操作。
string json = @"{
'channel': {
'title': 'Star Wars',
'link': 'http://www.starwars.com',
'description': 'Star Wars blog.',
'obsolete': 'Obsolete value',
'item': []
}
}";
JObject rss = JObject.Parse(json);
JObject channel = (JObject)rss["channel"];
channel["title"] = ((string)channel["title"]).ToUpper();
channel["description"] = ((string)channel["description"]).ToUpper();
channel.Property("obsolete").Remove();
channel.Property("description").AddAfterSelf(new JProperty("new", "New value"));
JArray item = (JArray)channel["item"];
item.Add("Item 1");
item.Add("Item 2");
Console.WriteLine(rss.ToString());
// {
// "channel": {
// "title": "STAR WARS",
// "link": "http://www.starwars.com",
// "description": "STAR WARS BLOG.",
// "new": "New value",
// "item": [
// "Item 1",
// "Item 2"
// ]
// }
// }
在上面的例子,依次进行了:使用函数修改值,删除属性,在某属性后面添加属性,在数组内部添加成员。
合并Json文件也很简单,类似于两个集合的操作,看下面的例子:
JObject o1 = JObject.Parse(@"{
'FirstName': 'John',
'LastName': 'Smith',
'Enabled': false,
'Roles': [ 'User' ]
}");
JObject o2 = JObject.Parse(@"{
'Enabled': true,
'Roles': [ 'User', 'Admin' ]
}");
o1.Merge(o2, new JsonMergeSettings
{
// union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union
});
string json = o1.ToString();
// {
// "FirstName": "John",
// "LastName": "Smith",
// "Enabled": true,
// "Roles": [
// "User",
// "Admin"
// ]
// }
这里要说的是,可以使用MergeArrayHandling对象来设置合并方式,上面使用的是合并模式:Union,即当前Json有时,只会出现一次,此外还有:
类似与数学中的并集,补集,叠加。
此外,MergeNullValueHandling属性可以控制在合并是值为Null的要不要被忽略;
JObject对象中的成员类型并不是C#中类型,要变成普通类型,你需要使用:ToObject
JValue v1 = new JValue(true);
bool b = v1.ToObject();
Console.WriteLine(b);
// true
int i = v1.ToObject();
Console.WriteLine(i);
// 1
string s = v1.ToObject();
Console.WriteLine(s);
// "True"
虽然很简单,但是刚接触NewtonJson可能会遇到这个问题。
使用JToken.DeepEquals函数判断两个JObject对象是否相等,这个相等必须要所有属性值一模一样:
JValue s1 = new JValue("A string");
JValue s2 = new JValue("A string");
JValue s3 = new JValue("A STRING");
Console.WriteLine(JToken.DeepEquals(s1, s2));
// true
Console.WriteLine(JToken.DeepEquals(s2, s3));
// false
JObject o1 = new JObject
{
{ "Integer", 12345 },
{ "String", "A string" },
{ "Items", new JArray(1, 2) }
};
JObject o2 = new JObject
{
{ "Integer", 12345 },
{ "String", "A string" },
{ "Items", new JArray(1, 2) }
};
Console.WriteLine(JToken.DeepEquals(o1, o2));
// true
Console.WriteLine(JToken.DeepEquals(s1, o1["String"]));
// true
注意:虽然两个json问价的内容一样,但是它们毕竟是2个不同的对象,使用JToken.RefernceEquals判断会返回false。
对于拷贝,主要是对内容进行拷贝,但是创建的是新对象:
JValue s1 = new JValue("A string");
JValue s2 = new JValue("A string");
JValue s3 = new JValue("A STRING");
Console.WriteLine(JToken.DeepEquals(s1, s2));
// true
Console.WriteLine(JToken.DeepEquals(s2, s3));
// false
JObject o1 = new JObject
{
{ "Integer", 12345 },
{ "String", "A string" },
{ "Items", new JArray(1, 2) }
};
JObject o2 = new JObject
{
{ "Integer", 12345 },
{ "String", "A string" },
{ "Items", new JArray(1, 2) }
};
Console.WriteLine(JToken.DeepEquals(o1, o2));
// true
Console.WriteLine(JToken.DeepEquals(s1, o1["String"]));
// true
以上就是对NewtonJson的一点总结,很多是参考官方文档,我只选了一些常见的知识点进行介绍,建议大家遇到不能解决的问题时还是看官方文档。
一般而言,我更倾向与使用JObject对象处理Json,因为你可以获得JavaScript处理Json的90%的体验,使用起来很灵活,而且结合Linq语法,可以很方便的处理它。