C#中如何用DataContractJsonSerializer处理XML数据?

来源:集群教程作者:木下头衔:网络博主
导读:本期聚焦于木下创作的《C#中如何用DataContractJsonSerializer处理XML数据?》,敬请观看详情。DataContractJsonSerializer 明明是为 JSON 设计,为何会与 XML 扯上关系?实际开发中字段可能保存 XML 片段、配置文本或第三方接口返回的 XML 内容,既要保持结构可读,又需要输出 JSON。本文先厘清 DataContractJsonSerializer 与 XML 序列化工具 DataContractSerializer 的职责差异,再演示在 C# 中声明数据契约、处理 XML 字符串和 XElement 属性的方法,并结合 MemoryStream 与编码设置给出可运行示例。同时说明常见的类型不支持与转义问题,帮助读者避免把 JSON 序列化器当成 XML 解析器使用。文章重点是字符串承载 XML、反序列化后使用 XElement.Parse 解析,以及 XML 属性值在 JSON 中的转义表现,这些都直接影响接口输出和日志可读性。

在 C# 的序列化工具里,DataContractJsonSerializer 和 XML 通常不会出现在同一个讨论里。前者位于 System.Runtime.Serialization.Json 命名空间,负责把数据契约对象写成 JSON 字符串;后者更多是数据格式或数据来源。但当对象里包含 XML 字符串、XML 配置片段或第三方返回的报文时,就需要在 JSON 序列化过程中妥善处理这些 XML 内容。本文会围绕 DataContractJsonSerializer 的边界展开,并给出可运行的转换方案。

C#中如何用DataContractJsonSerializer处理XML数据?

先厘清:DataContractJsonSerializer 与 XML 序列化不是同一件事

很多刚接触 C# 序列化的开发者会把 DataContractJsonSerializer 和 DataContractSerializer 放在一起理解,因为它们都依赖 [DataContract][DataMember] 特性。实际上两者的输出格式完全不同:DataContractSerializer 生成 XML 标记,DataContractJsonSerializer 生成 JSON 文本。DataContractJsonSerializer 并不具备解析 XML 输入的能力,它只能把对象中的属性值按照 JSON 规则写出来。如果传入一个 XML 文档流,它不会像 XML 解析器那样去识别节点、属性或命名空间。

这意味着当业务数据中混入 XML 时,不能期望直接调用 DataContractJsonSerializer 就把 XML 转成 JSON 结构。正确思路是把 XML 当作普通字符串或先解析成 CLR 对象,再交给 JSON 序列化器。比如一个数据契约类可以包含字符串类型的属性,用来保存完整的 XML 片段。这样序列化时 XML 只是 JSON 对象中的字符串值,反序列化后再用 XElement.ParseXmlDocument 继续解析。

下面代码展示了同一个数据契约对象分别被 DataContractSerializer 和 DataContractJsonSerializer 处理时的输出差异。这样能更直观地理解两者职责。

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

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

    [DataMember]
    public int Age { get; set; }
}

public class Program
{
    public static void Main()
    {
        var person = new Person { Name = "Tom", Age = 30 };

        var xmlSerializer = new DataContractSerializer(typeof(Person));
        using (var ms = new MemoryStream())
        {
            xmlSerializer.WriteObject(ms, person);
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
        }

        var jsonSerializer = new DataContractJsonSerializer(typeof(Person));
        using (var ms = new MemoryStream())
        {
            jsonSerializer.WriteObject(ms, person);
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
        }
    }
}

运行后可以看到 XML 输出带有 <Person> 元素,而 JSON 输出是 {"Name":"Tom","Age":30}。这说明 DataContractJsonSerializer 的输入和输出都不包含 XML 节点结构,它只是对属性值做 JSON 编码。理解这一点之后,处理 XML 数据就变成如何把 XML 安全地放进属性值的问题。

使用字符串属性承载 XML 内容

