XML Serialization - XmlCDataSection as Serialization.XmlText
Asked Answered
I

5

8

I am having problems serializing a cdata section using c#

I need to serialize XmlCDataSection object property as the innertext of the element.

The result I am looking for is this:

<Test value2="Another Test">
  <![CDATA[<p>hello world</p>]]>
</Test>

To produce this, I am using this object:

public class Test
{
    [System.Xml.Serialization.XmlText()]
    public XmlCDataSection value { get; set; }

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string value2 { get; set; }
}

When using the xmltext annotation on the value property the following error is thrown.

System.InvalidOperationException: There was an error reflecting property 'value'. ---> System.InvalidOperationException: Cannot serialize member 'value' of type System.Xml.XmlCDataSection. XmlAttribute/XmlText cannot be used to encode complex types

If I comment out the annotation, the serialization will work but the cdata section is placed into a value element which is no good for what I am trying to do:

<Test value2="Another Test">
  <value><![CDATA[<p>hello world</p>]]></value>
</Test>

Can anybody point me in the right direction to getting this to work.

Thanks, Adam

Irritant answered 9/9, 2009 at 10:0 Comment(1)
What happens if you remove [XmlText]?Rib
I
5

Thanks Richard, only now had chance to get back to this. I think I have resolved the problem by using your suggestion. I have created a CDataField object using the following:

public class CDataField : IXmlSerializable
    {
        private string elementName;
        private string elementValue;

        public CDataField(string elementName, string elementValue)
        {
            this.elementName = elementName;
            this.elementValue = elementValue;
        }

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void WriteXml(XmlWriter w)
        {
            w.WriteStartElement(this.elementName);
            w.WriteCData(this.elementValue);
            w.WriteEndElement();
        }

        public void ReadXml(XmlReader r)
        {                      
            throw new NotImplementedException("This method has not been implemented");
        }
    }
Irritant answered 22/10, 2009 at 13:27 Comment(1)
it throws A.CDataField cannot be serialized because it does not have a parameterless constructor errorYves
E
2

The way Test is defined, your data is a CData object. So the serialisation system is trying to preserve the CData object.

But you want to serialise some text data as a CData section.

So first, the type of Test.value should be String.

You then need to control how that field is serialised, but there does not appear to be any inbuilt method or attribute to control how strings are serialised (as string, maybe with entities for reserved characters, or as CDATA). (Since, from an XML infoset perspective all of these are the same, this is not surprising.)

You can of course implemented IXmlSerializable and just code the serialisation of the Test type yourself which gives you complete control.

Eldin answered 9/9, 2009 at 11:43 Comment(0)
A
2

This basically shorter version of Jack answer with better error messages:

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

[XmlText]
public XmlNode[] ContentAsCData
{
    get => new[] { new XmlDocument().CreateCDataSection(Content) };
    set => Content = value?.Cast<XmlCDataSection>()?.Single()?.Data;
}
Athirst answered 15/8, 2019 at 17:50 Comment(0)
T
1

Just found an alternative from here:

     [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;
                }
            }
        }
Trap answered 5/8, 2015 at 2:23 Comment(0)
V
0

I had very same problem as Adam. However this Answer does not helped me at 100% :) but gives me a clue. So I'va created a code like below. It generates XML like this:

<Actions>
    <Action Type="reset">
      <![CDATA[
      <dbname>longcall</dbname>
      <ontimeout>
       <url>http://[IPPS_ADDRESS]/</url>
       <timeout>10</timeout>
      </ontimeout>
      ]]>
    </Action>
    <Action Type="load">
      <![CDATA[
      <dbname>longcall</dbname>
      ]]>
    </Action>
</Actions>

Code:

public class ActionsCDataField : IXmlSerializable
{
    public List<Action> Actions { get; set; }

    public ActionsCDataField()
    {
        Actions = new List<Action>();
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void WriteXml(XmlWriter w)
    {
        foreach (var item in Actions)
        {
            w.WriteStartElement("Action");
            w.WriteAttributeString("Type", item.Type);
            w.WriteCData(item.InnerText);                
            w.WriteEndElement();
            w.WriteString("\r\n");
        }
    }

    public void ReadXml(XmlReader r)
    {
        XmlDocument xDoc = new XmlDocument();
        xDoc.Load(r);

        XmlNodeList nodes = xDoc.GetElementsByTagName("Action");
        if (nodes != null && nodes.Count > 0)
        {
            foreach (XmlElement node in nodes)
            {
                Action a = new Action();
                a.Type = node.GetAttribute("Type");
                a.InnerText = node.InnerXml;
                if (a.InnerText != null && a.InnerText.StartsWith("<![CDATA[") && a.InnerText.EndsWith("]]>"))
                    a.InnerText = a.InnerText.Substring("<![CDATA[".Length, a.InnerText.Length - "<![CDATA[]]>".Length);

                Actions.Add(a);
            }
        }
    }
}

public class Action
{
    public String Type { get; set; }
    public String InnerText { get; set; }
}
Versicular answered 27/1, 2014 at 9:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.