¿Cómo se serializa una cadena como CDATA usando XmlSerializer?

¿Es posible a través de un atributo de algún tipo serializar una cadena como CDATA usando .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 } 

Además del modo publicado por John Saunders, puedes usar XmlCDataSection como el tipo directamente, aunque se reduce a casi lo mismo:

 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()); 

Salida:

   Hello World]]>  

En la clase a ser serializada:

 public CData Content { get; set; } 

Y la clase 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); } } 

Esta implementación tiene la capacidad de procesar CDATA nested dentro de la cadena que está codificando (según la respuesta original de John Saunders).

Por ejemplo, supongamos que desea codificar la siguiente cadena literal en CDATA:

 I am purposefully putting some  in here!! 

Querrá que la salida resultante se vea así:

  in here!!]]> 

La siguiente implementación recorrerá la cadena, dividirá las instancias de ...]]>... en ...]] y >... y creará secciones CDATA separadas para cada una.

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

En mi caso, estoy usando campos mixtos, algunos no, algunos CDATA, al menos para mí la siguiente solución funciona …

Al leer siempre el campo Valor, obtengo los contenidos, independientemente de si son CDATA o simplemente texto sin formato.

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

Mejor tarde que nunca.

Aclamaciones

Tenía una necesidad similar pero necesitaba un formato de salida diferente: quería un atributo en el nodo que contiene el CDATA. Me inspiré en las soluciones anteriores para crear las mías. Tal vez ayudará a alguien en el 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() { } } 

En el objeto principal que se serializará, tengo la siguiente propiedad:

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

Obtengo el siguiente resultado:

   
]]>