Как вы сериализуете строку как CDATA с помощью XmlSerializer?

Возможно ли через некоторый атрибут сериализовать строку как CDATA с помощью .Net XmlSerializer?

Ответ 1

[XmlRoot("root")]
public class Sample1Xml
{
    internal Sample1Xml()
    {
    }

    [XmlElement("node")]
    public NodeType Node { get; set; }

    #region Nested type: NodeType

    public class NodeType
    {
        [XmlAttribute("attr1")]
        public string Attr1 { get; set; }

        [XmlAttribute("attr2")]
        public string Attr2 { get; set; }

        [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));
                }

                Content = value[0].Value;
            }
        }
    }

    #endregion
}

Ответ 2

В дополнение к способу, опубликованному Джоном Сондерсом, вы можете напрямую использовать XmlCDataSection в качестве типа, хотя он сводится почти к тому же:

private string _message;
[XmlElement("CDataElement")]
public XmlCDataSection Message
{  
    get 
    { 
        XmlDocument doc = new XmlDocument();
        return doc.CreateCDataSection( _message);
    }
    set
    {
        _message = value.Value;
    }
}

Ответ 3

[Serializable]
public class MyClass
{
    public MyClass() { }

    [XmlIgnore]
    public string MyString { get; set; }
    [XmlElement("MyString")]
    public System.Xml.XmlCDataSection MyStringCDATA
    {
        get
        {
            return new System.Xml.XmlDocument().CreateCDataSection(MyString);
        }
        set
        {
            MyString = value.Value;
        }
    }
}

Использование:

MyClass mc = new MyClass();
mc.MyString = "<test>Hello World</test>";
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, mc);
Console.WriteLine(writer.ToString());

Вывод:

<?xml version="1.0" encoding="utf-16"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <MyString><![CDATA[<test>Hello World</test>]]></MyString>
</MyClass>

Ответ 4

В классе для сериализации:

public CData Content { get; set; }

И класс CData:

public class CData : IXmlSerializable
{
    private string _value;

    /// <summary>
    /// Allow direct assignment from string:
    /// CData cdata = "abc";
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static implicit operator CData(string value)
    {
        return new CData(value);
    }

    /// <summary>
    /// Allow direct assigment to string
    /// string str = cdata;
    /// </summary>
    /// <param name="cdata"></param>
    /// <returns></returns>
    public static implicit operator string(CData cdata)
    {
        return cdata._value;
    }

    public CData() : this(string.Empty)
    {
    }

    public CData(string value)
    {
        _value = value;
    }

    public override string ToString()
    {
        return _value;
    }

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

    public void ReadXml(System.Xml.XmlReader reader)
    {
        _value = reader.ReadElementString();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteCData(_value);
    }
}

Ответ 5

Эта реализация имеет возможность обрабатывать вложенные CDATA внутри кодируемой строки (на основе оригинального ответа Джона Сондерса).

Например, предположим, что вы хотите закодировать следующую литеральную строку в CDATA:

I am purposefully putting some <![CDATA[ cdata markers right ]]> in here!!

Вы хотите, чтобы результирующий результат выглядел примерно так:

<![CDATA[I am purposefully putting some <![CDATA[ cdata markers right ]]]]><![CDATA[> in here!!]]>

Следующая реализация будет перебирать строку, разделять экземпляры ...]]>... на ...]] и >... и создавать отдельные секции CDATA для каждого.

[XmlRoot("root")]
public class Sample1Xml
{
    internal Sample1Xml()
    {
    }

    [XmlElement("node")]
    public NodeType Node { get; set; }

    #region Nested type: NodeType

    public class NodeType
    {
        [XmlAttribute("attr1")]
        public string Attr1 { get; set; }

        [XmlAttribute("attr2")]
        public string Attr2 { get; set; }

        [XmlIgnore]
        public string Content { get; set; }

        [XmlText]
        public XmlNode[] CDataContent
        {
            get
            {
                XmlDocument dummy = new XmlDocument();
                List<XmlNode> xmlNodes = new List<XmlNode>();
                int tokenCount = 0;
                int prevSplit = 0;
                for (int i = 0; i < Content.Length; i++)
                {
                    char c = Content[i];
                    //If the current character is > and it was preceded by ]] (i.e. the last 3 characters were ]]>)
                    if (c == '>' && tokenCount >= 2)
                    {
                        //Put everything up to this point in a new CData Section
                        string thisSection = Content.Substring(prevSplit, i - prevSplit);
                        xmlNodes.Add(dummy.CreateCDataSection(thisSection));
                        prevSplit = i;
                    }
                    if (c == ']')
                    {
                        tokenCount++;
                    }
                    else
                    {
                        tokenCount = 0;
                    }
                }
                //Put the final part of the string into a CData section
                string finalSection = Content.Substring(prevSplit, Content.Length - prevSplit);
                xmlNodes.Add(dummy.CreateCDataSection(finalSection));

                return xmlNodes.ToArray();
            }
            set
            {
                if (value == null)
                {
                    Content = null;
                    return;
                }

                if (value.Length != 1)
                {
                    throw new InvalidOperationException(
                        String.Format(
                            "Invalid array length {0}", value.Length));
                }

                Content = value[0].Value;
            }
        }
    }

Ответ 6

В моем случае я использую смешанные поля, некоторые CDATA - нет, по крайней мере для меня работает следующее решение...

Всегда считывая поле Value, я получаю содержимое независимо от того, является ли CDATA или просто текстовым.

    [XmlElement("")]
    public XmlCDataSection CDataValue {
        get {
            return new XmlDocument().CreateCDataSection(this.Value);
        }
        set {
            this.Value = value.Value;
        }
    }

    [XmlText]
    public string Value;

Лучше поздно, чем никогда.

Приветствия

Ответ 7

У меня была аналогичная потребность, но мне нужен другой формат вывода - мне нужен атрибут node, который содержит CDATA. Я нашел вдохновение в вышеупомянутых решениях, чтобы создать свою собственную. Может быть, это поможет кому-то в будущем...

public class EmbedScript
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlText]
    public XmlNode[] Script { get; set; }

    public EmbedScript(string type, string script)
    {
        Type = type;
        Script = new XmlNode[] { new XmlDocument().CreateCDataSection(script) };
    }

    public EmbedScript()
    {

    }
}

В родительском объекте, который будет сериализован, у меня есть следующее свойство:

    [XmlArray("embedScripts")]
    [XmlArrayItem("embedScript")]
    public List<EmbedScript> EmbedScripts { get; set; }

Я получаю следующий вывод:

<embedScripts>
    <embedScript type="Desktop Iframe">
        <![CDATA[<div id="play_game"><iframe height="100%" src="http://www.myurl.com" width="100%"></iframe></div>]]>
    </embedScript>
    <embedScript type="JavaScript">
        <![CDATA[]]>
    </embedScript>
</embedScripts>

Ответ 8

Если вам нужна инкапсуляция CDATA, вы можете использовать для этого класс XmlCDataSection. Вы следующий класс как тип вашего элемента 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;
            }
        }
    }

Я нашел этот ответ здесь. Проверьте ссылку для полного обсуждения.