Membuat serial int nullable

92

Saya memiliki kelas dengan int nullable? datatype diatur ke serial sebagai elemen xml. Apakah ada cara untuk mengaturnya sehingga xml serializer tidak akan membuat serial elemen jika nilainya null?

Saya telah mencoba menambahkan atribut [System.Xml.Serialization.XmlElement (IsNullable = false)], tetapi saya mendapatkan pengecualian serialisasi runtime yang mengatakan ada kesalahan yang mencerminkan jenisnya, karena "IsNullable mungkin tidak disetel ke 'salah 'untuk tipe Nullable. Pertimbangkan untuk menggunakan tipe' System.Int32 'atau hapus properti IsNullable dari atribut 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; 
        } 
    }
     ...
}

Kelas di atas akan berseri menjadi:

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

Tetapi untuk ID yang null saya tidak menginginkan elemen ID sama sekali, terutama karena ketika saya menggunakan OPENXML di MSSQL, ia mengembalikan 0, bukan null untuk elemen yang terlihat seperti

Jeremy
sumber

Jawaban:

150

XmlSerializer mendukung ShouldSerialize{Foo}()pola tersebut, sehingga Anda dapat menambahkan metode:

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

Ada juga {Foo}Specifiedpolanya - tidak yakin apakah XmlSerializer mendukungnya.

Marc Gravell
sumber
8
XmlSerializer juga mendukung pola Ditentukan [Foo}.
David Schmitt
23
Halaman Relevent ada di sini: msdn.microsoft.com/en-us/library/53b8022e%28VS.71%29.aspx
cbp
1
Adakah cara untuk menggunakan ShouldSerialize <prop> dengan properti yang dibuat secara otomatis? yaitu tidak ada variabel lokal.
Jay
1
@Jay: Tidak perlu satu. Anda bisa menghubungi HasValueproperti.
Steven Sudit
1
@mark jika, untuk anggota (properti / bidang) FooAnda juga memiliki a public bool FooSpecified {get {...} set {...}}, maka getdigunakan untuk melihat apakah Fooharus diserialisasi, dan setdipanggil saat menetapkan nilai Fooselama deserialisasi.
Marc Gravell
26

Saya menggunakan pola mikro ini untuk mengimplementasikan serialisasi 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; } }

Ini menyediakan antarmuka yang tepat untuk pengguna tanpa kompromi dan masih melakukan hal yang benar saat membuat serial.

David Schmitt
sumber
1
Karena SomeValue mungkin nol ... publik ganda XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } setel {SomeValue = value; }}
Doug Domeny
XmlSomeValue hanya seharusnya digunakan oleh XmlSerializer yang hanya akan menyentuhnya jika XmlSomeValueSpecified benar (yaitu SomeValue.Value bukan null.
David Schmitt
@ pettys: Ini XML, apa yang Anda harapkan? ;-)
David Schmitt
Jawaban yang diterima adalah dari tahun 2008. Yang ini seharusnya yang sekarang. Jawaban menarik terkait dengan Specified vs ShouldSerialize
daniloquio
Pasti harus menjadi jawaban teratas.
tyteen4a03
12

Saya menemukan solusi dengan memanfaatkan dua properti. Sebuah int? properti dengan atribut XmlIgnore dan properti objek yang berseri.

    /// <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
sumber
Solusi ini sangat bagus karena juga memungkinkan NULL dikodekan sebagai nilai "placeholder" untuk klien, yang tidak mengenali NULL dalam int, yaitu Flex.
Kuba Wyrostek
Anda dapat menambahkan [EditorBrowsable (EditorBrowsableState.Never)] ke properti serialisasi xml ke aviod yang melihatnya saat pengkodean
Antonio Rodríguez
6

Wow terima kasih pertanyaan / jawaban ini sangat membantu saya. Saya menyukai Stackoverflow.

Saya membuat apa yang Anda lakukan di atas sedikit lebih umum. Yang benar-benar kami cari adalah memiliki Nullable dengan perilaku serialisasi yang sedikit berbeda. Saya menggunakan Reflector untuk membangun Nullable saya sendiri, dan menambahkan beberapa hal di sana-sini untuk membuat serialisasi XML bekerja seperti yang kita inginkan. Sepertinya bekerja dengan cukup baik:

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

Anda kehilangan kemampuan untuk menjadikan anggota Anda sebagai int? dan seterusnya (sebagai gantinya harus menggunakan Nullable <int>) tetapi selain itu semua perilaku tetap sama.

scobi
sumber
1
Ini melemparkan System.ExecutionEngineExceptionpada XmlSerializer.Serializesaya.
Martin Braun
1

Sayangnya, perilaku yang Anda gambarkan secara akurat didokumentasikan seperti itu dalam dokumen untuk XmlElementAttribute.IsNullable.

Serge Wautier
sumber
1

Posting yang sangat berguna sangat membantu.

Saya memilih untuk menggunakan revisi Scott ke tipe data Nullable (Of T), namun kode yang diposting masih membuat serial elemen Nullable ketika itu adalah Null - meskipun tanpa atribut "xs: nil = 'true'".

Saya perlu memaksa serializer untuk menjatuhkan tag sepenuhnya jadi saya cukup menerapkan IXmlSerializable pada struktur (ini ada di VB tetapi Anda mendapatkan gambarnya):

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

Saya lebih suka metode ini untuk menggunakan pola (foo) Ditentukan karena itu memerlukan penambahan muatan ember properti yang berlebihan ke objek saya, sedangkan menggunakan tipe Nullable baru hanya memerlukan pengetikan ulang properti.

James Close
sumber