将类的Nullable double属性序列化为Xml Text

时间:2015-01-23 13:04:51

标签: c# xml xml-serialization

我必须使用以下代码进行序列化:

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

我正在尝试使用value属性作为节点的文本序列化NodeC,这是一个double,但它不起作用,即使使用ShouldSerialize模式来避免序列化空节点。 NodeA和NodeB工作正常。我需要NodeC的帮助。

1 个答案:

答案 0 :(得分:3)

您无法将可为空的双重序列化为XmlText。如果你看一下你所获得的System.InvalidOperationException的全文,你会看到类似的内容:

   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)

该消息是自我解释的。来自documentation for XmlTextAttribute

的确认
  

您可以将XmlTextAttribute应用于返回基元和枚举类型的公共字段和公共读/写属性。

     

您可以将XmlTextAttribute应用于返回字符串数组的字段或属性。您还可以将该属性应用于Object类型的数组,但必须将Type属性设置为string。在这种情况下,插入到数组中的任何字符串都被序列化为XML文本。

     

XmlTextAttribute也可以应用于返回XmlNode或XmlNode对象数组的字段。

要理解为什么ShouldSerializeXXX()在这里没有帮助,您应该了解XmlSerializer works as follows

  1. 第一次序列化类型时,XmlSerializer构造函数在内部编写运行时c#代码,以使用反射对类型和所有引用类型的实例进行序列化和反序列化,然后编译代码并加载将DLL导入内存。

  2. 随后,类实例的序列化和反序列化由先前创建的动态DLL执行。

  3. 但是第1步无法访问该类的实例。它基于类型信息创建其动态库。而且,根据类型信息,当ShouldSerializeXXX()为空时,无法推断相关的false方法将返回double? value。因此,动态代码生成中止,因为无法生成将可空双精度写为XmlText的代码。

    作为一种解决方法,您可以创建一个表示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);
            }
        }
    }