Bagaimana cara memeriksa apakah suatu objek dapat diserialkan di C #

94

Saya mencari cara mudah untuk memeriksa apakah suatu objek di C # dapat bersambung.

Seperti yang kita ketahui, Anda membuat objek dapat diserialkan dengan mengimplementasikan antarmuka ISerializable atau dengan menempatkan [Serializable] di bagian atas kelas.

Apa yang saya cari adalah cara cepat untuk memeriksa ini tanpa harus mencerminkan kelas untuk mendapatkan atributnya. Antarmuka akan cepat menggunakan pernyataan is .

Menggunakan saran @ Flard, ini adalah kode yang saya buat, berteriak apakah ada cara yang lebih baik.

private static bool IsSerializable(T obj)
{
    return ((obj is ISerializable) || (Attribute.IsDefined(typeof (T), typeof (SerializableAttribute))));
}

Atau bahkan lebih baik hanya mendapatkan tipe objek dan kemudian menggunakan properti IsSerializable pada tipe:

typeof(T).IsSerializable

Ingat meskipun ini tampaknya hanya kelas yang kita hadapi jika kelas tersebut berisi kelas lain, Anda mungkin ingin memeriksa semuanya atau mencoba dan membuat serial dan menunggu kesalahan seperti yang ditunjukkan @pb.

FryHard
sumber
1
Maaf itu gagal ketika bidang di obj tidak dapat diserialkan, lihat sampel saya.
Paul van Brenk
Saya pikir ini adalah pendekatan yang jauh lebih baik: stackoverflow.com/questions/236599/…
xero
Pernyataan "Anda membuat objek dapat diserialkan dengan mengimplementasikan antarmuka ISerializable atau dengan menempatkan [Serializable] di bagian atas kelas" adalah salah. Untuk sebuah objek menjadi serializable, kelasnya harus mendeklarasikan SerializableAttribute. Menerapkan ISerializable hanya memberi Anda lebih banyak kendali atas proses tersebut.
Mishax

Jawaban:

115

Anda memiliki properti yang indah di Typekelas yang disebut IsSerializable.

leppie
sumber
7
Ini hanya akan memberi tahu Anda jika atribut Serializable dilampirkan ke kelas Anda.
Fatema
37
maksudnya adalah bahwa anggota dari objek itu mungkin tidak dapat diserialkan meskipun tipe yang memuatnya adalah. Baik? bukankah kita harus secara rekursif menelusuri objek itu anggota dan memeriksa masing-masing, jika tidak hanya mencoba untuk membuat serial dan melihat apakah gagal?
Brian Sweeney
3
Misalnya untuk List <SomeDTO> IsSerializable benar meskipun SomeDTO TIDAK dapat diserialkan
Simon Dowdeswell
43

Anda harus memeriksa semua jenis dalam grafik objek yang diserialkan untuk atribut serializable. Cara termudah adalah mencoba membuat serial objek dan menangkap pengecualian. (Tapi itu bukan solusi terbersih). Type.IsSerializable dan memeriksa atribut serializalbe tidak memperhitungkan grafik.

Sampel

[Serializable]
public class A
{
    public B B = new B();
}

public class B
{
   public string a = "b";
}

[Serializable]
public class C
{
    public D D = new D();
}

[Serializable]
public class D
{
    public string d = "D";
}


class Program
{
    static void Main(string[] args)
    {

        var a = typeof(A);

        var aa = new A();

        Console.WriteLine("A: {0}", a.IsSerializable);  // true (WRONG!)

        var c = typeof(C);

        Console.WriteLine("C: {0}", c.IsSerializable); //true

        var form = new BinaryFormatter();
        // throws
        form.Serialize(new MemoryStream(), aa);
    }
}
Paul van Brenk
sumber
Jika biayanya tidak terlalu besar, saya pikir pendekatan ini adalah yang terbaik. Itu dapat memeriksa persyaratan serialisasi yang berbeda (biner, xml). Selain itu, objek mungkin memiliki anggota umum yang dapat ditukar dengan jenis kelas yang diwariskan yang dapat merusak serialisasi dan dapat berubah saat runtime. List (Dari baseclass) dapat memiliki item yang ditambahkan dari subclassA yang tidak dapat diserialkan, di mana baseclass dan subclassB dapat diserialkan.
VoteCoffee
Jawaban ini menggunakan kloning untuk memeriksa apakah serialisasi dapat dilakukan bolak-balik. Ini mungkin berlebihan dalam beberapa kasus meskipun serialisasi tidak diharapkan untuk mengatur beberapa anggota: stackoverflow.com/questions/236599/…
VoteCoffee
18

Ini adalah pertanyaan lama, yang mungkin perlu diperbarui untuk .NET 3.5+. Type.IsSerializable sebenarnya bisa mengembalikan false jika kelas menggunakan atribut DataContract. Ini cuplikan yang saya gunakan, jika bau, beri tahu saya :)

public static bool IsSerializable(this object obj)
{
    Type t = obj.GetType();

     return  Attribute.IsDefined(t, typeof(DataContractAttribute)) || t.IsSerializable || (obj is IXmlSerializable)

}
Mike_G
sumber
1
Pertanyaan lama dan jawaban lama tetapi ini SANGAT benar! Type.IsSerializable hanyalah solusi yang berfungsi sebagian. Faktanya, mengingat banyaknya penggunaan WCF dan DataContracts saat ini, ini sebenarnya adalah solusi yang sangat buruk!
Jaxidian
Bagaimana jika obj masuk sebagai null?
N73k
@ N73k melakukan nullpemeriksaan dan kembali jika true?
FredM
9

Gunakan Type.IsSerializable seperti yang ditunjukkan orang lain.

