【问题标题】:How to prevent XmlSerialzer from escaping "nested XML"?如何防止 XmlSerialzer 转义“嵌套 XML”?
【发布时间】:2016-06-29 18:31:10
【问题描述】:

我正在使用 XmlSerializer 序列化/反序列化复杂对象。一个属性包含一个 XML 字符串,应将其写入字符串属性而无需反序列化。

示例(可在 LinqPad 中执行):

[XmlRoot("RootObject")]
[Serializable]
public class RootClass
{
    [XmlArray("SubObjects")]
    [XmlArrayItem("SubObject")]
    public SubClass[] SubObjecs { get; set;} 
}

[Serializable]
public class SubClass
{
    [XmlElement("XmlConfiguration")]
    public string XmlConfiguration { get; set;}
}

void Main()
{
    var obj = new RootClass()
    {
        SubObjecs = new[]
        {
            new SubClass { XmlConfiguration = "<ConfigurationX>SomeConfiguration1</ConfigurationX>" },
            new SubClass { XmlConfiguration = "<ConfigurationY>SomeConfiguration2</ConfigurationY>" }
        }
    };

    var serializer = new XmlSerializer(typeof(RootClass));
    using (var stream = new MemoryStream())
    {
        serializer.Serialize(stream, obj);
        stream.Position = 0;
        Console.WriteLine(Encoding.UTF8.GetString(stream.GetBuffer()));
    }
}

例子的输出是:

<?xml version="1.0"?>
<RootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <SubObjects>
        <SubObject>
            <XmlConfiguration>&lt;ConfigurationX&gt;SomeConfiguration1&lt;/ConfigurationX&gt;</XmlConfiguration>
        </SubObject>
        <SubObject>
            <XmlConfiguration>&lt;ConfigurationY&gt;SomeConfiguration2&lt;/ConfigurationY&gt;</XmlConfiguration>
        </SubObject>
    </SubObjects>
</RootObject>

XML 是有时以编程方式编写的配置文件,但主要由人编写/修改。因此XmlConfiguration 中的 XML 不应包含转义字符。

问题: 是否可以防止 XmlSerializer 转义 '' 字符?如果没有,是否有另一个可以使用的序列化程序?

一个有效的选项是XmlWriter.WriteRaw。但是,如果可能的话,我会避免这种不可靠且不易维护的解决方案。


我在这里发现了一个类似的问题:How to prevent XmlSerializer from escaping < and > characters。但这个问题与 !CDATA[[Content]] 有关,对我的问题没有答案。

【问题讨论】:

  • 您可以在嵌套的XmlConfiguration 类上使用[XmlAnyElement][XmlAnyAttribute]。见XML Serialization - Leaving part of the XML as an XmlElement
  • 顺便说一下,如果您更喜欢 LINQ-to-XML 而不是旧的XmlDocument,那么List&lt;XElement&gt; 将与XmlAnyElement 一起使用(尽管List&lt;XAttribute&gt; 将与[XmlAnyAttribute] 一起使用,奇怪。)例如见Deserialize dynamic XML
  • @dbc:很好,谢谢!也可以将[XmlAnyElement]XElement 结合使用(没有列表)。你想写你的 cmets 作为答案吗?

标签: c# xml escaping xml-serialization xmlserializer


【解决方案1】:

正如上面 dbc 的评论中提到的,有一个使用 XmlAnyElement 属性的解决方案,如下所述:Deserialize dynamic XML


我找到了一个混合了XmlSerializerXmlWriter.WriteRaw 的解决方案。在实现IXmlSerializable时,可以控制XmlSerializer的序列化过程。 Therfore IXmlSerializable 必须只为需要特殊处理的类实现(这对我来说没问题):

[Serializable]
public class SubClass : IXmlSerializable
{
    [XmlElement("XmlConfiguration")]
    public string XmlConfiguration { get; set; }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteStartElement("XmlConfiguration");
        writer.WriteRaw(XmlConfiguration);
        writer.WriteEndElement();
    }

    public void ReadXml(XmlReader reader)
    {
        reader.ReadToDescendant("XmlConfiguration");
        XmlConfiguration = reader.ReadInnerXml();
        reader.ReadEndElement();
    }

    public XmlSchema GetSchema()
    {
        return (null);
    }
}

【讨论】:

    【解决方案2】:

    如果您需要 CDATA 封装,您可以为此使用 XmlCDataSection Class。您将类作为 xml 元素的类型。您可以根据需要为每种不同类型的元素更改名称或属性。

    public class XmlConfiguration //Or any other class name.
        {
            [XmlAttribute("attr1")]
            public string Attr1 { get; set; } //You don't need this but use if you need attribute.
    
            [XmlAttribute("attr2")]
            public string Attr2 { get; set; } //Or second one.
    
            [XmlIgnore]
            public string Content { get; set; }
    
            [XmlText]
            public XmlNode[] CDataContent
            {
                get
                {
                    var dummy = new XmlDocument();
                    return new XmlNode[] {dummy.CreateCDataSection(Content)};
                }
                set
                {
                    if (value == null)
                    {
                        Content = null;
                        return;
                    }
    
                    if (value.Length != 1)
                    {
                        throw new InvalidOperationException(
                            String.Format(
                                "Invalid array length {0}", value.Length));
                    }
    
                    var node0 = value[0];
                    var cdata = node0 as XmlCDataSection;
                    if (cdata == null)
                    {
                        throw new InvalidOperationException(
                            String.Format(
                                "Invalid node type {0}", node0.NodeType));
                    }
    
                    Content = cdata.Data;
                }
            }
        }
    

    我找到了这个答案here。查看完整讨论的链接。

    【讨论】:

      猜你喜欢
      • 2023-02-14
      • 1970-01-01
      • 2019-01-18
      • 1970-01-01
      • 2013-01-12
      • 1970-01-01
      • 2014-10-16
      • 2019-12-24
      相关资源
      最近更新 更多