在使用C#的XmlSerializer做对象到XML的序列化时,很多初学者会发现,即使实体类没有定义任何命名空间,最终生成的XML根节点上也会自动带上xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"和xmlns:xsd="http://www.w3.org/2001/XMLSchema"这两个属性。它们虽然符合标准,但在某些只认固定报文格式的系统中会被当作多余字段而拒绝接收。

一、为什么会自动生成xsi和xsd
XmlSerializer在设计上为了保证序列化结果的Schema自描述能力,会在没有显式指定命名空间集合时,默认向输出写入XML Schema实例和XML Schema定义这两个标准命名空间的引用。从底层看,序列化器在内部维护了一个XmlSerializerNamespaces对象,当它为null或为空时,运行时会自动补全上述两个常用URI。
这种机制对绝大多数场景无害,因为标准XML解析器会忽略未知前缀。但当你需要与其他语言、旧系统或者人工编写的报文做严格字节级比对时,这两个属性就成了噪音。理解这一点后,我们的目标就很明确:告诉序列化器使用我们自己的、空的命名空间集合,从而阻止默认值注入。
二、通过XmlSerializerNamespaces去除属性
核心做法是手动创建一个XmlSerializerNamespaces实例,添加一个前缀为空字符串、命名空间也为空字符串的映射,然后将其作为Serialize方法的参数传入。这样序列化器认为你已经提供了完整的命名空间定义,就不会再补充默认项。
下面是一段完整的控制台示例,演示如何序列化一个简单订单对象并去掉多余属性:
using System;
using System.IO;
using System.Xml.Serialization;
namespace Demo
{
// 简单实体类,未设置任何命名空间
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
Order order = new Order { Id = 1, Name = "测试订单" };
XmlSerializer serializer = new XmlSerializer(typeof(Order));
// 关键:创建空命名空间映射
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
StringWriter writer = new StringWriter();
// 传入ns,阻止默认xsi/xsd生成
serializer.Serialize(writer, order, ns);
string xml = writer.ToString();
Console.WriteLine(xml);
}
}
}
运行以上代码,输出的XML根节点将只包含<Order>而不带任何xmlns属性。如果去掉ns.Add("", "")和第三个参数,结果就会变回带有xmlns:xsi与xmlns:xsd的版本。
需要注意的是,空前缀映射必须同时保证前缀和命名空间都是空字符串。若只写ns.Add("a", "")或ns.Add("", "http://temp"),不仅无法去掉默认属性,还会引入你自定义的前缀或命名空间,导致报文结构变化。
三、使用XmlWriter时的注意事项
在部分项目中,我们会用XmlWriter配合XmlWriterSettings来控制缩进和编码。此时同样要把命名空间集合传给Serialize,并且确保Settings中没有强制设置命名空间相关的规则。
示例代码如下:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class User
{
public string Account { get; set; }
}
class Test
{
static void Run()
{
User u = new User { Account = "admin" };
XmlSerializer ser = new XmlSerializer(typeof(User));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = false;
XmlSerializerNamespaces emptyNs = new XmlSerializerNamespaces();
emptyNs.Add("", "");
using (XmlWriter xw = XmlWriter.Create(Console.Out, settings))
{
ser.Serialize(xw, u, emptyNs);
}
}
}
这段代码在控制台输出的XML既带声明又无多余命名空间,格式整洁。如果对接的接口要求不带XML声明,只需把OmitXmlDeclaration设为true即可,去除xsi的逻辑不受影响。
另外,若实体类上使用过XmlRoot特性并指定了Namespace,那么Add("", "")将无法覆盖该特性,根节点仍会输出对应命名空间。此时应把特性的Namespace改为空或调整映射策略。
四、反序列化是否受影响
很多开发者担心:去掉命名空间后,对方发来的报文或者我们自己保存的报文还能正常反序列化回来吗?答案是只要反序列化端的类结构一致,且没有在XmlSerializer构造时强制绑定命名空间,空命名空间报文可以被正常读取。
因为XmlSerializer在反序列化时,对于不带命名空间的元素默认按无命名空间匹配。只有当你的类通过特性绑定了具体命名空间,而报文却没有时才会失败。因此,在内部系统间传输并统一去属性,是安全且推荐的做法。
| 场景 | 是否推荐去xsi/xsd | 说明 |
|---|---|---|
| 第三方严格格式接口 | 推荐 | 避免对方校验失败 |
| 内部服务间通信 | 可选 | 标准解析器兼容,但整洁利于排查 |
| 需要Schema校验 | 不推荐 | xsi类型提示有助于校验器识别 |
五、常见误区总结
误区一是试图在实体类里写属性去“隐藏”命名空间,实际上XmlSerializer不会识别普通字符串字段来控制命名空间输出。误区二是用字符串替换的方式事后删掉xmlns,这容易破坏XML结构或误删正常业务属性。
正确思路始终是通过XmlSerializerNamespaces显式接管命名空间声明。掌握这一方式后,你不仅能去掉xsi和xsd,也可以灵活为不同节点分配前缀,满足各类报文规范。
XmlSerializerC#_序列化xmlns_xsi修改时间:2026-08-07 12:09:29