前言

阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html

本文描述ASP.NET Web API中的JSON和XML格式化器。

在ASP.NET Web API中,媒体类型格式化器(Media-type Formatter)是一种能够做以下工作的对象:

  • 从HTTP消息体读取CLR(公共语言运行时)对象
  • 将CLR对象写入HTTP消息体

Web API提供了用于JSON和XML的媒体类型格式化器。框架已默认将这些格式化器插入到消息处理管线之中。客户端在HTTP请求的Accept报头中可以请求JSON或XML。

JSON媒体类型格式化器

JSON格式化是由JsonMediaTypeFormatter类提供的。默认情况下,JsonMediaTypeFormatter使用Json.NET库执行序列化工作。Json.NET是一个第三方开源项目。

如果喜欢,你可以将JsonMediaTypeFormatter配置成使用DataContractJsonSerializer来代替Json.NET。要想这么做,只需UseDataContractJsonSerializer将属性设置为true即可:

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

JSON序列化

本小节描述,在使用默认的Json.NET序列化器时,JSON格式化器的一些特定行为。这并不意味着要包含Json.NET库的整个文档。更多信息参阅Json.NET Documentation。

什么会被序列化?

默认情况下,所有public属性和字段都会被包含在序列化的JSON中。为了忽略一个属性或字段,需要用JsonIgnore注解属性修饰它。

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }

    [JsonIgnore]
    public int ProductCode { get; set; } // omitted
}

如果你更喜欢“opt-in(选入)”方法,可以用DataContract注解属性来修饰类。如果有注解属性,则成员均被忽略,除非有DataMemberDataMember也可以序列化private成员。

[DataContract]
public class Product
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public decimal Price { get; set; }
    public int ProductCode { get; set; }  // omitted by default
}

只读属性

只读属性默认是序列化的。

Dates(日期)

默认情况下,Json.NET会将日期写成ISO 8601格式。UTC(Coordinated Universal Time — 世界标准时间)格式的日期书写时带有后缀“Z”。本地时间格式的日期包括了一个时区偏移量。例如:

--27T18::.53403Z         // UTC(标准时间)
--27T11:::    // Local(本地时间)

默认情况下,Json.NET保留时区。通过设置DateTimeZoneHandling属性,可以重写这一行为:

// Convert all dates to UTC
// 将所有日期转换成UTC格式
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.DateTimeZoneHandling =
     Newtonsoft.Json.DateTimeZoneHandling.Utc;

如果你喜欢使用微软的JSON日期格式("\/Date(ticks)\/ ")而不是ISO 8601,可以在SerializerSettings上设置DateFormatHandling属性:

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

Indenting(缩进)

为了书写有缩进的JSON,可以将Formatting设置为Formatting.Indented

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

Camel Casing(驼峰式大小写转换)

为了在不修改数据模型的情况下,用驼峰式大小写转换JSON的属性名,可以设置序列化器上的CamelCasePropertyNamesContractResolver

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

匿名类型与弱类型对象

动作方法或以返回一个匿名对象,并将其序列化成JSON。例如:

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

响应消息体将含有以下JSON:

{,"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媒体类型格式化器

  XML格式化是由XmlMediaTypeFormatter类提供的。默认情况下,XmlMediaTypeFormatter使用DataContractSerializer类来执行序列化。如果喜欢,你可以将XmlMediaTypeFormatter配置成使用XmlSerializer而不是DataContractSerializer。要想这么做,可将UseXmlSerializer属性设置为true: 

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

XmlSerializer类支持的类型集要比DataContractSerializer更窄一些,但对结果XML有更多的控制。如果需要与已有的XML方案匹配,可考虑使用XmlSerializer

XML Serialization——XML序列化

本小节描述使用默认DataContractSerializer的时,XML格式化器的一些特殊行为。默认情况下,DataContractSerializer行为如下:

  •   序列化所有public读/写属性和字段。为了忽略一个属性或字段,请用IgnoreDataMember注解属性修饰它。
  •   private和protected成员不作序列。
  •   只读属性不作序列化
  •   类名和成员名按类声明中的确切呈现写入XML
  •   使用XML的默认命名空间

如果需要在序列化上的更多控制,可以用DataContract注解属性修饰类。当这个注解属性出现时,该类按以策略序列化:

  •   “Opt in(选入)”方法:属性与字段默认不被序列化。为了序列化一个属性或字段,请用DataMember注解属性修饰它。
  •   要序列化private或protected成员,请用DataMember注解属性修饰它。
  •   只读属性不被序列化。
  •   要改变类名在XML中的呈现,请在DataContract注解属性中设置Name参数。
  •   要改变成员名在XML中的呈现,请设置DataMember注解属性中的Nmae参数。
  •   要改变XML命名空间,请设置DataContract类中的Namespace参数。

Read-Only Properties——只读属性

只读属性是不被序列化的。如果只读属性有一个支撑private字段,可以用DataMember注解属性对这个private字段进行标记。这种办法需要在类上使用DataContract注解属性。

[DataContract]
public class Product
{
    [DataMember]
    private int pcode;  // serialized(序列化的)

    // Not serialized (read-only)
    // 不作序列化(只读)
    public int ProductCode { get { return pcode; } }
}

Dates——日期

日期被写成ISO 8601格式。例如,“2012-05-23T20:21:37.9116538Z”。

Indenting——缩进

要书写缩进的XML,请将Indent属性设置为true

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

设置每一类型(Per-Type)的XML序列化器

你可以为不同的CLR类型设置不同的XML序列化器。例如,你可能有一个特殊的数据对象,它出于向后兼容而需要XmlSerializer。你可以为此对象使用XmlSerializer,而对其它类型继续使用DataContractSerializer

为了设置用于特殊类型的XML序列化器,要调用SetSerializer

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
// Use XmlSerializer for instances of type "Product":
// 对“Product”类型的实例使用XmlSerializer:
xml.SetSerializer<Product>(new XmlSerializer(typeof(Product)));

你可以指定一个XmlSerializer,或任何派生于XmlObjectSerializer的对象。

Removing the JSON or XML Formatter——去除JSON或XML格式化器

你可以从格式化器列表中删除JSON格式化器,或XML格式化器,只要你不想使用它们。这么做的主要原因是:

  •   将你的Web API响应限制到特定的媒体类型。例如,你可能决定只支持JSON响应,而删除XML格式化器。
  •   用一个自定义格式化器代替默认的格式化器。例如,你可能要用自己的自定义JSON格式化器实现来代替(默认的)JSON格式化器。

以下代码演示了如何删除默认的格式化器。在Global.asax中定义的Application_Start方法中调用它。

void ConfigureApi(HttpConfiguration config)
{
    // Remove the JSON formatter
    // 删除JSON格式化器
    config.Formatters.Remove(config.Formatters.JsonFormatter);

    // or(或者)

    // Remove the XML formatter
    // 删除XML格式化器
    config.Formatters.Remove(config.Formatters.XmlFormatter);
}

Handling Circular Object References——处理循环对象引用

在默认情况下,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”。而且,它检测到Employee.Department属性产生了一个循环,因此,它用一个对象引用{"$ref":"1"}代替这个值。

对象引用是不标准的JSON。在使用此特性之前,要考虑你的客户端是否能够解析这种结果。简单地去除对象图中的循环,可能是更好的办法。例如,此例中Employee链接回Department并不是真正的需要。

为了保留XML中的对象引用,可以使用两个选项。较简单的选项是对模型类添加[DataContract(IsReference=true)]。IsReference参数启用了对象引用。记住,DataContract构成了序列化的“选入(Opt-in)”,因此,你还需要对属性添加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

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

Testing Object Serialization——测试对象序列化

在设计Web API时,对如何序列化对象进行测试是有用的。不必创建控制器或调用控制器动作,便可做这种事。

string Serialize<T>(MediaTypeFormatter formatter, T value)
{
    // Create a dummy HTTP Content.
    // 创建一个HTTP内容的哑元
    Stream stream = new MemoryStream();
    var content = new StreamContent(stream);

    // Serialize the object.
    // 序列化对象
    formatter.WriteToStreamAsync(typeof(T), value, stream, content.Headers, null).Wait();

    // Read the serialized string.
    // 读取序列化的字符串
    stream.Position = ;
    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 = ;

    // Deserialize to an object of type T
    // 解序列化成类型为T的对象
    return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
}

// Example of use
// 使用示例(用例)
void TestSerialization()
{
     };

    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);
}

总结

本课主要简单的了解一下JSON和XML的序列化和反序列的使用。

本文的参考链接为 http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

同时本文已更新至 Web API导航系列 http://www.cnblogs.com/aehyok/p/3446289.html

Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化的更多相关文章

  1. ASP.NET Web API中的JSON和XML序列化

    ASP.NET Web API中的JSON和XML序列化 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok ...

  2. 【ASP.NET Web API教程】6.2 ASP.NET Web API中的JSON和XML序列化

    谨以此文感谢关注此系列文章的园友!前段时间本以为此系列文章已没多少人关注,而不打算继续下去了.因为文章贴出来之后,看的人似乎不多,也很少有人对这些文章发表评论,而且几乎无人给予“推荐”.但前几天有人询 ...

  3. JAVA学习第三十三课(经常使用对象API) —迭代器

    数字有非常多用数组存,对象有非常多就要用集合存 可是数组是固定长度的,集合是可变长度的 集合的由来: 对象用来封装特有数据,对象多了须要存储,假设对象个数不确定,就须要使用集合容器来存储 集合的特点: ...

  4. Kali Linux Web 渗透测试视频教程— 第十三课-密码破解

    Kali Linux Web 渗透测试— 第十三课-密码破解 文/玄魂 目录 Kali Linux Web 渗透测试— 第十三课-密码破解............................... ...

  5. 【ASP.NET MVC 学习笔记】- 19 REST和RESTful Web API

    本文参考:http://www.cnblogs.com/willick/p/3441432.html 1.目前使用Web服务的三种主流的方式是:远程过程调用(RPC),面向服务架构(SOA)以及表征性 ...

  6. Asp.Net Web API 2第十一课——在Web API中使用Dependency Resolver

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文主要来介绍在Asp.N ...

  7. Asp.Net Web API 2(入门)第一课

    Asp.Net Web API 2(入门)第一课   前言 Http不仅仅服务于Web Pages.它也是一个创建展示服务和数据的API的强大平台.Http是简单的.灵活的.无处不在的.你能想象到几乎 ...

  8. Asp.Net Web API 2第九课——自承载Web API

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html Asp.Net Web A ...

  9. Asp.Net Web API 2第八课——Web API 2中的属性路由

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 路由就是Web API如何 ...

随机推荐

  1. 《Hive编程指南》—— 读后总结

    知识图谱

  2. golang: 根据json生成go源文件

    https://github.com/ChimeraCoder/gojson $ git clone https://github.com/ChimeraCoder/gojson.git$ cd go ...

  3. two sample ttest &amp; paired ttst

    来源:http://www.pinzhi.org/thread-1023-1-1.html 成对t检验Paired Test是对来自同一总体的样本,在不同条件影响下获取的2组样本进行分析,以评价不同条 ...

  4. 在eclipse中使用jax-ws构建webservices服务端和客户端

    服务端: package com.yinfu.service; import javax.jws.WebService; import javax.xml.ws.Endpoint; @WebServi ...

  5. Java每日一则-001

    Java中类名与文件名的关系 1.Java保存的文件名必须与类名一致: 2.如果文件中只有一个类,文件名必须与类名一致: 3.一个Java文件中只能有一个public类: 4.如果文件中不止一个类,文 ...

  6. JSON 数据使用

    当用不同的数据的模板需要更换时.假设数据点的量.使用json非常方便. json物: var JSONObject= { "name":"Bill Gates" ...

  7. TcpListener 示例

    using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; class ...

  8. 用命令直接在两台ubuntu之间传输数据

    首先查看openssh-server是否启动: ps -e | grep ssh 如果没有任何提示则是没有启动: sudo /etc/init.d/ssh -start 启动进程.若提示找不到命令则需 ...

  9. Java BigDecimal详解,提供了丰富的四舍五入规则

    java.math.BigDecimal类提供用于算术,刻度操作,舍入,比较,哈希算法和格式转换操作. toString()方法提供BigDecimal的规范表示.它使用户可以完全控制舍入行为. 提供 ...

  10. NLP 第10章 基于深度学习的NLP 算法