Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

简介: 原文:Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET Web API中的JSON和XML格式化器。
原文: Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

前言

阅读本文之前,您也可以到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”。本地时间格式的日期包括了一个时区偏移量。例如:

2012-07-27T18:51:45.53403Z         // UTC(标准时间)
2012-07-27T11:51:45.53403-07:00    // 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 = 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媒体类型格式化器

  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":"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构成了序列化的“选入(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 = 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);
}

总结

 本课主要简单的了解一下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

目录
相关文章
|
3月前
|
开发框架 前端开发 .NET
VB.NET中如何利用ASP.NET进行Web开发
在VB.NET中利用ASP.NET进行Web开发是一个常见的做法,特别是在需要构建动态、交互式Web应用程序时。ASP.NET是一个由微软开发的开源Web应用程序框架,它允许开发者使用多种编程语言(包括VB.NET)来创建Web应用程序。
72 5
|
4月前
|
XML 开发框架 .NET
ASP.NET Web Api 如何使用 Swagger 管理 API
ASP.NET Web Api 如何使用 Swagger 管理 API
134 1
|
4月前
|
开发框架 .NET 开发工具
【Azure 应用服务】App Service 的.NET Version选择为.NET6,是否可以同时支持运行ASP.NET V4.8的应用呢?
【Azure 应用服务】App Service 的.NET Version选择为.NET6,是否可以同时支持运行ASP.NET V4.8的应用呢?
|
4月前
|
API
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
|
4月前
|
存储 开发框架 .NET
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间
|
4月前
|
开发框架 .NET API
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
230 0
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
1月前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
|
1月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
2月前
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。