67

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?

Ben Griswold
  • 17,793
  • 14
  • 58
  • 60

4 Answers4

139

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks Marc. Now that you told me what to look for, I think I found the MSDN reference on XmlSerializer "property + specified" pattern. Here it is: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx. It's even difficult to find once you find the right MSDN reference page if you ask me. :) Thanks again. – Ben Griswold Aug 18 '09 at 21:30
  • 7
    And here's the MSDN reference for the ShouldSerialize pattern: http://msdn.microsoft.com/en-us/library/53b8022e(VS.71).aspx – Ben Griswold Aug 18 '09 at 21:38
  • (JohannesH already posted that; I assumed you meant "in the context of XmlSerializer") – Marc Gravell Aug 18 '09 at 21:41
  • Why in the world didn't Microsoft make this an attribute or a parameter of an existing attribute? We have to implement publicly exposed methods in order to suppress null values from being emitted? I mean, I get that there might be instances where you want to keep a property from serializing based on something other than if it's null, and for those cases this is fine. However, suppressing based on null is by far the most common case. Why why why Microsoft? – crush Mar 10 '14 at 19:31
  • @crush the overall pattern is shared by lots of the component-model. The only oddity here is the fact that it needs to be public, which is a feature of the code-generation meta-programming approach. Just one of many reasons why I don't do it that way when I do meta-programming ;p – Marc Gravell Mar 10 '14 at 19:59
6

There is also an alternative to get

 <amount /> instead of <amount xsi:nil="true" />

Use

[XmlElement("amount", IsNullable = false)]
public string SerializableAmount
{
    get { return this.Amount == null ? "" : this.Amount.ToString(); }
    set { this.Amount = Convert.ToDouble(value); }
}
mko
  • 6,638
  • 12
  • 67
  • 118
0
public class XmlNullable<T>
    where T : struct
{
    [XmlText]
    public T Value { get; set; }

    public static implicit operator XmlNullable<T>(T value)
    {
        return new XmlNullable<T>
        {
            Value = value
        };
    }

    public static implicit operator T?(XmlNullable<T> value)
    {
        if (value == null)
        {
            return null;
        }

        return value.Value;
    }

    public override string ToString()
    {
        return Value.ToString();
    }
}

Tested successfully with

  • int
  • bool
  • enum with [XmlEnum] attribute
  • serialize and deserialize

Example:

[XmlRoot("example")]
public class Example
{
    [XmlElement("amount")]
    public XmlNullable<double> Amount { get; set; }
}

Null property ignored:

new Example()

will result in XML:

<example />

Property with value gets to XML, as before:

var example = new Example
{
    Amount = 3.14
};

will result in XML:

<example>
  <amount>3.14</amount>
</example>
Joseph K
  • 1
  • 1
-1

you can try this :

xml.Replace("xsi:nil=\"true\"", string.Empty);