本文講講一些純技術的東西。並且講講一些原理性的東西,和一般的百度的文章不一致,如果你對序列化不清楚,絕對可以很有收獲。
ok,我們先建一個控制台項目:
安裝 Newtonsoft.Json 組件
ok,安裝完成之后,我們來講講序列化的本質:
為什么需要序列化?
要理解這個問題很關鍵。很多事就會豁然開朗,與其講為什么序列化,不如我們先來看看一個例子:
我們存儲了一條字符串的數據到文件
我們可能會這么寫
public static void SaveString(string str ) { using(System.IO.StreamWriter sw = new System.IO.StreamWriter( "123.txt", false, Encoding.Default )) { sw.Write( str ); } }
然后讀取你存儲的數據時候,會這么讀
public static string ReadString( ) { using (System.IO.StreamReader sr = new System.IO.StreamReader( "123.txt", Encoding.Default )) { return sr.ReadToEnd( ); } }
其實這里的代碼已經實現了最基本的序列化功能。如果你覺得存儲string很簡單,那么來存儲一個int[]數組把。將整個數據存儲到文件,然后讀取加載
public static void SaveIntArray( int[] value ) { byte[] buffer = new byte[value.Length * 4]; for (int i = 0; i < value.Length; i++) { BitConverter.GetBytes( value[i] ).CopyTo( buffer, i * 4 ); } System.IO.File.WriteAllBytes( "1234.txt", buffer ); }
然后加載的步驟是
public static int[] ReadIntArray( ) { byte[] buffer = System.IO.File.ReadAllBytes( "1234.txt" ); int[] value = new int[buffer.Length / 4]; for (int i = 0; i < value.Length; i++) { value[i] = BitConverter.ToInt32( buffer, i * 4 ); } return value; }
上述的兩個示例展示了最基本的序列化和反序列化操作。我們實現序列化和反序列通常是用於寫入文件,加載數據,發送到網絡,從網絡接收數據。
這個就是我們最大的需求了。
實際復雜的數據
比如我們有一串復雜的數據,需要寫文件,或是收發網絡,寫文件和收發網絡的本質是讀寫byte[]數組。而byte[]數組和string是幾乎等效的,可以相互轉換。很多的文件寫入,或是網絡收發都是支持支持string的
所以我們的序列化和反序列化的本質就是實際的數據和string的相互轉換。
比如這個數據對象
public class Account { public string UserName { get; set; } public string Password { get; set; } public int Age { get; set; } public string Phone { get; set; } public double Stature { get; set; } }
要把這個數據對象轉換成string,然后支持轉換回來,應該怎么操作呢?
接下來就是干貨了
XML序列化及,反序列化
直接上代碼:
public class Account { public string UserName { get; set; } public string Password { get; set; } public int Age { get; set; } public string Phone { get; set; } public double Stature { get; set; } public string toXml( ) { System.Xml.Linq.XElement element = new System.Xml.Linq.XElement( "Account" ); element.SetElementValue( nameof( UserName ), UserName ); element.SetElementValue( nameof( Password ), Password ); element.SetElementValue( nameof( Age ), Age.ToString() ); element.SetElementValue( nameof( Phone ), Phone ); element.SetElementValue( nameof( Stature ), Stature.ToString( ) ); return element.ToString( ); } public void LoadByXml( string data ) { System.Xml.Linq.XElement element = System.Xml.Linq.XElement.Parse( data ); UserName = element.Element( nameof( UserName ) ).Value; Password = element.Element( nameof( Password ) ).Value; Age = int.Parse( element.Element( nameof( Age ) ).Value ); Phone = element.Element( nameof( Phone ) ).Value; Stature = double.Parse( element.Element( nameof( Stature ) ).Value ); } }
我們改造一下就可以了,現在這個類支持了序列化及反序列了,我們看看控制台輸出了什么?
static void Main( string[] args ) { Account account = new Account( ) { UserName = "張三", Password = "123456", Age = 25, Phone = "12345678901", Stature = 170.2d }; Console.WriteLine( account.toXml( ) ); Console.ReadLine( ); }
看看控制台:
輸出了我們想要的字符串信息。好了,接下來,我們看看JSON格式的數據
JSON序列化及反序列化
json因為庫的方便性,所以不需要寫多余的代碼了
static void Main( string[] args ) { Account account = new Account( ) { UserName = "張三", Password = "123456", Age = 25, Phone = "12345678901", Stature = 170.2d }; //Console.WriteLine( account.toXml( ) ); Console.WriteLine( JObject.FromObject( account ).ToString( ) ); Console.ReadLine( ); }
輸出
靈活的操作
現在有個小問題,密碼部分的內容我不想支持序列化,或是換句話說,密碼的信息在序列化的時候忽略。那么類改成下面即可
public class Account { public string UserName { get; set; } [Newtonsoft.Json.JsonIgnore] public string Password { get; set; } public int Age { get; set; } public string Phone { get; set; } public double Stature { get; set; } public string toXml( ) { System.Xml.Linq.XElement element = new System.Xml.Linq.XElement( "Account" ); element.SetElementValue( nameof( UserName ), UserName ); //element.SetElementValue( nameof( Password ), Password ); element.SetElementValue( nameof( Age ), Age.ToString() ); element.SetElementValue( nameof( Phone ), Phone ); element.SetElementValue( nameof( Stature ), Stature.ToString( ) ); return element.ToString( ); } public void LoadByXml( string data ) { System.Xml.Linq.XElement element = System.Xml.Linq.XElement.Parse( data ); UserName = element.Element( nameof( UserName ) ).Value; //Password = element.Element( nameof( Password ) ).Value; Age = int.Parse( element.Element( nameof( Age ) ).Value ); Phone = element.Element( nameof( Phone ) ).Value; Stature = double.Parse( element.Element( nameof( Stature ) ).Value ); } }
這樣就忽略了密碼的xml序列化和json的序列化
xml支持子元素和特性,內容存儲上更加的豐富,json更加直接一點,內容精簡一點,json也可以寫成上述的xml靈活的方法,這樣就支持任意格式對象的序列化和反序列化了。
public class Account { public string UserName { get; set; } [Newtonsoft.Json.JsonIgnore] public string Password { get; set; } public int Age { get; set; } public string Phone { get; set; } public double Stature { get; set; } public string toXml( ) { System.Xml.Linq.XElement element = new System.Xml.Linq.XElement( "Account" ); element.SetElementValue( nameof( UserName ), UserName ); //element.SetElementValue( nameof( Password ), Password ); element.SetElementValue( nameof( Age ), Age.ToString() ); element.SetElementValue( nameof( Phone ), Phone ); element.SetElementValue( nameof( Stature ), Stature.ToString( ) ); return element.ToString( ); } public void LoadByXml( string data ) { System.Xml.Linq.XElement element = System.Xml.Linq.XElement.Parse( data ); UserName = element.Element( nameof( UserName ) ).Value; //Password = element.Element( nameof( Password ) ).Value; Age = int.Parse( element.Element( nameof( Age ) ).Value ); Phone = element.Element( nameof( Phone ) ).Value; Stature = double.Parse( element.Element( nameof( Stature ) ).Value ); } public string toJson( ) { JObject json = new JObject( ); //json.Add( nameof( Password ), new JValue( Password ) ); json.Add( nameof( UserName ), new JValue( UserName ) ); json.Add( nameof( Age ), new JValue( Age ) ); json.Add( nameof( Phone ), new JValue( Phone ) ); json.Add( nameof( Stature ), new JValue( Stature ) ); return json.ToString( ); } public void LoadByJson(string data ) { JObject json = JObject.Parse( data ); UserName = json[nameof( UserName )].Value<string>( ); //Password = json[nameof( Password )].Value<string>( ); Age = json[nameof( Age )].Value<int>( ); Phone = json[nameof( Phone )].Value<string>( ); Stature = json[nameof( Stature )].Value<double>( ); } }
這樣也能實現任意的序列化操作,甚至針對其中某個屬性進行加密解密都可以。
性能對比(具體時間取決於電腦性能,我的cpu : i5-4590 內存 ddr3-1600)
兩種序列化都有適合的場景,此處演示下序列化1W次的性能對比,看看性能差異
static void Main( string[] args ) { Account account = new Account( ) { UserName = "張三", Password = "123456", Age = 25, Phone = "12345678901", Stature = 170.2d }; DateTime start = DateTime.Now; for (int i = 0; i < 10000; i++) { string value = account.toXml( ); ; } Console.WriteLine( "Time:" + (DateTime.Now - start).TotalMilliseconds ); start = DateTime.Now; for (int i = 0; i < 10000; i++) { string value = JObject.FromObject( account ).ToString( ); ; } Console.WriteLine( "Time:" + (DateTime.Now - start).TotalMilliseconds ); start = DateTime.Now; for (int i = 0; i < 10000; i++) { string value = account.toJson( ); ; } Console.WriteLine( "Time:" + (DateTime.Now - start).TotalMilliseconds ); Console.ReadLine( ); }
第一次運行:
第二次運行:
第三次運行:
接下來加上解析部分
{ Account account = new Account( ) { UserName = "張三", Password = "123456", Age = 25, Phone = "12345678901", Stature = 170.2d }; DateTime start = DateTime.Now; for (int i = 0; i < 10000; i++) { string value = account.toXml( ); Account account1 = new Account( ); account1.LoadByXml( value ); } Console.WriteLine( "Time:" + (DateTime.Now - start).TotalMilliseconds ); start = DateTime.Now; for (int i = 0; i < 10000; i++) { string value = JObject.FromObject( account ).ToString( ); Account account1 = JObject.Parse( value ).ToObject<Account>( ); } Console.WriteLine( "Time:" + (DateTime.Now - start).TotalMilliseconds ); start = DateTime.Now; for (int i = 0; i < 10000; i++) { string value = account.toJson( ); Account account1 = new Account( ); account1.LoadByJson( value ); } Console.WriteLine( "Time:" + (DateTime.Now - start).TotalMilliseconds ); Console.ReadLine( ); }
我們再來看看運行結果:
第二次運行結果:
第三次結果:
終極性能PK
如果我們自己來寫json的序列化呢?
public class Account { public string UserName { get; set; } [Newtonsoft.Json.JsonIgnore] public string Password { get; set; } public int Age { get; set; } public string Phone { get; set; } public double Stature { get; set; } public string toXml( ) { System.Xml.Linq.XElement element = new System.Xml.Linq.XElement( "Account" ); element.SetElementValue( nameof( UserName ), UserName ); //element.SetElementValue( nameof( Password ), Password ); element.SetElementValue( nameof( Age ), Age.ToString() ); element.SetElementValue( nameof( Phone ), Phone ); element.SetElementValue( nameof( Stature ), Stature.ToString( ) ); return element.ToString( ); } public void LoadByXml( string data ) { System.Xml.Linq.XElement element = System.Xml.Linq.XElement.Parse( data ); UserName = element.Element( nameof( UserName ) ).Value; //Password = element.Element( nameof( Password ) ).Value; Age = int.Parse( element.Element( nameof( Age ) ).Value ); Phone = element.Element( nameof( Phone ) ).Value; Stature = double.Parse( element.Element( nameof( Stature ) ).Value ); } public string toJson( ) { JObject json = new JObject( ); //json.Add( nameof( Password ), new JValue( Password ) ); json.Add( nameof( UserName ), new JValue( UserName ) ); json.Add( nameof( Age ), new JValue( Age ) ); json.Add( nameof( Phone ), new JValue( Phone ) ); json.Add( nameof( Stature ), new JValue( Stature ) ); return json.ToString( ); } public void LoadByJson(string data ) { JObject json = JObject.Parse( data ); UserName = json[nameof( UserName )].Value<string>( ); //Password = json[nameof( Password )].Value<string>( ); Age = json[nameof( Age )].Value<int>( ); Phone = json[nameof( Phone )].Value<string>( ); Stature = json[nameof( Stature )].Value<double>( ); } public string toMyJson( ) { StringBuilder sb = new StringBuilder( ); sb.Append( "{" ); sb.Append( Environment.NewLine ); sb.Append( $" \"{nameof( UserName )}\":" ); sb.Append( $"\"{ UserName.Replace( "\"", "\\\"" )}\"," ); sb.Append( Environment.NewLine ); sb.Append( $" \"{nameof( Password )}\":" ); sb.Append( $"\"{ Password.Replace( "\"", "\\\"" ) }\"," ); sb.Append( Environment.NewLine ); sb.Append( $" \"{nameof( Age )}\":" ); sb.Append( $"{Age}," ); sb.Append( Environment.NewLine ); sb.Append( $" \"{nameof( Phone )}\":" ); sb.Append( $"\"{ Phone.Replace( "\"", "\\\"" )}\"," ); sb.Append( Environment.NewLine ); sb.Append( $" \"{nameof( Stature )}\":" ); sb.Append( $"{Stature}," ); sb.Append( Environment.NewLine ); sb.Append( "}" ); return sb.ToString( ); } }
上述的方法 toMyJson 就是我們所寫的一個方法名稱,這個方法會主動進行序列化,里面也考慮了,如果字符串數據帶有"的情況,需要進行替換 \" 的情況,我們單獨運行這個序列化的方法10000次
所以關於性能,可以大致做個參考
以上的結果只能參照一下,寫法上大家可以根據自己的需求來選擇,性能上肯定會有所差別的。感謝閱讀: