谨以此文感谢关注此系列文章的园友!前段时间本觉得此系列文章已没多少人关注,而不打算继续下去了。由于文章贴出来以后,看的人彷佛很少,也不多有人对这些文章发表评论,并且几乎无人给予“推荐”。但前几天有人询问为什么好久没有更新,这让我感受把这文章翻译出来仍是有价值的。为此,本人打算将此工做继续下去。这些关于Web API的技术文章均由微软专业人员撰写,虽然文章做为博客帖子而写得比较简单,但若是仔细揣摩其内容,应当仍是可以有很多收获的,也但愿个人这些译文可以使那些对Web API有兴趣的园友从中获得一些收获。web
尽管最近正忙于《Pro ASP.NET MVC 4》一书的翻译,但我仍会将此系列文章继续下去,只不过可能不会仔细检查,译文必定会有比较多的错误,恳请读者谅解。json
本文引自:http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serializationapi
By Mike Wasson|May 30, 2012
做者:Mike Wasson|2012-3-30服务器
This article describes the JSON and XML formatters in ASP.NET Web API.
本文描述ASP.NET Web API中的JSON和XML格式化器。app
In ASP.NET Web API, a media-type formatter is an object that can:
在ASP.NET Web API中,媒体类型格式化器(Media-type Formatter)是一种可以作如下工做的对象:框架
Web API provides media-type formatters for both JSON and XML. The framework inserts these formatters into the pipeline by default. Clients can request either JSON or XML in the Accept header of the HTTP request.
Web API提供了用于JSON和XML的媒体类型格式化器。框架已默认将这些格式化器插入到消息处理管线之中。客户端在HTTP请求的Accept报头中能够请求JSON或XML。asp.net
Contents
本小节内容less
JSON formatting is provided by the JsonMediaTypeFormatter class. By default, JsonMediaTypeFormatter uses the Json.NET library to perform serialization. Json.NET is a third-party open source project.
JSON格式化是由JsonMediaTypeFormatter类提供的。默认状况下,JsonMediaTypeFormatter使用Json.NET库执行序列化工做。Json.NET是一个第三方开源项目。ide
If you prefer, you can configure the JsonMediaTypeFormatter class to use the DataContractJsonSerializer instead of Json.NET. To do so, set the UseDataContractJsonSerializer property to true:
若是喜欢,你能够将JsonMediaTypeFormatter配置成使用DataContractJsonSerializer来代替Json.NET。要想这么作,只需UseDataContractJsonSerializer将属性设置为true便可:oop
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.UseDataContractJsonSerializer = true;
This section describes some specific behaviors of the JSON formatter, using the default Json.NET serializer. This is not meant to be comprehensive documentation of the Json.NET library; for more information, see the Json.NET Documentation.
本小节描述,在使用默认的Json.NET序列化器时,JSON格式化器的一些特定行为。这并不意味着要包含Json.NET库的整个文档。更多信息参阅Json.NET Documentation。
By default, all public properties and fields are included in the serialized JSON. To omit a property or field, decorate it with the JsonIgnore attribute.
默认状况下,全部public属性和字段都会被包含在序列化的JSON中。为了忽略一个属性或字段,须要用JsonIgnore注解属性修饰它。
public class Product { public string Name { get; set; } public decimal Price { get; set; }
[JsonIgnore] public int ProductCode { get; set; } // omitted }
If you prefer an "opt-in" approach, decorate the class with the DataContract attribute. If this attribute is present, members are ignored unless they have the DataMember. You can also use DataMember to serialize private members.
若是你更喜欢“opt-in(选入)”方法,能够用DataContract注解属性来修饰类。若是有注解属性,则成员均被忽略,除非有DataMember。DataMember也能够序列化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 }
Read-only properties are serialized by default.
只读属性默认是序列化的。
By default, Json.NET writes dates in ISO 8601 format. Dates in UTC (Coordinated Universal Time) are written with a "Z" suffix. Dates in local time include a time-zone offset. For example:
默认状况下,Json.NET会将日期写成ISO 8601格式。UTC(Coordinated Universal Time — 世界标准时间)格式的日期书写时带有后缀“Z”。本地时间格式的日期包括了一个时区偏移量。例如:
2012-07-27T18:51:45.53403Z // UTC(标准时间) 2012-07-27T11:51:45.53403-07:00 // Local(本地时间)
By default, Json.NET preserves the time zone. You can override this by setting the DateTimeZoneHandling property:
默认状况下,Json.NET保留时区。经过设置DateTimeZoneHandling属性,能够重写这一行为:
// Convert all dates to UTC // 将全部日期转换成UTC格式 var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
If you prefer to use Microsoft JSON date format ("\/Date(ticks)\/") instead of ISO 8601, set the DateFormatHandling property on the serializer settings:
若是你喜欢使用微软的JSON日期格式("\/Date(ticks)\/ ")而不是ISO 8601,能够在SerializerSettings上设置DateFormatHandling属性:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;
To write indented JSON, set the Formatting setting to Formatting.Indented:
为了书写有缩进的JSON,能够将Formatting设置为Formatting.Indented:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
To write JSON property names with camel casing, without changing your data model, set the CamelCasePropertyNamesContractResolver on the serializer:
为了在不修改数据模型的状况下,用驼峰式大小写转换JSON的属性名,能够设置序列化器上的CamelCasePropertyNamesContractResolver:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
An action method can return an anonymous object and serialize it to JSON. For example:
动做方法或以返回一个匿名对象,并将其序列化成JSON。例如:
public object Get() { return new { Name = "Alice", Age = 23, Pets = new List<string> { "Fido", "Polly", "Spot" } }; }
The response message body will contain the following JSON:
响应消息体将含有如下JSON:
{"Name":"Alice","Age":23,"Pets":["Fido","Polly","Spot"]}
If your web API receives loosely structured JSON objects from clients, you can deserialize the request body to a Newtonsoft.Json.Linq.JObject type.
若是Web API从客户端接收了松散结构的JSON,你能够将该请求体解序列化成Newtonsoft.Json.Linq.JObject类型。
public void Post(JObject person) { string name = person["Name"].ToString(); int age = person["Age"].ToObject<int>(); }
However, it is usually better to use strongly typed data objects. Then you don't need to parse the data yourself, and you get the benefits of model validation.
然而,一般更好的是使用强类型数据对象。那么,便不须要自行对数据进行解析,而且能获得模型验证的好处。
The XML serializer does not support anonymous types or JObject instances. If you use these features for your JSON data, you should remove the XML formatter from the pipeline, as described later in this article.
XML序列化器不支持匿名类型或JObject实例。若是将这些特性用于JSON数据,应该去掉管线中的XML格式化器,如本文稍后描述的那样。
XML formatting is provided by the XmlMediaTypeFormatter class. By default, XmlMediaTypeFormatter uses the DataContractSerializer class to perform serialization.
XML格式化是由XmlMediaTypeFormatter类提供的。默认状况下,XmlMediaTypeFormatter使用DataContractSerializer类来执行序列化。
If you prefer, you can configure the XmlMediaTypeFormatter to use the XmlSerializer instead of the DataContractSerializer. To do so, set the UseXmlSerializer property to true:
若是喜欢,你能够将XmlMediaTypeFormatter配置成使用XmlSerializer而不是DataContractSerializer。要想这么作,可将UseXmlSerializer属性设置为true:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.UseXmlSerializer = true;
The XmlSerializer class supports a narrower set of types than DataContractSerializer, but gives more control over the resulting XML. Consider using XmlSerializer if you need to match an existing XML schema.
XmlSerializer类支持的类型集要比DataContractSerializer更窄一些,但对结果XML有更多的控制。若是须要与已有的XML方案匹配,可考虑使用XmlSerializer。
This section describes some specific behaviors of the XML formatter, using the default DataContractSerializer.
本小节描述使用默认DataContractSerializer的时,XML格式化器的一些特殊行为。
By default, the DataContractSerializer behaves as follows:
默认状况下,DataContractSerializer行为以下:
If you need more control over the serialization, you can decorate the class with the DataContract attribute. When this attribute is present, the class is serialized as follows:
若是须要在序列化上的更多控制,能够用DataContract注解属性修饰类。当这个注解属性出现时,该类按以策略序列化:
Read-only properties are not serialized. If a read-only property has a backing private field, you can mark the private field with the DataMember attribute. This approach requires the DataContract attribute on the class.
只读属性是不被序列化的。若是只读属性有一个支撑private字段,能够用DataMember注解属性对这个private字段进行标记。这种办法须要在类上使用DataContract注解属性。
[DataContract] public class Product { [DataMember] private int pcode; // serialized(序列化的)
// Not serialized (read-only) // 不做序列化(只读) public int ProductCode { get { return pcode; } } }
Dates are written in ISO 8601 format. For example, "2012-05-23T20:21:37.9116538Z".
日期被写成ISO 8601格式。例如,“2012-05-23T20:21:37.9116538Z”。
To write indented XML, set the Indent property to true:
要书写缩进的XML,请将Indent属性设置为true:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.Indent = true;
You can set different XML serializers for different CLR types. For example, you might have a particular data object that requires XmlSerializer for backward compatibility. You can use XmlSerializer for this object and continue to use DataContractSerializer for other types.
你能够为不一样的CLR类型设置不一样的XML序列化器。例如,你可能有一个特殊的数据对象,它出于向后兼容而须要XmlSerializer。你能够为此对象使用XmlSerializer,而对其它类型继续使用DataContractSerializer。
To set an XML serializer for a particular type, call SetSerializer.
为了设置用于特殊类型的XML序列化器,要调用SetSerializer。
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; // Use XmlSerializer for instances of type "Product": // 对“Product”类型的实例使用XmlSerializer: xml.SetSerializer<Product>(new XmlSerializer(typeof(Product)));
You can specify an XmlSerializer or any object that derives from XmlObjectSerializer.
你能够指定一个XmlSerializer,或任何派生于XmlObjectSerializer的对象。
You can remove the JSON formatter or the XML formatter from the list of formatters, if you do not want to use them. The main reasons to do this are:
你能够从格式化器列表中删除JSON格式化器,或XML格式化器,只要你不想使用它们。这么作的主要缘由是:
The following code shows how to remove the default formatters. Call this from your Application_Start method, defined in Global.asax.
如下代码演示了如何删除默认的格式化器。在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); }
By default, the JSON and XML formatters write all objects as values. If two properties refer to the same object, or if the same object appears twice in a collection, the formatter will serialize the object twice. This is a particular problem if your object graph contains cycles, because the serializer will throw an exception when it detects a loop in the graph.
在默认状况下,JSON和XML格式化器将全部对象都写成值。若是两个属性引用了同一个对象,或者,若是在一个集合同一个对象出现了两次,格式化器将对此对象作两次序列化。这是在对象图含有循环的状况下会出现的特有问题,由于,序列化器在检测到对象图中的循环时,会抛出异常(故格式化器会预先经过两个序列化,来消除这种循环对象引用 — 译者注)。
Consider the following object models and controller.
考虑如下对象模型和控制器。
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; } }
Invoking this action will cause the formatter to thrown an exception, which translates to a status code 500 (Internal Server Error) response to the client.
调用此动做会触发格式化器招聘异常,该异常将转换成发送给客户端的状态代码500(内部服务器错误)响应。
To preserve object references in JSON, add the following code to Application_Start method in the Global.asax file:
为了保留JSON中的对象引用,对Global.asax文件的Application_Start方法添加如下代码:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;
Now the controller action will return JSON that looks like this:
如今,此控制器动做将返回相似于以下形式的JSON:
{"$id":"1","Name":"Sales","Manager":{"$id":"2","Name":"Alice","Department":{"$ref":"1"}}}
Notice that the serializer adds an "$id" property to both objects. Also, it detects that the Employee.Department property creates a loop, so it replaces the value with an object reference: {"$ref":"1"}.
注意,序列化器对两个对象都添加了一个“$id”。并且,它检测到Employee.Department属性产生了一个循环,所以,它用一个对象引用{"$ref":"1"}代替这个值。
Object references are not standard in JSON. Before using this feature, consider whether your clients will be able to parse the results. It might be better simply to remove cycles from the graph. For example, the link from Employee back to Department is not really needed in this example.
对象引用是不标准的JSON。在使用此特性以前,要考虑你的客户端是否可以解析这种结果。简单地去除对象图中的循环,多是更好的办法。例如,此例中Employee连接回Department并非真正的须要。
To preserve object references in XML, you have two options. The simpler option is to add [DataContract(IsReference=true)] to your model class. The IsReference parameter enables object references. Remember that DataContract makes serialization opt-in, so you will also need to add DataMember attributes to the properties:
为了保留XML中的对象引用,可使用两个选项。较简单的选项是对模型类添加[DataContract(IsReference=true)]。IsReference参数启用了对象引用。记住,DataContract构成了序列化的“选入(Opt-in)”,所以,你还须要对属性添加DataMember注解属性(使用了这一注解属性的模型属性,才被选入(Opt-in)为序列化对象 — 译者注):
[DataContract(IsReference=true)] public class Department { [DataMember] public string Name { get; set; }
[DataMember] public Employee Manager { get; set; } }
Now the formatter will produce XML similar to following:
如今,该格式化器将产生相似于以下形式的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>
If you want to avoid attributes on your model class, there is another option: Create a new type-specific DataContractSerializer instance and set preserveObjectReferences to true in the constructor. Then set this instance as a per-type serializer on the XML media-type formatter. The following code show how to do this:
若是想避免在模型类上使用注解属性,还有另外一个选项:建立亲的类型专用的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);
As you design your web API, it is useful to test how your data objects will be serialized. You can do this without creating a controller or invoking a controller action.
在设计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 = 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 // 解序列化成类型为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); }
看完此文若是以为有所收获,请给个推荐。 你的推荐是我继续下去的动力,也会让更多人关注并获益,这也是你的贡献。