Jak sprawdzić, czy obiekt można serializować w C #

94

Szukam łatwego sposobu sprawdzenia, czy obiekt w C # jest możliwy do serializacji.

Jak wiemy, obiekt nadaje się do serializacji poprzez implementację interfejsu ISerializable lub umieszczenie [Serializable] na szczycie klasy.

To, czego szukam, to szybki sposób sprawdzenia tego bez konieczności odzwierciedlania klasy, aby uzyskać jej atrybuty. Interfejs byłby szybki przy użyciu instrukcji is .

Korzystając z sugestii @ Flarda, oto kod, który wymyśliłem, krzyk, czy jest lepszy sposób.

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

Lub jeszcze lepiej po prostu pobierz typ obiektu, a następnie użyj właściwości IsSerializable na typie:

typeof(T).IsSerializable

Pamiętaj jednak, że wydaje się, że jest to tylko klasa, z którą mamy do czynienia, jeśli klasa zawiera inne klasy, prawdopodobnie chcesz je wszystkie sprawdzić lub spróbować serializować i poczekać na błędy, jak wskazał @pb.

FryHard
źródło
1
Przepraszamy, ale kończy się niepowodzeniem, gdy pola w obj nie można serializować, zobacz mój przykład.
Paul van Brenk,
Myślę, że jest to o wiele lepsze podejście: stackoverflow.com/questions/236599/...
xero
Instrukcja „sprawisz, że obiekt będzie możliwy do serializacji poprzez zaimplementowanie interfejsu ISerializable lub umieszczenie elementu [Serializable] na początku klasy” jest fałszywe. Aby obiekt był możliwy do serializacji, jego klasa musi zadeklarować SerializableAttribute. Wdrożenie ISerializable zapewnia tylko większą kontrolę nad procesem.
Mishax

Odpowiedzi:

115

Masz cudowną posiadłość w Typeklasie o nazwie IsSerializable.

leppie
źródło
7
Po prostu poinformuje Cię, czy atrybut Serializable jest dołączony do Twojej klasy.
Fatema,
37
chodzi o to, że elementy członkowskie tego obiektu mogą nie być serializowane, nawet jeśli typem zawierającym jest. dobrze? czy nie jest tak, że musimy rekurencyjnie drążyć składowe tych obiektów i sprawdzać każdy z nich, jeśli nie tylko próbować serializować go i sprawdzić, czy się nie powiedzie?
Brian Sweeney
3
Na przykład dla List <SomeDTO> IsSerializable jest prawdziwe, nawet jeśli SomeDTO NIE jest serializowalny
Simon Dowdeswell
43

Będziesz musiał sprawdzić wszystkie typy na wykresie serializowanych obiektów dla atrybutu możliwego do serializacji. Najłatwiejszym sposobem jest próba serializacji obiektu i przechwycenie wyjątku. (Ale to nie jest najczystsze rozwiązanie). Type.IsSerializable i sprawdzanie atrybutu serializalbe nie bierze pod uwagę wykresu.

Próba

[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
źródło
Jeśli koszt nie jest zbyt duży, myślę, że takie podejście jest najlepsze. Może sprawdzać różne wymagania dotyczące serializacji (binarne, xml). Ponadto obiekt może mieć ogólny element członkowski, który można zamienić na dziedziczone typy klas, które mogą przerwać serializację i zmienić się w czasie wykonywania. Lista (klasy bazowej) może mieć dodane elementy z podklasy A, której nie można serializować, gdzie klasy bazowej i podklasy B można serializować.
VoteCoffee
Ta odpowiedź używa klonowania, aby sprawdzić, czy serializacja może działać w obie strony. W niektórych przypadkach może to być przesada, chociaż nie oczekuje się, że serializacja ustawi niektórych członków: stackoverflow.com/questions/236599/…
VoteCoffee
18

To stare pytanie, które może wymagać aktualizacji dla .NET 3.5+. Type.IsSerializable może faktycznie zwrócić false, jeśli klasa używa atrybutu DataContract. Oto fragment, którego używam, jeśli śmierdzi, daj mi znać :)

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

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

}
Mike_G
źródło
1
Stare pytanie i stare odpowiedzi, ale to BARDZO prawda! Type.IsSerializable to tylko częściowo funkcjonalne rozwiązanie. W rzeczywistości, biorąc pod uwagę, jak wielu korzysta obecnie z usług WCF i DataContracts, jest to w rzeczywistości bardzo słabe rozwiązanie!
Jaxidian
A co, jeśli obj ma wartość null?
N73k
@ N73k nullsprawdź i wróć, jeśli true?
FredM
9

Użyj Type.IsSerializable, jak wskazali inni.

Prawdopodobnie nie warto próbować odzwierciedlać i sprawdzać, czy wszystkie elementy składowe w grafie obiektów są możliwe do serializacji.

Element członkowski może być zadeklarowany jako typ możliwy do serializacji, ale w rzeczywistości może zostać utworzony jako typ pochodny, którego nie można serializować, jak w następującym wymyślonym przykładzie:

[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

Dlatego nawet jeśli ustalisz, że określone wystąpienie Twojego typu jest możliwe do serializacji, nie możesz ogólnie mieć pewności, że będzie to prawdą dla wszystkich wystąpień.

Joe
źródło
6
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));

Prawdopodobnie wymaga odbicia pod wodą, ale najprostszy sposób?

Grad van Horck
źródło
5

Oto odmiana 3,5, która udostępnia ją wszystkim klasom przy użyciu metody rozszerzającej.

public static bool IsSerializable(this object obj)
{
    if (obj is ISerializable)
        return true;
    return Attribute.IsDefined(obj.GetType(), typeof(SerializableAttribute));
}
Michael Meadows
źródło
2

Wziąłem odpowiedź na to pytanie i odpowiedź tutaj i zmodyfikowałem ją, aby uzyskać listę typów, których nie można serializować. W ten sposób możesz łatwo wiedzieć, które z nich zaznaczyć.

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

A potem nazywasz to ...

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

Po uruchomieniu nonSerializableTypes będzie mieć listę. Może być lepszy sposób na zrobienie tego niż przekazanie pustej listy do metody rekurencyjnej. Niech ktoś mnie poprawi, jeśli tak.

kanalizacja
źródło
0

Obiekt wyjątku może być możliwy do serializacji, ale przy użyciu innego wyjątku, który nie jest. Właśnie to miałem z WCF System.ServiceModel.FaultException: FaultException można serializować, ale ExceptionDetail nie jest!

Więc używam następujących:

// 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
źródło
0

Moje rozwiązanie w VB.NET:

W przypadku obiektów:

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

Dla typów:

''' <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
źródło