序列化一个可空整数

我有一个带有可空整型的类?将 datatype 设置为序列化为 xml 元素。有没有什么方法可以设置它,这样当值为 null 时 xml 序列化程序就不会序列化元素?

我试图添加[系统。Xml.序列化。XmlElement (IsNullable = false)]属性,但是我得到一个运行时序列化异常,说明反映类型的错误,因为“对于 Nullable 类型,IsNullable 可能没有设置为‘ false’。考虑使用「系统」。Int32’类型或从 XmlElement 属性中删除 IsNullable 属性。”

[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;
}
}
...
}

上面的类将序列化为:

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

但是对于 ID 为 null 的情况,我根本不需要 ID 元素,主要是因为当我在 MSSQL 使用 OPENXML 时,对于类似的元素,它返回0而不是 null

87326 次浏览

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

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());
}
}
}

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.

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.

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.

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.