【问题标题】:XmlSerializer: serializing a class property as an attribute of a custom subelementXmlSerializer:将类属性序列化为自定义子元素的属性
【发布时间】:2012-02-06 11:08:16
【问题描述】:

我正在使用XmlSerializer。我的班级:

[Serializable]
[XmlRoot(ElementName="MyClass")]
public class MyClass
{
    public string Value;
}

我想对其进行序列化,以便 Value 最终成为名为(例如)“文本”的子元素的属性。

期望的结果:

<MyClass>
    <Text Value="3"/>
</MyClass>

但是NOT(这就是将Value标记为XmlAttribute的效果)

<MyClass Value="3">
</MyClass>

NOT(这会将 Value 标记为 XmlElement):

<MyClass>
    <Value>3</Value>
</MyClass>

我如何做到这一点?

我知道我可以将Value 的类型从字符串更改为另一个可序列化的自定义类。

不幸的是,我有很多这样的属性,所以我需要创建几十个小类。

有没有更快的解决方案?


编辑:

回应您的 cmets:

  • 不,不是每个属性都必须序列化为名为“Text”的子元素。子元素的名称是唯一且明确的。

  • 示例输出 XML:

    <visibility>
        <site visible="yes"/>
        <comparator visible="no"/>
        <expiration days="7"/>
        <comment>blahblahblah</comment>
    <visibility>
    
  • 示例类:

[XmlRoot(ElementName="Visibility")]
public class Visibility
{
    [XPath("/site@visible")] // if only this was possible!
    public string OnSite
    {
        get { return SiteVisible ? "yes" : "no"; }
    }

    [XPath("/comparator@visible")] // as above...
    public string InComparator
    {
        get { return ComparatorVisible ? "yes" : "no"; }
    }

    [XmlIgnore]
    public bool SiteVisible;
    [XmlIgnore]
    public bool ComparatorVisible;

    [XPath("/expiration@days")] // as above...
    public int ExpiresAfterDays; 

    [XmlElement("comment")] // this is easy
    public string Comment;
}

【问题讨论】:

  • 您能否提供完整的所需 XML 结构(3-5 个元素/属性)和适当的类?
  • 我有一个解决方案 - 但如果每个属性都需要序列化为名为“文本”的元素 - 所以您需要根据基里尔的要求提供更多示例。拥有多个“文本”的问题在于,不可能以相反的方向反序列化,因为序列化程序无法确定每个“文本”映射到哪个属性。请提供更多,以便我们提供帮助。
  • 顺便说一句 - 我怀疑你的意思是 [XmlIgnore] 而不是 [NonSerialized]。
  • 哦,没错。 NonSerialized 用于二进制序列化。
  • 您寻求的功能不存在。硬着头皮写一百个小班。它只执行一次。

标签: c# xml serialization xml-serialization


【解决方案1】:

不改变Value的类型我认为这是不可能的。您可以在Value 上添加属性XmlElement(ElementName="Text"),但您将获得类似于此的结果:

<MyClass> 
    <Text>3</Text> 
</MyClass> 

已编辑: 另一种解决方案可能涉及 XSLT 转换:您可以使用 .Net 序列化生成 xml,然后应用 xml 转换。

XslTransform myXslTransform = new XslTransform();
myXslTransform.Load(xsltDoc);
myXslTransform.Transform(sourceDoc, resultDoc);

我的例子的变形应该是这样的:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
    <root>
        <xsl:apply-templates/>
    </root>
    </xsl:template>
    <xsl:template match="MyClass">
        <MyClass>
            <Text>
               <xsl:attribute name="Value">
                    <xsl:value-of select="Text"/>
               </xsl:attribute>
            </Text>
        </MyClass>
    </xsl:template>
</xsl:stylesheet>

【讨论】:

  • 这是我对生成的 XML 执行转换的想法的一个很好的变体,因为它涉及预先存在的功能 (XSLT)。我想,一个自定义属性加上一个动态生成足够 XSLT 的类将大大增强 .NET 的 XML 序列化功能
【解决方案2】:

