在序列化期间将 c# class 属性分组到 XmlElement 属性中

Group group c# class properties into XmlElement attributes during serialization

在c#classes序列化为xml的过程中,需要匹配某个xml结构,但最好不要改动[=25] =]结构已经存在。

是否可以创建一种将多个属性分组到它们自己的 xml 元素中,并将它们的值存储为 xml 属性?

示例 C# 代码类似于:

class SomeClass {
    [XmlElement("Element1")]
    [XmlAttribute("attribute1")]
    int prop1;
    [XmlElement("Element1")]
    [XmlAttribute("attribute2")]
    int prop2;

    [XmlElement("Element2")]
    [XmlAttribute("attribute1")]
    int prop3;
    [XmlElement("Element2")]
    [XmlAttribute("attribute2")]
    int prop4;
    [XmlElement("Element2")]
    [XmlAttribute("attribute3")]
    int prop5;
    [XmlElement("Element2")]
    [XmlAttribute("attribute4")]
    int prop6;
}

xml 输出:

<SomeClass>
    <Element1 Attribute1="value1" attribute2="value2"/>
    <Element2 Attribute1="value3" attribute2="value4" attribute3="value5" attribute4="value6"/>
</SomeClass>

连载后

如果该解决方案也适用于反序列化,那就太棒了。

您可以通过为每个 XmlElement 使用单独的 类 来做到这一点:

public class SomeClass 
{
    public Element1 Element1 { get; set; }
    
    public Element2 Element2 { get; set; }
}

public class Element1 
{
    [XmlAttribute]
    public int attribute1 { get; set; }

    [XmlAttribute]
    public int attribute2 { get; set; }
}

public class Element2
{
    [XmlAttribute]
    public int attribute1 { get; set; }

    [XmlAttribute]
    public int attribute2 { get; set; }
    
    [XmlAttribute]
    public int attribute3 { get; set; }

    [XmlAttribute]
    public int attribute4 { get; set; }
}

Online demo

如果你真的必须避免“改变 class 结构”,属性(在我看来)不会得到你想要的 XML 结构 and 帮助您获得可读、可维护的代码。

IXmlSerializable interface allows you to manually control serialization/deserialization of your class. When implementing this interface, you have full control to create whatever XML text you desire when your class is consumed by XmlSerializer.

下面是一个编译控制台应用程序示例,供您试用。它展示了如何在 SomeClass XML 中读取和写入自定义节点,以及如何将 class 属性放入这些节点的 XML 属性中。请注意,我使用 nameof 来获取属性名称,但如果需要,您也可以轻松地将 XML 属性名称硬编码为 "Attribute1""Attribute2"

你会看到,写起来比使用属性更麻烦。但是一旦掌握了它,它就会非常简单。而且它绝对不会改变 class 结构。

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

namespace ConsoleApp1
{
    public class SomeClass : IXmlSerializable
    {
        // element 1
        public int prop1 { get; set; }
        public int prop2 { get; set; }

        // element 2
        public int prop3 { get; set; }
        public int prop4 { get; set; }
        public int prop5 { get; set; }
        public int prop6 { get; set; }

        #region IXmlSerializable
        public XmlSchema GetSchema()
        {
            return null;
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("Element1");
            writer.WriteAttributeString(nameof(prop1), prop1.ToString());
            writer.WriteAttributeString(nameof(prop2), prop2.ToString());
            writer.WriteEndElement();

            writer.WriteStartElement("Element2");
            writer.WriteAttributeString(nameof(prop3), prop3.ToString());
            writer.WriteAttributeString(nameof(prop4), prop4.ToString());
            writer.WriteAttributeString(nameof(prop5), prop5.ToString());
            writer.WriteAttributeString(nameof(prop6), prop6.ToString());
            writer.WriteEndElement();
        }

        public void ReadXml(XmlReader reader)
        {
            // element 1
            reader.Read();
            reader.MoveToAttribute(nameof(prop1));
            if (reader.ReadAttributeValue())
            {
                prop1 = int.Parse(reader.Value);
            }
            reader.MoveToAttribute(nameof(prop2));
            if (reader.ReadAttributeValue())
            {
                prop2 = int.Parse(reader.Value);
            }

            // element 2
            reader.Read();
            reader.MoveToAttribute(nameof(prop3));
            if (reader.ReadAttributeValue())
            {
                prop3 = int.Parse(reader.Value);
            }
            reader.MoveToAttribute(nameof(prop4));
            if (reader.ReadAttributeValue())
            {
                prop4 = int.Parse(reader.Value);
            }
            reader.MoveToAttribute(nameof(prop5));
            if (reader.ReadAttributeValue())
            {
                prop5 = int.Parse(reader.Value);
            }
            reader.MoveToAttribute(nameof(prop6));
            if (reader.ReadAttributeValue())
            {
                prop6 = int.Parse(reader.Value);
            }
        }
        #endregion
    }

    class Program
    {
        static void Main()
        {
            string tempPath = "c:\temp\test.xml";
            XmlSerializer serializer = new XmlSerializer(typeof(SomeClass));

            // build an instance to serialize
            SomeClass s1 = new SomeClass
            {
                prop1 = 1,
                prop2 = 2,
                prop3 = 3,
                prop4 = 4,
                prop5 = 5,
                prop6 = 6
            };

            // serialize it
            using (StreamWriter sw = new StreamWriter(tempPath))
            {
                serializer.Serialize(sw, s1);

            }

            /* Produces the following XML:
                <?xml version="1.0" encoding="utf-8"?>
                <SomeClass>
                  <Element1 prop1="1" prop2="2" />
                  <Element2 prop3="3" prop4="4" prop5="5" prop6="6" />
                </SomeClass>
            */

            // deserialize
            SomeClass s2;
            using (StreamReader sr = new StreamReader(tempPath))
            {
                s2 = (SomeClass)serializer.Deserialize(sr);
            }

            // check contents of s2 as you please
        }
    }
}

(如果你喜欢这个,你应该在实际部署之前清理它 - 没有错误处理存在,例如在 int.Parse 上。它只是为了说明 IXmlSerializable 的使用,而不完全改变 class 结构。)