3

I have to serialize using the folowing code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace MyExample
{
    class Program
    {
    static void Main(string[] args)
    {
        MyXmlDocument document = new MyXmlDocument();

        document.MyExample.NodeA.value = "Value To Node A";
        document.MyExample.NodeB.value = "Value To Node B";
        document.MyExample.NodeC.value = 1234.567;
        document.WriteToXml(@"C:\Users\E9JR\Desktop\mydocument.xml");
        Console.Write("> Done!");
        Console.ReadKey();
    }
}

[XmlRoot(ElementName="xmlExample",IsNullable=false)]
public class XmlExample
{
    private NodeA_Elem _nodea;
    [XmlElement()]
    public NodeA_Elem NodeA
    {
        get
        {
            return _nodea;
        }
        set
        {
            _nodea = value;
        }
    }
    public bool ShouldSerializeNodeA()
    {
        return !String.IsNullOrEmpty(_nodea.value);
    }

    private NodeB_Elem _nodeb;
    [XmlElement(ElementName = "NodeB", IsNullable = false)]
    public NodeB_Elem NodeB
    {
        get
        {
            return _nodeb;
        }
        set
        {
            _nodeb = value;
        }
    }
    public bool ShouldSerializeNodeB()
    {
        return !String.IsNullOrEmpty(_nodeb.value);
    }

    private NodeC_Elem _nodec;
    [XmlElement(ElementName = "NodeC",IsNullable=false)]
    public NodeC_Elem NodeC
    {
        get
        {
            return _nodec;
        }
        set
        {
            _nodec = value;
        }
    }
    public bool ShouldSerializeNodeC()
    {
        return _nodec.value.HasValue;
    }

    public XmlExample()
    {
        _nodea = new NodeA_Elem();
        _nodeb = new NodeB_Elem();
        _nodec = new NodeC_Elem();
    }
}

public class NodeA_Elem
{
    [XmlText()]
    public string value { get; set; }
}

public class NodeB_Elem
{
    [XmlText()]
    public string value { get; set; }
}

public class NodeC_Elem
{
    [XmlText()]
    public double? value { get; set; }
}

public class MyXmlDocument
{
    private XmlExample _myexample;
    public XmlExample MyExample
    {
        get
        {
            return _myexample;
        }
        set
        {
            _myexample = value;
        }
    }

    public void WriteToXml(string path)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(XmlExample));

        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.Encoding = Encoding.Unicode;

        StringWriter txtwriter = new StringWriter();
        XmlWriter xmlwtr = XmlWriter.Create(txtwriter, settings);
        serializer.Serialize(xmlwtr, MyExample);

        StreamWriter writer = new StreamWriter(path);
        writer.Write(txtwriter.ToString());

        writer.Close();
    }

    public void ReadXml(string path)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(XmlExample));

        StreamReader reader = new StreamReader(path);

        MyExample = (XmlExample)serializer.Deserialize(reader);

    }

    public MyXmlDocument()
    {
        _myexample = new XmlExample();
    }
    }
}

I'm trying to serialize NodeC using as text for the node the value property, which is a double, but it's not working, even using the ShouldSerialize pattern to avoid serialize empty nodes. NodeA and NodeB is working fine. I need help for NodeC.

1 Answers1

4

You can't serialize a nullable double as XmlText. If you look at the full text of the System.InvalidOperationException you are getting, you will see something like:

   InnerException: System.InvalidOperationException
        Message="Cannot serialize member 'value' of type System.Nullable`1[System.Double]. XmlAttribute/XmlText cannot be used to encode complex types."
        Source="System.Xml"
        StackTrace:
             at System.Xml.Serialization.XmlReflectionImporter.ImportAccessorMapping(MemberMapping accessor, FieldModel model, XmlAttributes a, String ns, Type choiceIdentifierType, Boolean rpc, Boolean openModel, RecursionLimiter limiter)
             at System.Xml.Serialization.XmlReflectionImporter.ImportFieldMapping(StructModel parent, FieldModel model, XmlAttributes a, String ns, RecursionLimiter limiter)
             at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)

That message is self explanatory. Confirmation from the documentation for XmlTextAttribute:

You can apply the XmlTextAttribute to public fields and public read/write properties that return primitive and enumeration types.

You can apply the XmlTextAttribute to a field or property that returns an array of strings. You can also apply the attribute to an array of type Object but you must set the Type property to string. In that case, any strings inserted into the array are serialized as XML text.

The XmlTextAttribute can also be applied to a field that returns an XmlNode or an array of XmlNode objects.

To understand why ShouldSerializeXXX() doesn't help here, you should understand that XmlSerializer works as follows:

  1. The first time you serialize a type, the XmlSerializer constructor internally writes run-time c# code to serialize and deserialize instances of the type and all referenced types using reflection, then compiles the code and loads the resulting DLL into memory.

  2. Subsequently, serialization and deserialization of class instances are performed by the previously created dynamic DLL.

But step 1 does not have access to the instance of the class. It creates its dynamic library based purely on type information. And, from the type information, there is no way to infer that the relevant ShouldSerializeXXX() method will return false when the double? value is null. Thus, dynamic code generation aborts, because code to write a nullable double as XmlText can't be generated.

As a workaround, you could make a string property that represents the double:

public class NodeC_Elem
{
    [XmlIgnore]
    public double? value { get; set; }

    [XmlText]
    public string StringValue 
    {
        get
        {
            if (value == null)
                return null;
            return XmlConvert.ToString(value.Value);
        }
        set
        {
            if (value == null)
            {
                this.value = null;
                return;
            }
            this.value = XmlConvert.ToDouble(value);
        }
    }
}
dbc
  • 104,963
  • 20
  • 228
  • 340