How to make a value type nullable with .NET XmlSerializer?

前端 未结 7 508
陌清茗
陌清茗 2020-12-02 11:47

Let\'s suppose I have this object:

[Serializable]
public class MyClass
{
    public int Age { get; set; }
    public int MyClassB { get; set; }
}
[Serializab         


        
相关标签:
7条回答
  • 2020-12-02 11:54

    Extending Samuel's answer and Greg Beech's comment to the case of a boolean property: if the property is of type bool then you can't write a simple test in the propertySpecified property.

    A solution is to use a Nullable<bool> type, then the test in the propertySpecified property is simply property.HasValue. e.g.

    using System.Xml.Serialization;
    
    public class Person
    {
        public bool? Employed { get; set; }
    
        [XmlIgnore]
        public bool EmployedSpecified { get { return Employed.HasValue; } }
    }
    

    An alternative to using a nullable type for a numeric property (suggested by Greg Beech) is to set the value property to an invalid default value, such as -1, as follows:

    using System.ComponentModel;
    using System.Xml.Serialization;
    
    public class Person
    {
        [DefaultValue(-1)]
        public int Age { get; set; }
    
        [XmlIgnore]
        public bool AgeSpecified { get { return Age >= 0; } }
    }
    
    0 讨论(0)
  • 2020-12-02 11:58

    You need to do custom XML serialization; see IXmlSerializer.

    public class MyClass : IXmlSerializable
    {
        public int Age { get; set; }
        public MyClassB MyClassB { get; set; }
    
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            // http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
            return null;
        }
    
        public void ReadXml(XmlReader reader)
        {
            if (reader.IsStartElement("Age"))
                Age = reader.ReadContentAsInt();
    
            var serializer = new XmlSerializer(typeof(MyClassB));
            MyClassB = (MyClassB)serializer.Deserialize(reader);
        }
    
        public void WriteXml(XmlWriter writer)
        {
            if (Age > 0)
            {
                writer.WriteStartElement("Age");
                writer.WriteValue(Age);
                writer.WriteEndElement();
            }
    
            var serializer = new XmlSerializer(typeof(MyClassB));
            serializer.Serialize(writer, MyClassB);
        }
    }
    
    0 讨论(0)
  • 2020-12-02 12:03

    Forget about Nullable ... ShouldSerializeXXX is a pretty solution. Here Age will be serialized upon your condition.

    [Serializable]
    public class MyClass
    {
        public int Age { get; set; }
        public int MyClassB { get; set; }
    
        #region Conditional Serialization
        public bool ShouldSerializeAge() { return age > 0; }
        #endregion
    }
    
    [Serializable]
    public class MyClassB
    {
        public int RandomNumber { get; set; }
    }
    
    0 讨论(0)
  • 2020-12-02 12:08

    xsd.exe will autogenerate the XXXSpecified property and accessors if you set the 'minoccurs' attribute as 'minoccurs="0"' for an element ... if you are using a schema to define your xml/class

    0 讨论(0)
  • 2020-12-02 12:09

    I just discovered this. XmlSerialier looks for a XXXSpecified boolean property to determine if it should be included. This should solve the problem nicely.

    [Serializable]
    public class MyClass
    {
      public int Age { get; set; }
      [XmlIgnore]
      public bool AgeSpecified { get { return Age >= 0; } }
      public int MyClassB { get; set; }
    }
    
    [Serializable]
    public class MyClassB
    {
      public int RandomNumber { get; set; }
    }
    

    Proof:

    static string Serialize<T>(T obj)
    {
      var serializer = new XmlSerializer(typeof(T));
      var builder = new StringBuilder();
      using (var writer = new StringWriter(builder))
      {
        serializer.Serialize(writer, obj);
        return builder.ToString();
      }
    }
    
    static void Main(string[] args)
    {
      var withoutAge = new MyClass() { Age = -1 };
      var withAge = new MyClass() { Age = 20 };
    
      Serialize(withoutAge); // = <MyClass><MyClassB>0</MyClassB></MyClass>
      Serialize(withAge); // = <MyClass><Age>20</Age><MyClassB>0</MyClassB></MyClass>
    }
    

    Edit: Yes, it is a documented feature. See the MSDN entry for XmlSerializer

    Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName".

    0 讨论(0)
  • 2020-12-02 12:13

    You can use XmlElementAttribute.IsNullable:

    [Serializable]
    public class MyClass
    {
        [XmlElement(IsNullable = true)]
        public int? Age { get; set; }
    
        public int MyClassB { get; set; }
    }
    
    0 讨论(0)
提交回复
热议问题