序列化一个可为空的 int

Posted

技术标签:

【中文标题】序列化一个可为空的 int【英文标题】:Serialize a nullable int 【发布时间】:2010-09-19 16:27:48 【问题描述】:

我有一个可以为空的 int 类?数据类型设置为序列化为 xml 元素。有没有办法设置它,如果值为 null,xml 序列化程序将不会序列化元素?

我尝试添加 [System.Xml.Serialization.XmlElement(IsNullable=false)] 属性,但我得到一个运行时序列化异常,指出存在反映类型的错误,因为“可能未设置 IsNullable为 Nullable 类型设置为 'false'。考虑使用 'System.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>

但是对于为 null 的 ID,我根本不需要 ID 元素,主要是因为当我在 MSSQL 中使用 OPENXML 时,对于看起来像

的元素,它返回 0 而不是 null

【问题讨论】:

【参考方案1】:

XmlSerializer 支持ShouldSerializeFoo() 模式,所以你可以添加一个方法:

public bool ShouldSerializeID() return ID.HasValue;

还有FooSpecified 模式 - 不确定 XmlSerializer 是否支持该模式。

【讨论】:

XmlSerializer 还支持 [FooSpecified 模式。 相关页面在这里:msdn.microsoft.com/en-us/library/53b8022e%28VS.71%29.aspx 有什么方法可以将 ShouldSerialize 与自动生成的属性一起使用?即没有局部变量。 @Jay:不需要。您可以在该物业上致电HasValue @mark 如果对于成员(属性/字段)Foo 你也有一个public bool FooSpecified get ... set ...,那么get 用于查看Foo 是否应该被序列化,@在反序列化期间为Foo 赋值时调用987654330@。【参考方案2】:

我正在使用这个微模式来实现 Nullable 序列化:

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

这为用户提供了正确的界面而不会妥协,并且在序列化时仍然可以做正确的事情。

【讨论】:

由于 SomeValue 可能为空... public double XmlSomeValue get return SomeValue.HasValue? SomeValue.Value:0; 设置 SomeValue = 值; XmlSomeValue 只应该由 XmlSerializer 使用,只有在 XmlSomeValueSpecified 为真时才会触摸它(即 SomeValue.Value 不为空。 @pettys:这是 XML,你期待什么? ;-) 接受的答案是 2008 年的。现在应该是这个。 Interesting answer 与 Specified vs ShouldSerialize 相关 绝对应该是最佳答案。【参考方案3】:

我想出了一个利用两个属性的解决方法。一个整数?具有 XmlIgnore 属性和被序列化的对象属性的属性。

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

【讨论】:

这个解决方案很棒,因为它还允许将 NULL 编码为客户端的“占位符”值,这些客户端不识别 int 中的 NULL,即 Flex。 您可以将[EditorBrowsable(EditorBrowsableState.Never)]添加到xml序列化属性中,以避免在编码时看到它【参考方案4】:

哇,谢谢这个问题/答案真的帮助了我。我喜欢 ***。

我使您在上面所做的事情更加通用。我们真正想要的是让 Nullable 具有稍微不同的序列化行为。我使用 Reflector 构建了我自己的 Nullable,并在这里和那里添加了一些东西以使 XML 序列化按照我们想要的方式工作。似乎工作得很好:

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;

你失去了让你的成员成为 int 的能力?依此类推(必须改用 Nullable),但除此之外,所有行为都保持不变。

【讨论】:

这会在XmlSerializer.Serialize 上向我抛出一个System.ExecutionEngineException【参考方案5】:

不幸的是,您描述的行为在 XmlElementAttribute.IsNullable 的文档中得到了准确的记录。

【讨论】:

【参考方案6】:

非常有用的帖子帮助很大。

我选择使用 Scott 对 Nullable(Of T) 数据类型的修订,但是发布的代码仍然会在 Null 为 Null 时序列化 Nullable 元素 - 尽管没有“xs:nil='true'”属性。

我需要强制序列化程序完全删除标签,所以我只是在结构上实现了 IXmlSerializable(这是在 VB 中,但你明白了):

  '----------------------------------------------------------------------------
  ' 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

与使用 (foo)Specified 模式相比,我更喜欢这种方法,因为这需要向我的对象添加大量冗余属性,而使用新的 Nullable 类型只需要重新键入属性。

【讨论】:

以上是关于序列化一个可为空的 int的主要内容,如果未能解决你的问题,请参考以下文章

如果它为空或为空,如何从序列化中忽略可为空的属性?

将可为空的 int 映射到可为空的 int + automapper

XmlSerializer 和可为空的属性

使用 Protobuf-net 序列化可空双精度列表的问题

如何将 C# 可为空的 int 转换为 int

将 int.TryParse 与可为空的 int 一起使用 [重复]