【什么是Json】
序列化:将对象转换成字节流的过程,这样就可以轻松将对象保存在磁盘文件或数据库中。
反序列化:序列化的逆过程,就是将字节流转换回原来的对象的过程。
当前主流的序列化JSON字符串主要有两种方式:JavaScriptSerializer及Json.net(Nuget标识:Newtonsoft.Json)。JavaScriptSerializer是微软官方提供的一种方法,所以如果你用的是asp.netmvc,在Action中如果你返回的语句写的是”return Json(xxx);“,其实你用的就是JavaScriptSerializer方式。
Json,它是一个轻量级的数据交换格式,我们可以很简单的来读取和写它,并且它很容易被计算机转化和生成,它是完全独立于语言的。
【Json支持下面两种数据结构】
键值对的集合--各种不同的编程语言,都支持这种数据结构;
有序的列表类型值的集合--这其中包含数组,集合,矢量,或者序列,等等。
Json有下面几种表现形式
1.对象
一个没有顺序的“键/值”,一个对象以花括号“{”开始,并以花括号"}"结束,在每一个“键”的后面,有一个冒号,并且使用逗号来分隔多个键值对。例如:
var user = {"name":"Manas","gender":"Male","birthday":"1987-8-8"}
2.数组
设置值的顺序,一个数组以中括号"["开始,并以中括号"]"结束,并且所有的值使用逗号分隔,例如:
var userlist = [{"user":{"name":"Manas","gender":"Male","birthday":"1987-8-8"}},
{"user":{"name":"Mohapatra","Male":"Female","birthday":"1987-7-7"}}]
3.字符串
任意数量的Unicode字符,使用引号做标记,并使用反斜杠来分隔。例如:
var userlist = "{\"ID\":1,\"Name\":\"Manas\",\"Address\":\"India\"}"
【序列化和反序列化有三种方式】
1.使用JavaScriptSerializer类
2.使用DataContractJsonSerializer类
3.使用JSON.NET类库
【示例一】DataContractJsonSerializer类帮助我们序列化和反序列化Json,他在程序集 System.Runtime.Serialization.dll下的System.Runtime.Serialization.Json命名空间里。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;
namespace JsonSerializerAndDeSerializer
{
//服务契约定义了远程访问对象和可供调用的方法,数据契约则是服务端和客户端之间要传送的自定义数据类型。
//一旦声明一个类型为DataContract,那么该类型就可以被序列化在服务端和客户端之间传送
[DataContract]
public class Person
{
//只有声明为DataContract的类型的对象可以被传送,且只有成员属性会被传递,成员方法不会被传递。
//WCF对声明为DataContract的类型提供更加细节的控制,可以把一个成员排除在序列化范围以外,也就是说,
//客户端程序不会获得被排除在外的成员的任何信息,包括定义和数据。默认情况下,所有的成员属性都被排除在外,
//因此需要把每一个要传送的成员声明为DataMember
//注意:Person实体中的契约 [DataMember],[DataContract],是使用DataContractJsonSerializer序列化和反序列
//化必须要加的,对于其他两种方式不必加,也可以的。
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
[DataMember]
public string Sex { get; set; }
}
}
#region 1.DataContractJsonSerializer方式序列化和反序列化
Person stu = new Person()
{
ID = 1,
Name = "老三",
Sex = "男",
Age = 23
};
//序列化
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(Student));
MemoryStream msObj = new MemoryStream();
//将序列化之后的Json格式数据写入流中
js.WriteObject(msObj, stu);
msObj.Position = 0;
//从0这个位置开始读取流中的数据
StreamReader sr = new StreamReader(msObj, Encoding.UTF8);
string json = sr.ReadToEnd();
sr.Close();
msObj.Close();
Console.WriteLine(json);
//反序列化
string toDes = json;
//string to = "{\"ID\":\"1\",\"Name\":\"曹操\",\"Sex\":\"男\",\"Age\":\"1230\"}";
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(toDes)))
{
DataContractJsonSerializer deseralizer = new DataContractJsonSerializer(typeof(Student));
Student model = (Student)deseralizer.ReadObject(ms);// //反序列化ReadObject
Console.WriteLine("ID=" + model.ID);
Console.WriteLine("Name=" + model.Name);
Console.WriteLine("Age=" + model.Age);
Console.WriteLine("Sex=" + model.Sex);
}
Console.ReadKey();
#endregion
【示例二】
#region 2.JavaScriptSerializer方式实现序列化和反序列化
Student stu = new Student()
{
ID = 1,
Name = "关羽",
Age = 2000,
Sex = "男"
};
JavaScriptSerializer js = new JavaScriptSerializer();
string jsonData = js.Serialize(stu);//序列化
Console.WriteLine(jsonData);
////反序列化方式一:
string desJson = jsonData;
//Student model = js.Deserialize<Student>(desJson);// //反序列化
//string message = string.Format("ID={0},Name={1},Age={2},Sex={3}", model.ID, model.Name, model.Age, model.Sex);
//Console.WriteLine(message);
//Console.ReadKey();
////反序列化方式2
dynamic modelDy = js.Deserialize<dynamic>(desJson); //反序列化
string messageDy = string.Format("动态的反序列化,ID={0},Name={1},Age={2},Sex={3}",
modelDy["ID"], modelDy["Name"], modelDy["Age"], modelDy["Sex"]);//这里要使用索引取值,不能使用对象.属性
Console.WriteLine(messageDy);
Console.ReadKey();
#endregion
【示例三】
/使用Json.NET类库需要引入的命名空间
//-----------------------------------------------------------------------------
using Newtonsoft.Json;
//-------------------------------------------------------------------------
#region 3.Json.NET序列化
List<Student> lstStuModel = new List<Student>()
{
new Student(){ID=1,Name="张飞",Age=250,Sex="男"},
new Student(){ID=2,Name="潘金莲",Age=300,Sex="女"}
};
//Json.NET序列化
string jsonData = JsonConvert.SerializeObject(lstStuModel);
Console.WriteLine(jsonData);
Console.ReadKey();
//Json.NET反序列化
string json = @"{ 'Name':'C#','Age':'3000','ID':'1','Sex':'女'}";
Student descJsonStu = JsonConvert.DeserializeObject<Student>(json);//反序列化
Console.WriteLine(string.Format("反序列化: ID={0},Name={1},Sex={2},Sex={3}", descJsonStu.ID, descJsonStu.Name, descJsonStu.Age, descJsonStu.Sex));
Console.ReadKey();
#endregion
【Json格式化输出】
效果:
//格式化前的字符串:
{id=11,name="xxj",age=111,time=DateTime.Now};
//格式化后的字符串:
{
id=11,
name="xxj",
age=111,
time='2010-1-1 1:1:1'
};
自定义扩展方法
/// <summary>
/// 生成Json格式
/// key=字段名,value=字段值
/// </summary>
/// <typeparam name="T">待序列化的对象</typeparam>
/// <returns></returns>
public static string ToJsonSerialize<T>(this T obj) //where T : class
{
if (obj.IsNullOrDbNull())
return string.Empty;
if (obj is DataTable)
return DataTableToJson(obj as DataTable);
JavaScriptSerializer serialize = new JavaScriptSerializer();
//针对日期序列化时区的转化
var json = Regex.Replace(serialize.Serialize(obj), @"\\/Date(−?\d+)(−?\d+)\\/", match =>
{
var dt = new DateTime(1970, 1, 1);
dt = dt.AddMilliseconds(long.Parse(match.Groups[1].Value));
dt = dt.ToLocalTime();
return dt.ToString("yyyy-MM-dd HH:mm:ss");
});
return json;
}
格式化Json代码
/// <summary>
/// 测试json字符串格式化
/// </summary>
[TestMethod]
public void TestJsonFormat()
{
//格式化json字符串
var data = new
{
id=11,
name="xxj",
age=111,
time=DateTime.Now
};
var json = data.ToJsonSerialize();
//Newtonsoft.Json.dll, v4.5.0.0
JsonSerializer serializer = new JsonSerializer();
TextReader tr = new StringReader(json);
JsonTextReader jtr = new JsonTextReader(tr);
object obj = serializer.Deserialize(jtr);
if (obj != null)
{
StringWriter textWriter = new StringWriter();
JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
{
Formatting = Formatting.Indented,
Indentation = 4,
IndentChar = ' '
};
serializer.Serialize(jsonWriter, obj);
var jsonF = textWriter.ToString();
}
Assert.IsTrue(true);
}
【JavaScriptSerializer与Json.net】
【序列化】
我们先定义一个测试用的简单类--Person:
public class Person
{
public string Name;
public int Age;
public Guid TokenId;
public DateTime RegTime;
public Person Child;
public Person Friend;
}
类中的成员仅用来区分不同的变量类型。我们分别以JavaScriptSerializer和Json.net来序列化:
var person = new Person
{
Age = 28,
Name = "李玉宝<yubaolee:>",//故意添加特殊字符
RegTime = DateTime.Now,
TokenId = Guid.NewGuid(),
Child = new Person
{
Age = 1,
Name = "baby",
RegTime = DateTime.Now,
TokenId = Guid.NewGuid()
}
}; //注意这里面的Friend没有赋值,默认为空
JavaScriptSerializer serializer = new JavaScriptSerializer();
var jsstr = serializer.Serialize(person); //使用JavaScriptSerializer序列化
string newtonstr = JsonConvert.SerializeObject(person); //使用Json.net序列化
JavaScriptSerializer序列化是先生成一个对象,然后调用它的成员函数Serialize进行序列化;
Json.net直接使用提供的静态成员JsonConvert.SerializeObject进行序列化;
上面绿色为JavaScriptSerializer的结果,下面黄色背景为Json.net的结果,这里需要注意几个地方:
1、 JavaScriptSerializer序列化后的时间格式:"\/Date(1441813200214)\/" 表示的是1970年1月1日(DateTime的最小值)到date实际表示的日期之差的总毫秒数。通常我们需要把它转成标准的时间格式。可以用下面的方法进行字符串处理:
jsstr = Regex.Replace(jsstr, @"\\/Date(\d+)(\d+)\\/", match =>
{
DateTime dt = new DateTime(1970, 1, 1);
dt = dt.AddMilliseconds(long.Parse(match.Groups[1].Value));
dt = dt.ToLocalTime();
return dt.ToString("yyyy-MM-dd HH:mm:ss");
});
处理完成后的效果:
当然,你还可以通过使用继承JavaScriptConverter的方式,下面反序列化中会具体提及到这种方式。
Json.net默认生成的日期也不方便客户端阅读,需要简单的处理一下:
string newtonstr = JsonConvert.SerializeObject(p, Formatting.Indented,
new IsoDateTimeConverter() {DateTimeFormat = "yyyy-MM-dd HH:mm:ss"});
2、JavaScriptSerializer序列化会对特殊字符(如<>等)进行编码,比如上面的\u003c \u003e,很多人看到这个的时候,第一感觉就是太扯蛋了,接下来就是各种百度,怎么把这个转成正常的”<>”。实际上你不用做任何操作,这是标准的JS编码方式,前端会自行处理这个问题。比如:
<script type="text/javascript">
var str = 'yubaolee <yubaolee>'
var str2 = 'yubaolee \u003cyubaolee\u003e';
alert(str == str2); //结果为true
</script>
从上面两点可以看出,JavaScriptSerializer序列化出来的JSON字符串容易给人造成一些困惑,而Json.net完全没有上面的两种情况处理。所以现在很多人都在用Json.net,但从Html标准的角度上来看,JavaScriptSerializer序列化出来的结果更符合Html的要求。不过为了操作习惯,还是建议使用Json.net。
【反序列化】
我们分别用两种方式对上面已经成功序列化的两个字符串进行反序列化:
//对JavaScriptSerializer生成的字符串进行反序列化
//使用JavaScriptSerializer方式
var jsperson = serializer.Deserialize<Person>(jsstr);
//使用Json.net方式
var newtonperson = JsonConvert.DeserializeObject<Person>(jsstr);
//对Json.net生成的字符串进行反序列化
var jsperson2 = serializer.Deserialize<Person>(newtonstr);
var newtonperson2 = JsonConvert.DeserializeObject<Person>(newtonstr);
通过运行会发现4个反序列化代码都能正常运行,而不是像以前某些前辈说的,JavaScriptSerializer序列化的串只能用它反序列化,Json.net序列化的串只能用Json.net来反序列化。
上面反序列化的字符串是程序生成的,能正常反序列化不足为奇。但通常我们要反序列化的字符串是客户提交到服务器上面来的串,他们通常是不完整的,或有些还会出现类型不符的情况。比如:
string noChildStr =
"{\"Name\":\"李玉宝<yubaolee:>\"," +
"\"Age\":28," +
"\"RegTime\":\"2015-09-11 00:10:48\"," +
"\"Friend\":null}";
var jsperson = new JavaScriptSerializer().Deserialize<Person>(noChildStr);
var newtonperson = JsonConvert.DeserializeObject<Person>(noChildStr);
注意这个字符串中,没有TokenId,没有Child,而且Friend为null。看一看结果:
!解析的结果全部是我们想要的内容。但如果像下面这样呢?
string noChildStr =
"{\"Name\":\"李玉宝<yubaolee:>\"," +
"\"Age\":28," +
"\"RegTime\":\"2015-09-11 00:10:48\"," +
"\"Friend\":null," +
"\"TokenId\":null}"; //注意这个TokenId为空
在运行的时候,程序会直接报错。
错误的内容很容易理解,因为我们把一个null赋值给Guid类型,肯定会出错。在实际的项目操作过程中还有可能出现把一个内容为空的字符串传给Datetime类型,把一个数字传给GUID等各种参数传递的问题,关键是我们还要来处理它们,而不能使程序直接报错崩溃。
1、在JavaScriptSerializer中有一个JavaScriptConverter可以来处理这些问题,它是用来实现JSON序列化中自定义类型的处理。比如下面的代码,就是处理把一个null赋值给Guid的情况:
public class PersonJsConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
Person person = new Person();
object value = null;
if (dictionary.TryGetValue("TokenId", out value) && value != null)
person.TokenId = (Guid) value;
//其他字段略...
return person;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
var node = obj as Person;
if (node == null)
return null;
if (!string.IsNullOrEmpty(node.Name))
dic.Add("Name", node.Name);
//其他字段略...
return dic;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new Type[] { typeof(Person) };
}
}
}
然后在反序列化之前,我们把这个转换注册到实体对象中,这时再执行,程序就一切正常了:
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new PersonJsConverter(), });
var deserialize = serializer.Deserialize<Person>(noChildStr);
2、在使用Json.net时,采用了一种更加优雅的方式来处理这个问题--JsonConverter,它可以单独处理一个指定的类成员变量。这样就不用像上面的JavaScriptConverter一样处理整个类的所有成员。代码如下:
public class GuidConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(Guid));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
return Guid.Parse(reader.Value.ToString());
}
catch
{
//如果传进来的值造成异常,则赋值一个初值
return Guid.Empty;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
值得注意的是JsonConverter是一个Attribute,所以要在类成员上面添加一个特性:
public class Person
{
public string Name;
public int Age;
[JsonConverter(typeof(GuidConverter))]
public Guid TokenId { get; set; }
public DateTime RegTime;
public Person Child;
public Person Friend;
}