95

I have a class with a nullable int? datatype set to serialize as an xml element. Is there any way to set it up so the xml serializer will not serialize the element if the value is null?

I've tried to add the [System.Xml.Serialization.XmlElement(IsNullable=false)] attribute, but I get a runtime serialization exception saying there was a an error reflecting the type, because "IsNullable may not be set to 'false' for a Nullable type. Consider using 'System.Int32' type or removing the IsNullable property from the XmlElement attribute."

[Serializable]
[System.Xml.Serialization.XmlRoot("Score", Namespace = "http://mycomp.com/test/score/v1")]
public class Score
{
    private int? iID_m;
    ...

    /// <summary>
    /// 
    /// </summary>        
    public int? ID 
    { 
        get 
        { 
            return iID_m; 
        } 
        set 
        { 
            iID_m = value; 
        } 
    }
     ...
}

The above class will serialize to:

<Score xmlns="http://mycomp.com/test/score/v1">
    <ID xsi:nil="true" />
</Score>

But for IDs that are null I don't want the ID element at all, primarily because when I use OPENXML in MSSQL, it returns a 0 instead of null for an element that looks like

dbc
  • 104,963
  • 20
  • 228
  • 340
Jeremy
  • 44,950
  • 68
  • 206
  • 332

7 Answers7

157

XmlSerializer supports the ShouldSerialize{Foo}() pattern, so you can add a method:

public bool ShouldSerializeID() {return ID.HasValue;}

There is also the {Foo}Specified pattern - not sure if XmlSerializer supports that one.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 8
    XmlSerializer also supports the [Foo}Specified pattern. – David Schmitt Mar 04 '09 at 13:47
  • 23
    Relevent page is here: http://msdn.microsoft.com/en-us/library/53b8022e%28VS.71%29.aspx – cbp Jan 08 '10 at 06:31
  • 1
    Any way to use ShouldSerialize with auto-generated properties? i.e. no local variable. – Jay Jul 15 '10 at 15:20
  • 1
    @Jay: No need for one. You can just call `HasValue` on the property. – Steven Sudit Aug 16 '10 at 18:06
  • Please, forgive me my ignorance, but what is the {Foo}Specified pattern? – mark Sep 20 '11 at 09:27
  • 1
    @mark if, for member (property/field) `Foo` you also have a `public bool FooSpecified {get {...} set {...}}`, then the `get` is used to see if `Foo` should be serialized, and the `set` is called when assigning a value to `Foo` during deserialization. – Marc Gravell Sep 20 '11 at 10:25
  • According to the MSDN doc linked in the comments, we should also implement the resetXXX method, which would just set it to null again. The two methods appear to work together. – Michael Erickson Nov 22 '13 at 16:42
  • Why is it that a property of type `string` doesn't require this pattern? I.e. if I don't set a string property, it won't output an XML element, but for a float, I have to declare it as nullable AND implement this pattern. – AaronLS Apr 03 '14 at 20:59
  • AaronLS because strings are reference types and can be null anyway. Integers can not. – Marc Gravell Apr 03 '14 at 21:50
28

I'm using this micro-pattern to implement Nullable serialization:

[XmlIgnore]
public double? SomeValue { get; set; }

[XmlAttribute("SomeValue")] // or [XmlElement("SomeValue")]
[EditorBrowsable(EditorBrowsableState.Never)]
public double XmlSomeValue { get { return SomeValue.Value; } set { SomeValue= value; } }  
[EditorBrowsable(EditorBrowsableState.Never)]
public bool XmlSomeValueSpecified { get { return SomeValue.HasValue; } }

This provides the right interface to the user without compromise and still does the right thing when serializing.

