使用Json.Net處理json序列化和反序列化接口或繼承類

https://www.cnblogs.com/OpenCoder/p/4524786.html

以前一直沒有怎麼關注過Newtonsoft的Json.Net這個第三方的.NET Json框架,主要是我以前在開發項目的時候大多數使用的都是.NET自帶的Json序列化類JavaScriptSerializer,但是最近在項目中需要序列化和反序列化一個實現接口的類,而如果使用JavaScriptSerializer的話就會出現問題,我們來看看如下場景。

 

首先我們有一個接口IPeople和一個實現了該接口的類Man

複製代碼

interface IPeople
{
    string Name { get; set; }
    int Age { get; set; }
}

class Man : IPeople
{
    public string Name { get; set; }

    public int Age { get; set; }
}

複製代碼


我們使用JavaScriptSerializer直接序列化IPeople接口

複製代碼

IPeople poeple = new Man();
poeple.Age = 25;
poeple.Name = "Scott";

JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
string textJson = jsSerializer.Serialize(poeple);
poeple = jsSerializer.Deserialize<IPeople>(textJson);

複製代碼

會得到序列化後的json文本textJson如下

{"Name":"Scott","Age":25}

我們可以看到在序列化後的json中沒有任何屬性說明這段json到底是由什麼類序列化而來的,緊接着在JavaScriptSerializer執行jsSerializer.Deserialize<IPeople>(textJson)做反序列化的時候就拋出了異常提示IPeople沒有默認無參構造函數,也就是說JavaScriptSerializer不知道應該把textJson中的json反序列化爲類Man。

 

而如果我們使用的是Json.NET的話,就可以完美的實現接口IPeople的序列化和反序列化,我們來看看怎麼使用Json.NET的序列化和反序列化

複製代碼

IPeople poeple = new Man();
poeple.Age = 25;
poeple.Name = "Scott";

JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.TypeNameHandling = TypeNameHandling.All;//這一行就是設置Json.NET能夠序列化接口或繼承類的關鍵,將TypeNameHandling設置爲All後,Json.NET會在序列化後的json文本中附加一個屬性說明json到底是從什麼類序列化過來的,也可以設置TypeNameHandling爲Auto,表示讓Json.NET自動判斷是否需要在序列化後的json中添加類型屬性,如果序列化的對象類型和聲明類型不一樣的話Json.NET就會在json中添加類型屬性,反之就不添加,但是我發現TypeNameHandling.Auto有時候不太好用。。。
string textJson = JsonConvert.SerializeObject(poeple, jsonSerializerSettings);//將JsonSerializerSettings作爲參數傳入序列化函數,這樣序列化後的Json就附帶類型屬性
poeple = JsonConvert.DeserializeObject<IPeople>(textJson, jsonSerializerSettings);//將JsonSerializerSettings作爲參數傳入反序列化函數,這樣Json.NET就會讀取json文本中的類型屬性,知道應該反序列化成什麼類型

複製代碼

這裏IPeople接口能被成功序列化和返序列化的關鍵就是jsonSerializerSettings.TypeNameHandling = TypeNameHandling.All這行代碼,我們來看看Json.NET序列化後的json文本信息

{"$type":"Json.Man, Json","Name":"Scott","Age":25}

可以看到Json.NET在序列化後的json文本中添加了一個屬性叫$type來說明json是從Json.Man類序列化而來的,那麼後面再反序列化的時候Json.NET就成功地將上面的json文本反序列化成了類Man.

 

所以Json.NET在做json的序列化和反序列化的時候比JavaScriptSerializer更全面,當然在使用JavaScriptSerializer的時候自定義Converter也可以做到序列化接口和繼承類,但是這要麻煩很多。這一點也會讓我以後更多使用Json.NET來實現json的序列化和反序列化。

 

給出一個設置TypeNameHandling.Auto的例子說明,是老外寫的,我覺得將TypeNameHandling.Auto解釋得很清楚了。

Json.Net has a setting that intelligently adds type information - declare it like this:

new JsonSerializer { TypeNameHandling = TypeNameHandling.Auto };

This will determine whether type embedding is required and add it where necessary. Lets say I had the following classes:

複製代碼

public class Message
{
    public object Body { get; set; }
}

public class Person
{
    public string Name { get; set; }
}

public class Manager : Person
{

}

public class Department
{
    private List<Person> _employees = new List<Person>();
    public List<Person> Employees { get { return _employees; } }
}

複製代碼

 

Notice the Message Body is of type object, and that Manager subclasses Person. If I serialize a Message with a Department Body that has a single Manager I get this:

複製代碼

{
    "Body":
    {
        "$type":"Department, MyAssembly",
        "Employees":[
            {
                "$type":"Manager, MyAssembly",
                "Name":"Tim"
            }]
    }
}

複製代碼


Notice how it's added the $type property to describe the Department and Manager types. If I now add a Person to the Employees list and change the Message Body to be of type Department like this:

public class Message
{
    public Department Body { get; set; }
}

 

then the Body type annotation is no longer needed and the new Person is not annotated - absence of annotation assumes the element instance is of the declared array type. The serialized format becomes:

複製代碼

{
    "Body":
    {
        "Employees":[
            {
                "$type":"Manager, MyAssembly",
                "Name":"Tim"
            },
            {
                "Name":"James"
            }]
    }
}

複製代碼

This is an efficient approach - type annotation is only added where required. While this is .NET specific, the approach is simple enough to handle that deserializers/message types on other platforms should be fairly easily extended to handle this.

I'd be reticent about using this in a public API though, as it is non-standard. In that case you'd want to avoid polymorphism, and make versioning and type information very explicit properties in the message.

 

最後通過我寫的一個例子來演示怎麼自定義和使用Json.Net的轉換器,這個例子還闡述了Json.Net在序列化和反序列化實現了接口IEnumerable的類時所遇到的問題,有興趣的朋友可以下載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章