2008-10-28 21 views
81

Tengo una clase con un int nullable? datatype configurado para serializar como un elemento xml. ¿Hay alguna forma de configurarlo para que el serializador xml no serialice el elemento si el valor es nulo?Serializar un nullable int

He intentado agregar el atributo [System.Xml.Serialization.XmlElement (IsNullable = false)], pero obtengo una excepción de serialización en tiempo de ejecución que indica que hubo un error que refleja el tipo, porque "IsNullable puede no ser establece en 'falso' para un tipo Nullable. Considere usar el tipo 'System.Int32' o eliminar la propiedad IsNullable del atributo XmlElement. "

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

La clase por encima de serializará a:

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

Pero para los ID que son nulos no quiero que el elemento de identificación en todo, principalmente porque cuando se utiliza OPENXML en MSSQL, devuelve una 0 en lugar de nulo para un elemento que se parece a

Respuesta

139

XmlSerializer es compatible con el patrón ShouldSerialize{Foo}(), lo que puede añadir un método:

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

También existe el patrón {Foo}Specified - no estoy seguro si es compatible con XmlSerializer que uno.

+8

XmlSerializer también es compatible con el patrón [Foo} especificado. –

+23

Página relevante aquí: http://msdn.microsoft.com/en-us/library/53b8022e%28VS.71%29.aspx – cbp

+1

¿Alguna forma de utilizar ShouldSerialize con propiedades autogeneradas? es decir, sin variable local. – Jay

1

Lamentablemente, los comportamientos que describe están documentados con precisión como tales en los documentos para XmlElementAttribute.IsNullable.

12

Descubrí una solución utilizando dos propiedades. Un int? propiedad con un atributo XmlIgnore y una propiedad de objeto que se serializa.

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

Esta solución es excelente, ya que también permite codificar NULL como valor de "marcador de posición" para los clientes, que no reconocen NULL en las entradas, es decir, Flex. –

6

Wow gracias esta pregunta/respuesta realmente me ayudó. I corazón Stackoverflow.

Hice lo que está haciendo por encima de un poco más genérico. Todo lo que realmente estamos buscando es tener Nullable con un comportamiento de serialización ligeramente diferente. Usé Reflector para construir mi propio Nullable, y agregué algunas cosas aquí y allá para hacer que la serialización XML funcione de la manera que queremos. Parece funcionar bastante bien:

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

¿Pierde la capacidad de tener sus miembros como int? y así sucesivamente (tiene que usar Nullable <int> en su lugar), pero aparte de eso, todo el comportamiento permanece igual.

+1

Esto arroja una 'System.ExecutionEngineException' sobre' XmlSerializer.Serialize'. – modiX

23

estoy usando este micro-patrón para implementar la serialización anulable:

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

Esto proporciona la interfaz correcta al usuario sin compromiso y todavía hace lo correcto al serializar.

+1

Dado que SomeValue puede ser nulo ... public double XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } set {SomeValue = value; }} –

+0

XmlSomeValue solo debe ser usado por XmlSerializer, que solo lo tocará cuando XmlSomeValueSpecified sea verdadero (es decir, SomeValue.Value no es nulo. –

+5

Bruto, pero lo logró. +1 – pettys

1

Publicación muy útil ayudó mucho.

Opté por ir con la revisión de Scott al tipo de datos Nullable (Of T), sin embargo, el código publicado todavía serializa el elemento Nullable cuando es nulo, aunque sin el atributo "xs: nil = 'true'".

que necesitaba para forzar el serializador para dejar la etiqueta por completo por lo que simplemente implementado IXmlSerializable en la estructura (esto es en VB, pero se obtiene la imagen):

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

prefiero este método para usar el (foo) Patrón especificado ya que requiere agregar cargas compartidas de propiedades redundantes a mis objetos, mientras que usar el nuevo tipo Nullable solo requiere volver a escribir las propiedades.

Cuestiones relacionadas