如何防止XmlSerialzer转义"嵌套XML"?

时间:2016-06-29 18:31:10

标签: c# xml escaping xml-serialization xmlserializer

我正在使用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逃离&#39;&lt;&#39;和&#39;&gt;&#39;字符?如果没有,是否有另一个可以使用的序列化器?

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

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

2 个答案:

答案 0 :(得分:2)

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

我找到了XmlSerializerXmlWriter.WriteRaw混合的解决方案。实施IXmlSerializable时,可以控制XmlSerializer的序列化过程。因此,必须为需要特殊处理的类实现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);
    }
}

答案 1 :(得分:0)

如果您需要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。检查链接,完整的论述。