XML 纯字符串属性

XML Attribute on Plain String

我想用 C# 代码生成如下 XML。

<card>
 <name>Cool Card</name>
 <set rarity="common">S1</set>
</card>

我有这样的东西。

public class card
{
    public string name = "Cool Card";
    [XmlAttribute]
    public string rarity = "common";
    public string set = "S1";
}

public static void WriteXml()
{
    var serializer = new XmlSerializer(typeof(card));
    var myCard = new();

    using var sw = new StringWriter();
    serializer.Serialize(sw, myCard);
    XDocument doc = XDocument.Parse(sw.ToString());
    XmlWriterSettings xws = new();
    xws.OmitXmlDeclaration = true;
    xws.Indent = true;
    using var xw = XmlWriter.Create(path, xws);
    doc.Save(xw);
}

问题是这不会将“稀有度”属性添加到“设置”值。尝试添加 [XmlAttribute] 会将其添加到父元素而不是下一个同级元素,我不知道如何在普通字符串元素上获取它,所以目前我的输出看起来像。

<card rarity="common">
 <name>Cool Card</name>
 <set>S1</set>
</card>

XML example doc 显示了如何在元素上设置属性的示例,但只有一个具有嵌套字段的示例,而不是一个纯字符串。是否可以像我第一个发布的示例演示的那样向 XML 中的普通旧字符串元素添加属性?

试试这个:

public class card
{
    public string name = "Cool Card";
    public Set set = new();
}

public class Set
{
    [XmlText]
    public string value = "S1";
    [XmlAttribute]
    public string rarity = "common";
}

如果您考虑一下,没有其他方法可以将 xml 属性仅应用于声明它的元素。因此您需要将其移至另一个 class。当您执行此操作时,新的 属性 值是必需的,并且需要将其展平,因为不需要值节点,因为您需要 XmlText 属性。

在这种情况下最干净的选择是实现 IXmlSerializable:

public class card : IXmlSerializable
{
    public string name = "Cool Card";
    public string rarity = "common";
    public string set = "S1";

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        reader.ReadStartElement(nameof(card));

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            if (reader.Name == nameof(name))
            {
                this.name = reader.ReadElementContentAsString();
            }
            else if (reader.Name == nameof(set))
            {
                this.rarity = reader.GetAttribute(nameof(rarity));
                this.set = reader.ReadElementContentAsString();
            }
        }

        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteElementString(nameof(name), this.name);

        writer.WriteStartElement(nameof(set));
        writer.WriteAttributeString(nameof(rarity), this.rarity);
        writer.WriteString(this.set);
        writer.WriteEndElement();                
    }
}

如果你的 class 很大,你只需要在 XML 中做一点改变,有时实现 IXmlSerializable 是一团糟(你必须保存所有属性并且只保存一两个,做一点改变)。在这些情况下,您可以使用属性和一些小助手 classes 来获得相同的结果。这个想法是告诉 XML 序列化器不要序列化一些 属性 并使用另一个伪造的 属性 来进行序列化。

例如,使用您想要的 XML 结构创建集合 class:

public class XmlSet
{
    private readonly card _card;

    public XmlSet()
    {
        this._card = new card();
    }

    public XmlSet(card card)
    {
        this._card = card;
    }

    [XmlText]
    public string set
    {
        get { return this._card.set; }
        set { this._card.set = value; }
    }

    [XmlAttribute]
    public string rarity
    {
        get { return this._card.rarity; }
        set { this._card.rarity = value; }
    }
}

它只是一个包装器,具有您想要的 XML 属性。您 get/set 值 from/to 您的 card 对象。

然后,在你的卡片class中,忽略集合属性并序列化假的属性:

public class card
{
    public string name = "Cool Card";
    [XmlIgnore]
    public string rarity = "common";
    [XmlIgnore]
    public string set = "S1";

    // Added to serialization
    private XmlSet _xmlSetNode;

    [XmlElement("set")]
    public XmlSet XmlSet
    {
        get
        {
            this._xmlSetNode = this._xmlSetNode ?? new XmlSet(this);
            return this._xmlSetNode;
        }
        set { this._xmlSetNode = value; }
    }
}