Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize element value as string although it contains mixed content

Assuming an XML like this:

<my:Root xmlns:my="http://foo/bar">
    <my:FieldBasic>content</my:FieldBasic>
    <my:FieldComplex>
        <html xml:space="preserve" xmlns="http://www.w3.org/1999/xhtml">
            <div><h1>content</h1></div>
        </html>
    </my:FieldComplex>
<my:Root>

and a class like:

[Serializable]
[XmlType(AnonymousType = true, Namespace = "http://foo/bar")]
[XmlRoot(ElementName = "Root", Namespace = "http://foo/bar", IsNullable = false)]
public class MyRoot
{
    public string FieldBasic { get; set; }
    public string FieldComplex { get; set; }
}

How do I deserialize <my:FieldComplex> to a string within FieldComplex? It fails when it finds the HTML inside. I want to make it give me a string with this content:

<html xml:space="preserve" xmlns="http://www.w3.org/1999/xhtml">
    <div><h1>content</h1></div>
</html>

If I declare FieldComplex as public object FieldComplex (i.e. xsd:anyType) it kinda works and I get a XMLNode[] inside which I can use.

But I need the FieldComplex to be of type string for the serialization as for serialization the XML will not contain HTML, it will be like:

<my:Root xmlns:my="http://foo/bar">
    <my:FieldBasic>content</my:FieldBasic>
    <my:FieldComplex>content</my:FieldComplex>
<my:Root>

Declaring FieldComplex as object will insert these attributes on the <my:FieldComplex> element:

 xmlns:q1="http://www.w3.org/2001/XMLSchema" p3:type="q1:string" xmlns:p3="http://www.w3.org/2001/XMLSchema-instance

and I don't want that. I also don't want to use different classes for serialization and deserialization.

So, is it possible?

To make a long story short, is it possible to have this class:

public class MyRoot
{
    public string FieldBasic { get; set; }
    public string FielComplex { get; set; }
}

Serialize to this:

<my:Root xmlns:my="http://foo/bar">
    <my:FieldBasic>content</my:FieldBasic>
    <my:FieldComplex>content</my:FieldComplex>
<my:Root>

and deserialize from this:

<my:Root xmlns:my="http://foo/bar">
    <my:FieldBasic>content</my:FieldBasic>
    <my:FieldComplex>
        <html xml:space="preserve" xmlns="http://www.w3.org/1999/xhtml">
            <div><h1>content</h1></div>
        </html>
    </my:FieldComplex>
<my:Root>

?

P.S. Just to explain "the why?". I have a class witch gets serialized. The serialized XML then travels through multiple nodes in the application and eventually comes back but altered like above. The layers do some XML validation and having extra attributes or elements on input fail the validation and stops the flow. I want to map the return XML to the same class. The content is just strings from it's point of view but of course not the same for serialization/deserialization :(

like image 234
JohnDoDo Avatar asked Jan 04 '12 17:01

JohnDoDo


2 Answers

This isn't quite finished because I can't remember if you can / how to add the namespace prefix to the root element in Xml Serialization. But if you implement the IXmlSerializable interface in your MyRoot class like this:

[XmlRoot("Root", Namespace="http://foo/bar")]
public class MyRoot : IXmlSerializable

Then write the XML serialization methods yourself, something like this:

        void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
        {
            reader.MoveToContent();
            var outerXml = reader.ReadOuterXml();
            XElement root = XElement.Parse(outerXml);

            this.FieldBasic = root.Elements(XName.Get("FieldBasic", "http://foo/bar")).First().Value;
            this.FieldComplex = root.Elements(XName.Get("FieldComplex", "http://foo/bar")).First().Elements().First().Value.Trim();
        }



        void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteRaw(String.Format("\r\n\t<my:FieldBasic>\r\n\t\t{0}\r\n\t</my:FieldBasic>", this.FieldBasic));
            writer.WriteRaw(String.Format("\r\n\t<my:FieldComplex>\r\n\t\t{0}\r\n\t</my:FieldComplex>\r\n", this.FieldComplex));
        }

(Return null from the GetSchema method)

This should get you at least pretty close to what you're after.

You may also find these links helpful.

IXmlSerializable

Namespaces

like image 113
Balthy Avatar answered Sep 28 '22 05:09

Balthy


You could use CDATA in the XML to indicate that the contents is a string literal:

<my:Root xmlns:my="http://foo/bar">
  <my:FieldBasic>content</my:FieldBasic>
  <my:FieldComplex>
    <![CDATA[
      <html xml:space="preserve" xmlns="http://www.w3.org/1999/xhtml">
        <div><h1>content</h1></div>
      </html>
    ]]>
  </my:FieldComplex>
</my:Root>
like image 37
Darin Dimitrov Avatar answered Sep 28 '22 05:09

Darin Dimitrov