• 使用XmlSerializer序列化可空属性


    使用XmlSerializer可以方便的将对象序列化为xml,实现应用之间的数据交互。但是XmlSerializer却不能很好地序列化类型中的可空字段。 
    例如,有如下定义的类Person: 

    C#代码  收藏代码
    1. [Serializable]  
    2. [XmlRoot(ElementName = "Person")]  
    3. public class Person  
    4. {  
    5.     public string FirstName { getset; }  
    6.   
    7.     public string LastName { getset; }  
    8.   
    9.     public int? Age { getset; }  
    10. }  


    其中的Age属性为Nullable int类型。 
    我们的实例化代码如下所示: 

    C#代码  收藏代码
    1. var person = new Person  
    2.                  {  
    3.                      FirstName = "First",  
    4.                  };  
    5. person.OutputXml(Console.Out);  


    其中方法OutputXml为扩展方法,使用XmlSerializer来序列化对象,具体定义为: 

    C#代码  收藏代码
    1. public static void OutputXml<T>(this T dto, TextWriter textWriter)  
    2. {  
    3.     var xmlTypeMapping = typeof(T);  
    4.     var serializer = new XmlSerializer(xmlTypeMapping);  
    5.     var xmlns = new XmlSerializerNamespaces();  
    6.     xmlns.Add(string.Empty, string.Empty);  
    7.     using (var writer = new XmlTextWriter(textWriter) { Formatting = Formatting.Indented })  
    8.     {  
    9.         serializer.Serialize(writer, dto, xmlns);  
    10.     }  
    11. }  


    使用上述方法序列化对象person,得到的结果为: 
     
    注意到虽然Age属性为空,却仍然被序列化成 了古怪的xml,这往往不是所期望的结果。事实上同为空的LastName属性的序列化正是大多数情况下我们所期望的行为——忽略为空的属性。 
    另一方面,如果试图序列化类属性为xml 属性(而非xml元素),则甚至不能工作。例如如果我们将Person类的定义修改成如下形式以便序列化为xml属性: 

    C#代码  收藏代码
    1. [Serializable]  
    2. [XmlRoot(ElementName = "Person")]  
    3. public class Person  
    4. {  
    5.     [XmlAttribute]  
    6.     public string FirstName { getset; }  
    7.   
    8.     [XmlAttribute]  
    9.     public string LastName { getset; }  
    10.   
    11.     [XmlAttribute]  
    12.     public int? Age { getset; }  
    13. }  


    Xmlserializer甚至无法正常序列化上面同样的person对象并抛出如下“XmlAttribute/XmlText cannot be used to encode complex types”的错误: 
     
    如何解决上述的2个问题呢? 
    1. 序列化可空属性为XmlElement 
    为了在序列化可空属性的时候忽略空值的古怪输出,我们可以在Person类中定义一个返回bool的特殊方法ShouldSerializeAge,并在其中实现定义我们的序列化规则: 

    C#代码  收藏代码
    1. [Serializable]  
    2. [XmlRoot(ElementName = "Person")]  
    3. public class Person  
    4. {  
    5.     public string FirstName { getset; }  
    6.   
    7.     public string LastName { getset; }  
    8.   
    9.     public int? Age { getset; }  
    10.   
    11.     public bool ShouldSerializeAge()  
    12.     {  
    13.         return Age.HasValue;  
    14.     }  
    15. }  


    在这里我们定义序列化规则为:序列化非空Age。注意该方法的名字一定是以ShouldSerialize开头并连接上需要自定义规则的属性名称。这样序列化出来的person对象为: 
     
    空的Age和空的LastName一样在序列化时被忽略了输出。正是我们期望的结果! 
    2. 序列化可空属性为XmlAttribute 
    由于可空属性无法被直接序列化为XmlAttribute,我们需要采用间接的办法——定义间接属性。此时我们可以如下定义Person类: 

    C#代码  收藏代码
    1. [Serializable]  
    2. [XmlRoot(ElementName = "Person")]  
    3. public class Person  
    4. {  
    5.     [XmlAttribute]  
    6.     public string FirstName { getset; }  
    7.   
    8.     [XmlAttribute]  
    9.     public string LastName { getset; }  
    10.   
    11.     [XmlIgnore]   // (4)   
    12.     public int? Age { getset; }  
    13.   
    14.     [XmlAttribute(AttributeName = "Age")]  // (1)  
    15.     public string AgeValue  
    16.     {  
    17.         get  
    18.         {  
    19.             // (2)  
    20.             return Age.HasValue ? Age.Value.ToString() : null;  
    21.         }  
    22.         set  
    23.         {  
    24.             int result;  
    25.             // (3)  
    26.             Age = int.TryParse(value, out result) ? result : (int?) null;  
    27.         }  
    28.     }  
    29. }  


    注意类中注释的部分: 

    1. 为原可空属性定义一个“虚拟”的属性,该属性可为任意名称任意类型(示例里定义的为string类型的AgeValue),但注意要将该属性的序列化名称置为原可空属性Age。
    2. 在get方法里根据Age是否为空将其转化为AgeValue。
    3. 在set方法里将输入的value转化为合适的值赋给可空属性Age。
    4. 将原Age属性标记为XmlIgnore,使XmlSerializer在序列化时忽略序列化该属性。


    使用上述方法,我们可以将Age序列化为XmlAttribute了,虽然实际上我们是“骗”了XmlSerializer并使用另一个属性作为Age属性的值。 
    因此,当person对象中的Age为空时,我们得到如下的xml结果: 
     
    而当person对象中的Age不为空时,我们也可以正常的用XmlAttribute来表示Age的值了。 

  • 相关阅读:
    Windows 下安装 Python + Django
    asp.net core 一个中小型项目实战的起手式——Swagger配置
    asp.net core 一个中小型项目实战的起手式——项目搭建与仓储模式下的持久层创建(1)
    c#一些常用知识点
    ADO.NET中的5个主要对象
    JavaScript的闭包特性如何给循环中的对象添加事件(一)
    在什么情况下会用到虚方法?它与接口有什么不同?
    一般处理程序Session
    什么是code-Behind技术?
    什么是事务?
  • 原文地址:https://www.cnblogs.com/kissfu/p/3421289.html
Copyright © 2020-2023  润新知