在实际接口开发中,XML 内容通常以字符串形式出现在请求或响应报文里,例如支付回调报文、物流轨迹信息、电子发票数据等。最稳妥的做法是在数据契约类中使用 string 类型的属性来接收这些 XML 文本,然后正常使用 DataContractJsonSerializer 进行 JSON 序列化。这样无需额外配置,也能保证 XML 标签不会被当作 JSON 结构解析。

下面定义一个简单的信封类,其中 XmlContent 属性保存一段 XML 字符串。序列化时,JSON 会把这个字符串作为一个整体输出,并且自动处理尖括号、双引号等字符的转义。

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

[DataContract]
public class XmlEnvelope
{
    [DataMember(Name = "xmlContent")]
    public string XmlContent { get; set; }
}

public class JsonXmlDemo
{
    public static string SerializeXmlEnvelope(XmlEnvelope envelope)
    {
        var serializer = new DataContractJsonSerializer(typeof(XmlEnvelope));
        using (var stream = new MemoryStream())
        {
            serializer.WriteObject(stream, envelope);
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }

    public static void Main()
    {
        var envelope = new XmlEnvelope
        {
            XmlContent = "<note><to>Tom</to><body>Hello</body></note>"
        };

        string json = SerializeXmlEnvelope(envelope);
        Console.WriteLine(json);
    }
}

上述代码输出的 JSON 结构不会把 <note> 拆成对象属性,而是将其作为 xmlContent 字段的字符串值。浏览器或 JavaScript 端拿到这个 JSON 后,可以继续使用 DOMParser 或其他 XML 工具处理该字符串。这种方式的优点是实现简单、依赖少,非常适合 XML 不是主要数据结构、只作为某个字段出现的场景。

反序列化同样直接。把 JSON 字符串读取到 XmlEnvelope 对象后,再调用 XElement.Parse 解析 XML 字符串即可。需要注意的是,JSON 字符串本身在 C# 代码中必须正确转义,尤其是 XML 标签中的尖括号在代码里应写成 &lt;&gt;,否则运行时字符串不对。下面是一个完整的反序列化和 XML 解析示例。

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
using System.Xml.Linq;

public class JsonXmlReadDemo
{
    public static void Main()
    {
        string json = "{\"xmlContent\":\"<note><to>Tom</to><body>Hello</body></note>\"}";

        var serializer = new DataContractJsonSerializer(typeof(XmlEnvelope));
        using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
        {
            var result = (XmlEnvelope)serializer.ReadObject(stream);
            XElement note = XElement.Parse(result.XmlContent);
            Console.WriteLine(note.Element("to").Value);
            Console.WriteLine(note.Element("body").Value);
        }
    }
}

这段代码先通过 MemoryStream 把 JSON 字符串交给 DataContractJsonSerializer,反序列化得到 XmlEnvelope 实例,再用 LINQ to XML 的 XElement.Parse 读取 <to><body> 节点的值。这种先 JSON 反序列化、后 XML 解析的流程在实际项目中非常常见,也容易维护。

从 XML 文件或字符串转换到 JSON 的完整流程

如果数据源本身是一个 XML 文件,而最终要输出 JSON,不能直接把文件流塞给 DataContractJsonSerializer。正确做法是先读取 XML 文本,再根据业务需要提取内容填充到数据契约对象,最后调用 JSON 序列化器输出。这样可以把 XML 解析逻辑与 JSON 输出逻辑分离,代码结构更清晰。

假设本地有一个 XML 文件存放订单信息,内容大致如下,但为了保持示例完整,这里直接在代码中定义字符串。读取 XML 文件时可以使用 File.ReadAllText,路径中的反斜杠要保留,例如 C:\data\order.xml

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Xml.Linq;

[DataContract]
public class OrderInfo
{
    [DataMember(Name = "orderId")]
    public string OrderId { get; set; }

    [DataMember(Name = "customer")]
    public string Customer { get; set; }

