相关文章推荐
睿智的大熊猫  ·  Libssl Segfaults on ...·  1 年前    · 
发呆的洋葱  ·  boolzhu - 知乎·  1 年前    · 
  • 將 CLR 物件寫入 HTTP 訊息本文
  • Web API 提供 JSON 和 XML 的媒體類型格式器。 架構預設會將這些格式子插入管線中。 用戶端可以在 HTTP 要求的 Accept 標頭中要求 JSON 或 XML。

  • JSON Media-Type Formatter

  • Camel 大小寫
  • 匿名和Weakly-Typed物件
  • XML Media-Type Formatter

  • 設定 XML 序列化程式Per-Type
  • 移除 JSON 或 XML 格式器

  • 處理迴圈物件參考

  • 測試物件序列化

    JSON Media-Type Formatter

    JSON 格式是由 JsonMediaTypeFormatter 類別提供。 根據預設, JsonMediaTypeFormatter 會使用 Json.NET 程式庫來執行序列化。 Json.NET 是協力廠商開放原始碼專案。

    如果您想要的話,您可以將 JsonMediaTypeFormatter 類別設定為使用 DataContractJsonSerializer ,而不是 Json.NET。 若要這樣做,請將 UseDataContractJsonSerializer 屬性設定為 true

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.UseDataContractJsonSerializer = true;
    

    JSON 序列化

    本節說明 JSON 格式器的某些特定行為,使用預設 Json.NET 序列化程式。 這不是 Json.NET 程式庫的完整檔;如需詳細資訊,請參閱 Json.NET 檔

    哪些專案會序列化?

    根據預設,所有公用屬性和欄位都會包含在序列化的 JSON 中。 若要省略屬性或欄位,請使用 JsonIgnore 屬性裝飾它。

    public class Product
        public string Name { get; set; }
        public decimal Price { get; set; }
        [JsonIgnore]
        public int ProductCode { get; set; } // omitted
    

    如果您偏好「加入」方法,請使用 DataContract 屬性裝飾 類別。 如果存在此屬性,除非成員具有 DataMember,否則會忽略成員。 您也可以使用 DataMember 來序列化私人成員。

    [DataContract]
    public class Product
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public decimal Price { get; set; }
        public int ProductCode { get; set; }  // omitted by default
    

    Read-Only屬性

    唯讀屬性預設會序列化。

    根據預設,Json.NET 以 ISO 8601 格式寫入日期。 UTC (國際標準時間) 的日期會以 「Z」 尾碼撰寫。 當地時間的日期包含時區位移。 例如:

    2012-07-27T18:51:45.53403Z         // UTC
    2012-07-27T11:51:45.53403-07:00    // Local
    

    根據預設,Json.NET 保留時區。 您可以藉由設定 DateTimeZoneHandling 屬性來覆寫此專案:

    // Convert all dates to UTC
    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
    

    如果您想要使用 Microsoft JSON 日期格式 ("\/Date(ticks)\/") 而不是 ISO 8601,請在序列化程式設定上設定 DateFormatHandling 屬性:

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.DateFormatHandling 
    = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;
    

    若要撰寫縮排 JSON,請將 [格式化] 設定設定為 Formatting.Indented

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
    

    Camel 大小寫

    若要使用 camel 大小寫來撰寫 JSON 屬性名稱,而不需變更您的資料模型,請在序列化程式上設定 CamelCasePropertyNamesContractResolver

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    

    匿名和Weakly-Typed物件

    動作方法可以傳回匿名物件,並將其序列化為 JSON。 例如:

    public object Get()
        return new { 
            Name = "Alice", 
            Age = 23, 
            Pets = new List<string> { "Fido", "Polly", "Spot" } 
    

    回應訊息本文將包含下列 JSON:

    {"Name":"Alice","Age":23,"Pets":["Fido","Polly","Spot"]}
    

    如果您的 Web API 收到來自用戶端的鬆散結構化 JSON 物件,您可以將要求本文還原序列化為 Newtonsoft.Json.Linq.JObject 類型。

    public void Post(JObject person)
        string name = person["Name"].ToString();
        int age = person["Age"].ToObject<int>();
    

    不過,使用強型別資料物件通常比較好。 然後,您不需要自行剖析資料,而且您會獲得模型驗證的優點。

    XML 序列化程式不支援匿名型別或 JObject 實例。 如果您針對 JSON 資料使用這些功能,您應該從管線中移除 XML 格式器,如本文稍後所述。

    XML Media-Type Formatter

    XML 格式是由 XmlMediaTypeFormatter 類別所提供。 根據預設, XmlMediaTypeFormatter 會使用 DataContractSerializer 類別來執行序列化。

    如果您想要的話,您可以將 XmlMediaTypeFormatter 設定為使用 XmlSerializer ,而不是 DataContractSerializer。 若要這樣做,請將 UseXmlSerializer 屬性設定為 true

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    xml.UseXmlSerializer = true;
    

    XmlSerializer類別支援比DataContractSerializer更窄的類型集,但能更充分掌控產生的 XML。 如果您需要符合現有的 XML 架構,請考慮使用 XmlSerializer

    XML 序列化

    本節說明使用預設 DataContractSerializer之 XML 格式子的某些特定行為。

    根據預設,DataContractSerializer 的行為如下:

  • 所有公用讀取/寫入屬性和欄位都會序列化。 若要省略屬性或欄位,請使用 IgnoreDataMember 屬性裝飾它。
  • 私人和受保護的成員不會序列化。
  • 唯讀屬性不會序列化。 不過, (唯讀集合屬性的內容會序列化。)
  • 類別和成員名稱是以 XML 撰寫,與類別宣告中顯示的完全相同。
  • 使用預設 XML 命名空間。
  • 如果您需要更充分掌控序列化,您可以使用 DataContract 屬性裝飾 類別。 當此屬性存在時,類別會序列化,如下所示:

  • 「加入宣告」方法:屬性和欄位預設不會序列化。 若要序列化屬性或欄位,請使用 DataMember 屬性裝飾 它。
  • 若要序列化私用或受保護的成員,請使用 DataMember 屬性裝飾 它。
  • 唯讀屬性不會序列化。
  • 若要變更類別名稱在 XML 中的顯示方式,請在DataContract屬性中設定Name參數。
  • 若要變更成員名稱在 XML 中的顯示方式,請在DataMember屬性中設定Name參數。
  • 若要變更 XML 命名空間,請在DataContract類別中設定Namespace參數。
  • Read-Only屬性

    唯讀屬性不會序列化。 如果唯讀屬性具有備份私人欄位,您可以使用 DataMember 屬性標記私人欄位。 此方法需要 類別上的 DataContract 屬性。

    [DataContract]
    public class Product
        [DataMember]
        private int pcode;  // serialized
        // Not serialized (read-only)
        public int ProductCode { get { return pcode; } }
    

    日期是以 ISO 8601 格式撰寫。 例如,「2012-05-23T20:21:37.9116538Z」。

    若要寫入縮排 XML,請將 Indent 屬性設定為 true

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    xml.Indent = true;
    

    設定 XML 序列化程式Per-Type

    您可以為不同的 CLR 類型設定不同的 XML 序列化程式。 例如,您可能有需要 XmlSerializer 的特定資料物件,以取得回溯相容性。 您可以針對這個物件使用 XmlSerializer ,並繼續針對其他類型使用 DataContractSerializer

    若要針對特定類型設定 XML 序列化程式,請呼叫 SetSerializer

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    // Use XmlSerializer for instances of type "Product":
    xml.SetSerializer<Product>(new XmlSerializer(typeof(Product)));
    

    您可以指定 XmlSerializer 或任何衍生自 XmlObjectSerializer的物件。

    移除 JSON 或 XML 格式器

    如果您不想使用 JSON 格式器或 XML 格式器,則可以從格式子清單中移除這些格式子。 執行這項作業的主要原因包括:

  • 將 Web API 回應限制為特定媒體類型。 例如,您可能會決定只支援 JSON 回應,並移除 XML 格式子。
  • 若要以自訂格式器取代預設格式器。 例如,您可以使用自己的 JSON 格式器自訂實作 JSON 格式器來取代 JSON 格式器。
  • 下列程式碼示範如何移除預設格式子。 從 Global.asax 中定義的 Application_Start 方法呼叫此方法。

    void ConfigureApi(HttpConfiguration config)
        // Remove the JSON formatter
        config.Formatters.Remove(config.Formatters.JsonFormatter);
        // or
        // Remove the XML formatter
        config.Formatters.Remove(config.Formatters.XmlFormatter);
    

    處理迴圈物件參考

    根據預設,JSON 和 XML 格式器會將所有物件寫入為值。 如果兩個屬性參考相同的物件,或相同物件在集合中出現兩次,則格式器會序列化物件兩次。 如果您的物件圖形包含迴圈,這是特定的問題,因為序列化程式會在偵測到圖形中的迴圈時擲回例外狀況。

    請考慮下列物件模型和控制器。

    public class Employee
        public string Name { get; set; }
        public Department Department { get; set; }
    public class Department
        public string Name { get; set; }
        public Employee Manager { get; set; }
    public class DepartmentsController : ApiController
        public Department Get(int id)
            Department sales = new Department() { Name = "Sales" };
            Employee alice = new Employee() { Name = "Alice", Department = sales };
            sales.Manager = alice;
            return sales;
    

    叫用此動作會導致格式器擲回例外狀況,這會轉譯為狀態碼 500 (內部伺服器錯誤) 回應用戶端。

    若要在 JSON 中保留物件參考,請將下列程式碼新增至 Global.asax 檔案中的 Application_Start 方法:

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.PreserveReferencesHandling = 
        Newtonsoft.Json.PreserveReferencesHandling.All;
    

    現在控制器動作會傳回如下所示的 JSON:

    {"$id":"1","Name":"Sales","Manager":{"$id":"2","Name":"Alice","Department":{"$ref":"1"}}}
    

    請注意,序列化程式會將 「$id」 屬性新增至這兩個物件。 此外,它會偵測 Employee.Department 屬性建立迴圈,因此它會以物件參考取代值:{「$ref」:「1」}。

    物件參考不是 JSON 中的標準。 使用這項功能之前,請考慮您的用戶端是否能夠剖析結果。 最好是從圖形中移除迴圈。 例如,在此範例中,不需要從 Employee 回到 Department 的連結。

    若要在 XML 中保留物件參考,您有兩個選項。 更簡單的選項是將 新增 [DataContract(IsReference=true)] 至您的模型類別。 IsReference參數會啟用物件參考。 請記住, DataContract 會加入宣告序列化,因此您也需要將 DataMember 屬性新增至屬性:

    [DataContract(IsReference=true)]
    public class Department
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public Employee Manager { get; set; }
    

    現在,格式器會產生類似下列的 XML:

    <Department xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="i1" 
                xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" 
                xmlns="http://schemas.datacontract.org/2004/07/Models">
      <Manager>
        <Department z:Ref="i1" />
        <Name>Alice</Name>
      </Manager>
      <Name>Sales</Name>
    </Department>
    

    如果您想要避免模型類別上的屬性,有另一個選項:在建構函式中建立新的類型特定 DataContractSerializer 實例,並將 preserveObjectReferences 設定為 true 。 然後將這個實例設定為 XML 媒體類型格式子上的每一類型序列化程式。 下列程式碼示範如何執行這項操作:

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    var dcs = new DataContractSerializer(typeof(Department), null, int.MaxValue, 
        false, /* preserveObjectReferences: */ true, null);
    xml.SetSerializer<Department>(dcs);
    

    測試物件序列化

    當您設計 Web API 時,測試資料物件的序列化方式很有用。 您可以這麼做,而不需建立控制器或叫用控制器動作。

    string Serialize<T>(MediaTypeFormatter formatter, T value)
        // Create a dummy HTTP Content.
        Stream stream = new MemoryStream();
        var content = new StreamContent(stream);
        /// Serialize the object.
        formatter.WriteToStreamAsync(typeof(T), value, stream, content, null).Wait();
        // Read the serialized string.
        stream.Position = 0;
        return content.ReadAsStringAsync().Result;
    T Deserialize<T>(MediaTypeFormatter formatter, string str) where T : class
        // Write the serialized string to a memory stream.
        Stream stream = new MemoryStream();
        StreamWriter writer = new StreamWriter(stream);
        writer.Write(str);
        writer.Flush();
        stream.Position = 0;
        // Deserialize to an object of type T
        return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
    // Example of use
    void TestSerialization()
        var value = new Person() { Name = "Alice", Age = 23 };
        var xml = new XmlMediaTypeFormatter();
        string str = Serialize(xml, value);
        var json = new JsonMediaTypeFormatter();
        str = Serialize(json, value);
        // Round trip
        Person person2 = Deserialize<Person>(json, str);
    
  •