C# – Suppress Null Value Types from Being Emitted by XmlSerializer

cxmlxml-serialization

Please consider the following Amount value type property which is marked as a nullable XmlElement:

[XmlElement(IsNullable=true)] 
public double? Amount { get ; set ; }

When a nullable value type is set to null, the C# XmlSerializer result looks like the following:

<amount xsi:nil="true" />

Rather than emitting this element, I would like the XmlSerializer to suppress the element completely. Why? We're using Authorize.NET for online payments and Authorize.NET rejects the request if this null element exists.

The current solution/workaround is to not serialize the Amount value type property at all. Instead we have created a complementary property, SerializableAmount, which is based on Amount and is serialized instead. Since SerializableAmount is of type String, which like reference types are suppressed by the XmlSerializer if null by default, everything works great.

/// <summary>
/// Gets or sets the amount.
/// </summary>
[XmlIgnore]
public double? Amount { get; set; }

/// <summary>
/// Gets or sets the amount for serialization purposes only.
/// This had to be done because setting value types to null 
/// does not prevent them from being included when a class 
/// is being serialized.  When a nullable value type is set 
/// to null, such as with the Amount property, the result 
/// looks like: &gt;amount xsi:nil="true" /&lt; which will 
/// cause the Authorize.NET to reject the request.  Strings 
/// when set to null will be removed as they are a 
/// reference type.
/// </summary>
[XmlElement("amount", IsNullable = false)]
public string SerializableAmount
{
    get { return this.Amount == null ? null : this.Amount.ToString(); }
    set { this.Amount = Convert.ToDouble(value); }
}

Of course, this is just a workaround. Is there a cleaner way to suppress null value type elements from being emitted?

Best Answer

Try adding:

public bool ShouldSerializeAmount() {
   return Amount != null;
}

There are a number of patterns recognised by parts of the framework. For info, XmlSerializer also looks for public bool AmountSpecified {get;set;}.

Full example (also switching to decimal):

using System;
using System.Xml.Serialization;

public class Data {
    public decimal? Amount { get; set; }
    public bool ShouldSerializeAmount() {
        return Amount != null;
    }
    static void Main() {
        Data d = new Data();
        XmlSerializer ser = new XmlSerializer(d.GetType());
        ser.Serialize(Console.Out, d);
        Console.WriteLine();
        Console.WriteLine();
        d.Amount = 123.45M;
        ser.Serialize(Console.Out, d);
    }
}

More information on ShouldSerialize* on MSDN.