David Schmitt
  • 58,259
  • 26
  • 121
  • 165
  • 1
    Since SomeValue may be null... public double XmlSomeValue { get { return SomeValue.HasValue? SomeValue.Value : 0; } set { SomeValue = value; } } – Doug Domeny Jun 05 '09 at 11:56
  • XmlSomeValue is only supposed to be used by the XmlSerializer who'll only touch it when XmlSomeValueSpecified is true (i.e. SomeValue.Value is not null. – David Schmitt Jun 05 '09 at 12:02
  • The accepted answer is from 2008. This one should be the one now. [Interesting answer](http://stackoverflow.com/a/37842985/354756) related to *Specified vs ShouldSerialize* – daniloquio Jul 12 '16 at 13:30
12

I figured out a workaround utilizing two properties. An int? property with an XmlIgnore attribute and an object property which gets serialized.

    /// <summary>
    /// Score db record
    /// </summary>        
    [System.Xml.Serialization.XmlIgnore()]
    public int? ID 
    { 
        get 
        { 
            return iID_m; 
        } 
        set 
        { 
            iID_m = value; 
        } 
    }

    /// <summary>
    /// Score db record
    /// </summary>        
    [System.Xml.Serialization.XmlElement("ID",IsNullable = false)]
    public object IDValue
    {
        get
        {
            return ID;
        }
        set
        {
            if (value == null)
            {
                ID = null;
            }
            else if (value is int || value is int?)
            {
                ID = (int)value;
            }
            else
            {
                ID = int.Parse(value.ToString());
            }
        }
    }
Jeremy
  • 44,950
  • 68
  • 206
  • 332
  • This solution is great as it also allows NULL to be encoded as "placeholder" value for clients, that do not recognize NULLs in ints, i.e. Flex. – Kuba Wyrostek Jun 25 '13 at 09:01
  • You can add [EditorBrowsable(EditorBrowsableState.Never)] to the xml serialized property to aviod seen it when coding – Antonio Rodríguez May 22 '20 at 10:26
6

Wow thanks this question/answer really helped me out. I heart Stackoverflow.

I made what you are doing above a little more generic. All we're really looking for is to have Nullable with slightly different serialization behavior. I used Reflector to build my own Nullable, and added a few things here and there to make the XML serialization work the way we want. Seems to work pretty well:

public class Nullable<T>
{
    public Nullable(T value)
    {
        _value = value;
        _hasValue = true;
    }

    public Nullable()
    {
        _hasValue = false;
    }

    [XmlText]
    public T Value
    {
        get
        {
            if (!HasValue)
                throw new InvalidOperationException();
            return _value;
        }
        set
        {
            _value = value;
            _hasValue = true;
        }
    }

    [XmlIgnore]
    public bool HasValue
        { get { return _hasValue; } }

    public T GetValueOrDefault()
        { return _value; }
    public T GetValueOrDefault(T i_defaultValue)
        { return HasValue ? _value : i_defaultValue; }

    public static explicit operator T(Nullable<T> i_value)
        { return i_value.Value; }
    public static implicit operator Nullable<T>(T i_value)
        { return new Nullable<T>(i_value); }

    public override bool Equals(object i_other)
    {
        if (!HasValue)
            return (i_other == null);
        if (i_other == null)
            return false;
        return _value.Equals(i_other);
    }

    public override int GetHashCode()
    {
        if (!HasValue)
            return 0;
        return _value.GetHashCode();
    }

    public override string ToString()
    {
        if (!HasValue)
            return "";
        return _value.ToString();
    }

    bool _hasValue;
    T    _value;
}

You lose the ability to have your members as int? and so on (have to use Nullable<int> instead) but other than that all behavior stays the same.

scobi
  • 14,252
  • 13
  • 80
  • 114
1

Unfortunately, the behaviours you describe are accurately documented as such in the docs for XmlElementAttribute.IsNullable.

Serge Wautier
  • 21,494
  • 13
  • 69
  • 110
1

Very useful posting helped a great deal.

I opted to go with Scott's revision to the Nullable(Of T) datatype, however the code posted still serializes the Nullable element when it is Null - albeit without the "xs:nil='true'" attribute.

I needed to force the serializer to drop the tag completely so I simply implemented IXmlSerializable on the structure (this is in VB but you get the picture):

  '----------------------------------------------------------------------------
  ' GetSchema
  '----------------------------------------------------------------------------
  Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
    Return Nothing
  End Function

  '----------------------------------------------------------------------------
  ' ReadXml
  '----------------------------------------------------------------------------
  Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml
    If (Not reader.IsEmptyElement) Then
      If (reader.Read AndAlso reader.NodeType = System.Xml.XmlNodeType.Text) Then
         Me._value = reader.ReadContentAs(GetType(T), Nothing)
      End If
    End If
  End Sub

  '----------------------------------------------------------------------------
  ' WriteXml
  '----------------------------------------------------------------------------
  Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml
    If (_hasValue) Then
      writer.WriteValue(Me.Value)
    End If
  End Sub

I prefer this method to using the (foo)Specified pattern as that requires adding bucket loads of redundant properties to my objects, whereas using the new Nullable type just requires the retyping of the properties.

James Close
  • 862
  • 9
  • 15
0
    [XmlIgnore]
    public int? Revision { get; set; }
    [XmlAttribute("Revision")]
    public string RevisionAsString
    {
        get => Revision?.ToString();
        set => Revision = string.IsNullOrEmpty(value) ? default : int.Parse(value);
    }
    public bool ShouldSerializeRevisionAsString()
    {
        return Revision.HasValue;
    }
Oana
  • 537
  • 5
  • 11