对于这种灵活性,您应该真正考虑实施IXmlSerializable,因为这会给您更多的控制权:

[XmlRoot("visibility")]
public class Visibility : IXmlSerializable
{
    public string Site;
    public string Comparator;
    public int Expiration;
    public string Comment;

    public XmlSchema GetSchema()
    {
        throw new NotImplementedException();
    }

    public void ReadXml(XmlReader reader)
    {
        // implement me if you want to deserialize too.
        throw new NotImplementedException();
    }

    public void WriteXml(XmlWriter writer)
    {
        WriteProperty(writer, "site", "visible", Site);
        WriteProperty(writer, "comparator ", "visible", Comparator);
        WriteProperty(writer, "expiration ", "days", Expiration);

        if (!string.IsNullOrEmpty(Comment))
        {
            writer.WriteElementString("comment", Comment);
        }
    }

    private void WriteProperty<T>(XmlWriter writer, string elementName, string attibuteName, T value)
    {
        if (value != null)
        {
            writer.WriteStartElement(elementName);
            writer.WriteAttributeString(attibuteName, value.ToString());
            writer.WriteEndElement();
        }
    }
}

显然,这里有一些手动工作,但它确实允许您将所有序列化代码保存在一个地方,而不是让较小的类激增。

上面的例子只实现了序列化——如果你需要从 xml 反序列化到你的类型,你需要编写一个等效的反序列化实现。

【讨论】:

  • 我正在使用属性的自定义类 PropertyXmlFormatter。然后,您可以将每个属性设为 PropertyXmlFormatter 而不是字符串等。它具有重写的隐式强制转换运算符和 .Equals,因此它可以用作赋值和比较中的真实类型。问题是,最终工作量太大,不值得。
【解决方案3】:

感谢所有答案。遗憾的是 .NET XmlSerialization 库不允许这样做(我认为应该这样做!)。我正在寻找一个尽可能通用的解决方案。

我能想到的最好的一个(考虑到最大通用性的标准,同时实现起来相当快)是让XmlSerializer以它喜欢的方式序列化我的类,然后只转换输出,重新定位某些元素放入嵌套位置。

类似的东西:

    /// <remarks>
    /// (angle brackets replaced with round ones to avoid confusing the XML-based documentation comments format)
    /// 
    /// Let input XML be:
    /// 
    ///     (root)
    ///         (days)3(/days)
    ///     (/root)
    ///     
    /// Calling Reposition on this input with mappings argument being:
    ///     (key) "days"
    ///     (value) { "time", "days" }
    ///     
    /// Returns:
    /// (root)
    ///     (time days="3" /)
    /// (/root)
    /// </remarks>        
    static XElement Reposition(XElement input, KeyValuePair<string, string[]>[] mappings)
    {
        var result = new XElement(input);
        foreach (var mapping in mappings)
        {
            var element = result.Element(mapping.Key);
            if (element == null)
            {
                continue;
            }
            var value = element.Value;
            element.Remove();

            var insertAt = result;
            foreach (var breadcrumb in mapping.Value)
            {
                if (breadcrumb == mapping.Value.Last())
                {
                    insertAt.Add(new XAttribute(breadcrumb, value));
                }
                else
                {
                    insertAt.Add(new XElement(breadcrumb));
                    insertAt = insertAt.Element(breadcrumb);
                }
            }
        }
        return result;
    }

我想我会将它与一个自定义属性(类似于我希望存在的XPath 属性:请参阅我的问题中的示例代码)结合起来,并将此功能封装在我自己的序列化程序类中。

关于这种方法的任何 cmets / 见解?

我可以想到一个潜在的性能缺陷(在每次序列化后重写/重新解析 XML),但生成的 XML 片段预计不会很大,所以这可能可以忽略不计。

在这一点上,反序列化的问题并不困扰我(反序列化已经实现,并且通过 XPath 和一些实用方法完全“手动”完成)。

【讨论】:

  • 您可以考虑使用 XSLT 将 .Net 生成的 xml 转换为您数据交换所需的 XML。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-18
相关资源
最近更新 更多