    [DataMember(Name = "rawXml")]
    public string RawXml { get; set; }
}

public class XmlToJsonService
{
    public static string ConvertXmlFileToJson(string filePath)
    {
        string xmlText = File.ReadAllText(filePath);
        XElement root = XElement.Parse(xmlText);

        var order = new OrderInfo
        {
            OrderId = root.Element("orderId")?.Value,
            Customer = root.Element("customer")?.Value,
            RawXml = root.ToString()
        };

        var serializer = new DataContractJsonSerializer(typeof(OrderInfo));
        using (var stream = new MemoryStream())
        {
            serializer.WriteObject(stream, order);
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }
}

这段代码先读取 XML 文件内容,再使用 XElement 提取订单号和客户名,同时把完整 XML 作为原始字符串保存在 RawXml 字段中。这样输出的 JSON 既包含结构化字段,又保留了原始 XML 文本,便于后续审计或二次解析。注意 File.ReadAllText 默认使用 UTF-8 编码读取,如果 XML 文件是其他编码,需要显式传入编码参数。

这种方案的实用之处在于:DataContractJsonSerializer 只负责 JSON 序列化,XML 的解析工作完全交给 LINQ to XML 完成。两者各司其职,不会互相干扰。比如 XML 中存在命名空间时,可以在 XElement 解析时通过 XNamespace 处理,而 JSON 序列化器完全不需要关心这些 XML 细节。

容易忽略的转义、编码与类型问题

使用 DataContractJsonSerializer 处理包含 XML 的字符串时,最常遇到的问题就是转义表现。JSON 要求字符串中的双引号必须转义为 \",反斜杠自身也要转义为 \\。如果 XML 片段中有大量属性值使用双引号,例如 <item id="A001">,那么输出到 JSON 后这些双引号会变成 \",这在日志或配置文件中是正常现象,不必担心数据错误。反序列化时 JSON 解析器会自动还原。

另一个容易出错的地方是 MemoryStream 编码。DataContractJsonSerializer 在 WriteObject 时默认使用 UTF-8 编码写入,因此读取时也必须用 UTF-8 解码。如果写入完毕后直接使用 stream.Position = 0StreamReader 而不指定编码,在部分环境下可能因为检测编码而出现乱码。建议统一使用 Encoding.UTF8.GetString(stream.ToArray()),或者显式创建 StreamReader(stream, Encoding.UTF8)

还有一点需要明确:DataContractJsonSerializer 对 XElement 类型的直接支持并不稳定。虽然某些运行时版本可能把 XElement 属性序列化为字符串,但这种行为并不能作为可靠契约。如果数据契约类中声明 XElement 类型的属性,可能会遇到 InvalidDataContractException 或输出内容不如预期。因此更推荐在数据契约中使用 string 类型承载 XML,需要解析时再转换。这样既降低了序列化器的复杂度,也避免了不同 .NET 版本之间的行为差异。

当 XML 字符串非常大时,序列化后的 JSON 字符串会明显增大,因为所有换行、引号等都会按照 JSON 字符串规则保留或转义。此时如果接口响应体积敏感,可以考虑先对 XML 做压缩,或者只抽取关键字段输出,不一定要把完整 XML 原样放进 JSON。DataContractJsonSerializer 本身不提供压缩能力,但可以在写入 MemoryStream 之前先对字符串进行 GZip 压缩,再把压缩后的字节数组用 Base64 编码保存到 JSON 字段中。

总体来看,DataContractJsonSerializer 处理 XML 数据的核心思路并不是让它去解析 XML,而是让 XML 以字符串或提取后的对象形式进入 JSON 序列化流程。只要分清楚 XML 解析与 JSON 序列化两个阶段,就能避免大多数类型错误和转义混乱。配合 [DataContract][DataMember] 以及 XElement 解析,可以构建出清晰、稳定的数据转换逻辑。

C#DataContractJsonSerializerXML序列化修改时间:2026-09-19 11:54:36

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。