Mungkin tidak ada gunanya mencoba merefleksikan dan memeriksa apakah semua anggota dalam grafik objek dapat diserialkan.

Anggota dapat dideklarasikan sebagai tipe yang dapat diserialkan, tetapi pada kenyataannya dibuat sebagai tipe turunan yang tidak dapat diserialkan, seperti dalam contoh yang dibuat berikut:

[Serializable]
public class MyClass
{
   public Exception TheException; // serializable
}

public class MyNonSerializableException : Exception
{
...
}

...
MyClass myClass = new MyClass();
myClass.TheException = new MyNonSerializableException();
// myClass now has a non-serializable member

Oleh karena itu, meskipun Anda menentukan bahwa contoh tertentu dari tipe Anda dapat diserialkan, Anda tidak dapat secara umum memastikan ini akan benar untuk semua contoh.

Joe
sumber
6
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));

Mungkin melibatkan refleksi di bawah air, tetapi cara yang paling sederhana?

Grad van Horck
sumber
5

Berikut adalah variasi 3.5 yang membuatnya tersedia untuk semua kelas menggunakan metode ekstensi.

public static bool IsSerializable(this object obj)
{
    if (obj is ISerializable)
        return true;
    return Attribute.IsDefined(obj.GetType(), typeof(SerializableAttribute));
}
Michael Meadows
sumber
2

Saya mengambil jawaban atas pertanyaan ini dan jawabannya di sini dan memodifikasinya sehingga Anda mendapatkan Daftar jenis yang tidak dapat diserialkan. Dengan begitu Anda dapat dengan mudah mengetahui mana yang harus ditandai.

    private static void NonSerializableTypesOfParentType(Type type, List<string> nonSerializableTypes)
    {
        // base case
        if (type.IsValueType || type == typeof(string)) return;

        if (!IsSerializable(type))
            nonSerializableTypes.Add(type.Name);

        foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
        {
            if (propertyInfo.PropertyType.IsGenericType)
            {
                foreach (var genericArgument in propertyInfo.PropertyType.GetGenericArguments())
                {
                    if (genericArgument == type) continue; // base case for circularly referenced properties
                    NonSerializableTypesOfParentType(genericArgument, nonSerializableTypes);
                }
            }
            else if (propertyInfo.GetType() != type) // base case for circularly referenced properties
                NonSerializableTypesOfParentType(propertyInfo.PropertyType, nonSerializableTypes);
        }
    }

    private static bool IsSerializable(Type type)
    {
        return (Attribute.IsDefined(type, typeof(SerializableAttribute)));
        //return ((type is ISerializable) || (Attribute.IsDefined(type, typeof(SerializableAttribute))));
    }

Dan kemudian Anda menyebutnya ...

    List<string> nonSerializableTypes = new List<string>();
    NonSerializableTypesOfParentType(aType, nonSerializableTypes);

Saat dijalankan, nonSerializableTypes akan memiliki daftarnya. Mungkin ada cara yang lebih baik untuk melakukan ini daripada meneruskan List kosong ke metode rekursif. Seseorang mengoreksi saya jika demikian.

saluran pembuangan
sumber
0

Objek pengecualian mungkin dapat diserialkan, tetapi menggunakan pengecualian lain yang tidak. Inilah yang baru saja saya alami dengan WCF System.ServiceModel.FaultException: FaultException dapat diserialkan tetapi ExceptionDetail tidak!

Jadi saya menggunakan yang berikut ini:

// Check if the exception is serializable and also the specific ones if generic
var exceptionType = ex.GetType();
var allSerializable = exceptionType.IsSerializable;
if (exceptionType.IsGenericType)
    {
        Type[] typeArguments = exceptionType.GetGenericArguments();
        allSerializable = typeArguments.Aggregate(allSerializable, (current, tParam) => current & tParam.IsSerializable);
    }
 if (!allSerializable)
    {
        // Create a new Exception for not serializable exceptions!
        ex = new Exception(ex.Message);
    }
Eric
sumber
0

Solusi saya, di VB.NET:

Untuk Objek:

''' <summary>
''' Determines whether an object can be serialized.
''' </summary>
''' <param name="Object">The object.</param>
''' <returns><c>true</c> if object can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsObjectSerializable(ByVal [Object] As Object,
                                      Optional ByVal SerializationFormat As SerializationFormat =
                                                                            SerializationFormat.Xml) As Boolean

    Dim Serializer As Object

    Using fs As New IO.MemoryStream

        Select Case SerializationFormat

            Case Data.SerializationFormat.Binary
                Serializer = New Runtime.Serialization.Formatters.Binary.BinaryFormatter()

            Case Data.SerializationFormat.Xml
                Serializer = New Xml.Serialization.XmlSerializer([Object].GetType)

            Case Else
                Throw New ArgumentException("Invalid SerializationFormat", SerializationFormat)

        End Select

        Try
            Serializer.Serialize(fs, [Object])
            Return True

        Catch ex As InvalidOperationException
            Return False

        End Try

    End Using ' fs As New MemoryStream

End Function

Untuk Jenis:

''' <summary>
''' Determines whether a Type can be serialized.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <returns><c>true</c> if Type can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsTypeSerializable(Of T)() As Boolean

    Return Attribute.IsDefined(GetType(T), GetType(SerializableAttribute))

End Function

''' <summary>
''' Determines whether a Type can be serialized.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="Type">The Type.</param>
''' <returns><c>true</c> if Type can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsTypeSerializable(Of T)(ByVal Type As T) As Boolean

    Return Attribute.IsDefined(GetType(T), GetType(SerializableAttribute))

End Function
ElektroStudios
sumber