Como você serializar uma string como CDATA usando XmlSerializer?

É possível, por meio de um atributo de algum tipo, serializar uma string como CDATA usando o .Net XmlSerializer?

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

Além da maneira postada por John Saunders, você pode usar um XmlCDataSection como o tipo diretamente, embora se reduza quase a mesma coisa:

 private string _message; [XmlElement("CDataElement")] public XmlCDataSection Message { get { XmlDocument doc = new XmlDocument(); return doc.CreateCDataSection( _message); } set { _message = value.Value; } } 
 [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; } } } 

Uso:

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

Saída:

 < ?xml version="1.0" encoding="utf-16"?>  < ![CDATA[Hello World]]>  

Na class a ser serializada:

 public CData Content { get; set; } 

E a class CData:

 public class CData : IXmlSerializable { private string _value; ///  /// Allow direct assignment from string: /// CData cdata = "abc"; ///  ///  ///  public static implicit operator CData(string value) { return new CData(value); } ///  /// Allow direct assigment to string /// string str = cdata; ///  ///  ///  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); } } 

Essa implementação tem a capacidade de processar o CDATA nested dentro da string que você está codificando (com base na resposta original de John Saunders).

Por exemplo, suponha que você queira codificar a seguinte sequência literal em CDATA:

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

Você deseja que a saída resultante seja algo como isto:

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

A implementação a seguir fará um loop pela string, dividirá as instâncias de ...]]>... em ...]] e >... e criará seções CDATA separadas para cada uma.

 [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 xmlNodes = new List(); 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 ]] (ie 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; } } } 

No meu caso estou usando campos mistos, alguns CDATA outros não, pelo menos para mim a seguinte solução está funcionando ….

Por sempre ler o campo Valor, estou recebendo o conteúdo, independentemente se CDATA ou apenas texto simples.

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

Antes tarde do que nunca.

Felicidades

Eu tinha uma necessidade semelhante, mas precisava de um formato de saída diferente – eu queria um atributo no nó que contém o CDATA. Eu tirei algumas inspirações das soluções acima para criar o meu próprio. Talvez ajude alguém no futuro …

 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() { } } 

No object pai a ser serializado, tenho a seguinte propriedade:

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

Eu recebo a seguinte saída:

   < ![CDATA[
]]> < ![CDATA[]]>