导读:本期聚焦于小伙伴创作的《C#中如何用XmlSerializer把字段序列化为XML属性而不是元素?》,敬请观看详情。在C#处理对象转XML时,默认XmlSerializer会把公共字段变成子元素,导致结构冗余且不符合接口规范。其实只需给属性或字段标记XmlAttribute特性,就能让其以属性形式输出。本文说明具体写法与注意事项,比如类名需用XmlRoot指定根节点,集合类型不能直接标特性,还要避免同名冲突。掌握后能让生成的XML更紧凑,便于和第三方系统对接,也更容易对照XSD契约做校验。

在C#开发里,把业务对象转换成XML是常见需求,尤其是对接旧系统或调用WebService时。XmlSerializer是官方提供的序列化工具,但很多人发现它默认把对象的公共属性写成一层层的子元素。如果对方接口要求把数据放在节点的属性上,就需要用XmlAttribute特性来控制输出结构。

C#中如何用XmlSerializer把字段序列化为XML属性而不是元素?

为什么默认会序列化成元素

XmlSerializer在设计上倾向于用元素来表达对象图,因为它能自然地表达嵌套关系。当你定义一个普通类,里面有几个自动属性,不做任何特性修饰,序列化后每个属性都会成为根节点下的子元素。这种方式可读性好,但会让XML体积变大,而且有些外部系统明确只认属性。

例如下面这段代码,没有任何特性,输出的就是纯元素结构:

using System;
using System.Xml.Serialization;
using System.IO;

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

class Program
{
    static void Main()
    {
        User u = new User { Name = "张三", Age = 28 };
        XmlSerializer ser = new XmlSerializer(typeof(User));
        using (StringWriter sw = new StringWriter())
        {
            ser.Serialize(sw, u);
            Console.WriteLine(sw.ToString());
        }
    }
}

上面代码运行后得到的XML类似<User><Name>张三</Name><Age>28</Age></User>,所有数据都在子元素里。如果接口要求写成<User Name="张三" Age="28" />,就必须改写法。

使用XmlAttribute特性的基本方法

要把某个字段变成XML属性,只需在属性上方加XmlAttribute特性。加上之后,Serializer就会把它当作当前节点的属性来写,而不是子节点。根类本身也可以用XmlRoot来指定根节点名字,避免和类名耦合。

修改后的类定义如下,注意引用了System.Xml.Serialization命名空间:

using System;
using System.Xml.Serialization;
using System.IO;

[XmlRoot("User")]
public class User
{
    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlAttribute("age")]
    public int Age { get; set; }
}

class Program
{
    static void Main()
    {
        User u = new User { Name = "张三", Age = 28 };
        XmlSerializer ser = new XmlSerializer(typeof(User));
        using (StringWriter sw = new StringWriter())
        {
            ser.Serialize(sw, u);
            Console.WriteLine(sw.ToString());
        }
    }
}

这段程序输出的XML会变成<User name="张三" age="28" />,完全符合属性化要求。可以看出,加特性成本极低,却直接改变了序列化形态。

常见限制与避坑点

第一个坑是集合属性不能直接标XmlAttribute。因为XML属性只能装单个简单值,不能放数组或列表。如果你有一个List<string>,只能让它序列化为元素,或者自己先拼成逗号分隔的字符串再标特性。第二个坑是同一个类里属性和元素不能重名,否则反序列化会报异常。

另外,如果父类子类都有同名属性且都标了XmlAttribute,需要用XmlAttributeOverrides在构造Serializer时显式覆盖,否则运行时可能抛InvalidOperationException。下面示例展示如何用覆盖方式处理继承场景:

using System;
using System.Xml.Serialization;
using System.IO;

public class Base
{
    public string Id { get; set; }
}

public class Sub : Base
{
    [XmlAttribute("id")]
    public new string Id { get; set; }

    [XmlAttribute("val")]
    public int Val { get; set; }
}

class Program
{
    static void Main()
    {
        XmlAttributeOverrides ov = new XmlAttributeOverrides();
        XmlAttributes attrs = new XmlAttributes();
        attrs.XmlAttribute = new XmlAttributeAttribute("id");
        ov.Add(typeof(Base), "Id", attrs);

        Sub s = new Sub { Id = "A1", Val = 10 };
        XmlSerializer ser = new XmlSerializer(typeof(Sub), ov);
        using (StringWriter sw = new StringWriter())
        {
            ser.Serialize(sw, s);
            Console.WriteLine(sw.ToString());
        }
    }
}

通过覆盖,我们确保基类里的Id也以属性形式出现,避免生成多余元素。实际项目中遇到继承结构时,这一招很实用。

反序列化的对称性

用XmlAttribute序列化出来的XML,在反序列化时不需要额外配置,只要类定义保持一致,XmlSerializer会自动把属性值读回对象。这也说明特性是双向生效的,不必写两套逻辑。

简单演示读取过程:

using System;
using System.Xml.Serialization;
using System.IO;

[XmlRoot("User")]
public class User
{
    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlAttribute("age")]
    public int Age { get; set; }
}

class Program
{
    static void Main()
    {
        string xml = "<User name="李四" age="33" />";
        XmlSerializer ser = new XmlSerializer(typeof(User));
        using (StringReader sr = new StringReader(xml))
        {
            User u = (User)ser.Deserialize(sr);
            Console.WriteLine(u.Name + " " + u.Age);
        }
    }
}

只要字符串是合法属性化XML,上面代码就能正确还原对象。由此可见,掌握XmlAttribute特性不仅能控制输出,也能保证输入输出协议统一。

小结

把C#对象序列化为XML属性而不是元素,核心就是给目标属性加XmlAttribute特性,并用XmlRoot规划根节点。遇到集合、继承等复杂情况,再配合字符串预处理或XmlAttributeOverrides即可。这样生成的XML更紧凑,也更易于和严格定义属性的外部系统互通。

C#XmlSerializerXmlAttribute修改时间:2026-08-08 03:06:26

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