So überprüfen Sie, ob ein Objekt in C # serialisierbar ist

94

Ich suche nach einer einfachen Möglichkeit, um zu überprüfen, ob ein Objekt in C # serialisierbar ist.

Wie wir wissen, machen Sie ein Objekt serialisierbar, indem Sie entweder die ISerializable- Schnittstelle implementieren oder [Serializable] an die Spitze der Klasse setzen.

Was ich suche, ist eine schnelle Möglichkeit, dies zu überprüfen, ohne die Klasse widerspiegeln zu müssen, um ihre Attribute zu erhalten. Die Schnittstelle wäre schnell mit einer is- Anweisung.

Mit @ Flards Vorschlag ist dies der Code, den ich mir ausgedacht habe. Schrei, es gibt einen besseren Weg.

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

Oder noch besser, holen Sie sich einfach den Typ des Objekts und verwenden Sie dann die IsSerializable-Eigenschaft für den Typ:

typeof(T).IsSerializable

Denken Sie daran, dass dies nur die Klasse zu sein scheint, mit der wir es zu tun haben, wenn die Klasse andere Klassen enthält, die Sie wahrscheinlich alle überprüfen oder versuchen möchten, zu serialisieren und auf Fehler zu warten, wie @pb hervorhob.

FryHard
quelle
1
Entschuldigung, das schlägt fehl, wenn ein Feld in obj nicht serialisierbar ist. Siehe mein Beispiel.
Paul van Brenk
Ich denke, dies ist ein viel besserer Ansatz: stackoverflow.com/questions/236599/…
xero
Die Aussage "Sie machen ein Objekt serialisierbar, indem Sie entweder die ISerializable-Schnittstelle implementieren oder die [Serializable] oben in der Klasse platzieren" ist falsch. Damit ein Objekt serialisierbar ist, muss seine Klasse das SerializableAttribute deklarieren. Durch die Implementierung von ISerializable haben Sie nur mehr Kontrolle über den Prozess.
Mishax

Antworten:

115

Sie haben ein schönes Grundstück in der TypeKlasse namens IsSerializable.

Leppie
quelle
7
Dies informiert Sie nur, wenn Ihrer Klasse ein Attribut von Serializable zugeordnet ist.
Fatema
37
Sein Punkt ist, dass Mitglieder dieses Objekts möglicherweise nicht serialisierbar sind, obwohl der enthaltende Typ ist. richtig? Ist es nicht so, dass wir die Mitglieder dieses Objekts rekursiv untersuchen und jedes einzelne überprüfen müssen, wenn wir nicht einfach versuchen, es zu serialisieren und festzustellen, ob es fehlschlägt?
Brian Sweeney
3
Zum Beispiel für eine Liste <SomeDTO> ist die IsSerializable wahr, auch wenn SomeDTO NICHT serialisierbar ist
Simon Dowdeswell
43

Sie müssen alle Typen im Diagramm der zu serialisierenden Objekte auf das serialisierbare Attribut überprüfen. Am einfachsten ist es, das Objekt zu serialisieren und die Ausnahme abzufangen. (Aber das ist nicht die sauberste Lösung). Type.IsSerializable und das Überprüfen des serializalbe-Attributs berücksichtigen das Diagramm nicht.

Stichprobe

[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
quelle
Wenn die Kosten nicht zu hoch sind, halte ich diesen Ansatz für den besten. Es kann verschiedene Serialisierungsanforderungen (binär, xml) überprüfen. Ein Objekt verfügt möglicherweise auch über ein generisches Mitglied, das gegen geerbte Klassentypen ausgetauscht werden kann, die die Serialisierung unterbrechen und sich zur Laufzeit ändern können. In List (Of baseclass) können Elemente der Unterklasse A hinzugefügt werden, die nicht serialisierbar sind, wobei baseclass und subclassB serialisierbar sind.
VoteCoffee
Diese Antwort verwendet das Klonen, um zu überprüfen, ob die Serialisierung einen Roundtrip ausführen kann. In einigen Fällen kann es jedoch zu einem Overkill kommen, wenn durch die Serialisierung einige Mitglieder nicht erwartet werden: stackoverflow.com/questions/236599/…
VoteCoffee
18

Dies ist eine alte Frage, die möglicherweise für .NET 3.5+ aktualisiert werden muss. Type.IsSerializable kann tatsächlich false zurückgeben, wenn die Klasse das DataContract-Attribut verwendet. Hier ist ein Ausschnitt, den ich benutze, wenn es stinkt, lass es mich wissen :)

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

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

}
Mike_G
quelle
1
Alte Frage und alte Antworten, aber das ist SEHR wahr! Type.IsSerializable ist nur eine teilweise funktionale Lösung. Angesichts der Tatsache, wie viele heutzutage WCF und DataContracts verwenden, ist dies eine sehr schlechte Lösung!
Jaxidian
Was ist, wenn obj als null eingeht?
N73k
@ N73k nullüberprüfen und zurückgeben, wenn true?
FredM
9

Verwenden Sie Type.IsSerializable, wie andere darauf hingewiesen haben.

Es lohnt sich wahrscheinlich nicht zu versuchen, zu reflektieren und zu überprüfen, ob alle Elemente im Objektdiagramm serialisierbar sind.

Ein Mitglied könnte als serialisierbarer Typ deklariert werden, aber tatsächlich als abgeleiteter Typ instanziiert werden, der nicht serialisierbar ist, wie im folgenden erfundenen Beispiel:

[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

Selbst wenn Sie feststellen, dass eine bestimmte Instanz Ihres Typs serialisierbar ist, können Sie daher im Allgemeinen nicht sicher sein, dass dies für alle Instanzen gilt.

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

Vermutlich beinhaltet Reflexion unter Wasser, aber der einfachste Weg?

Grad van Horck
quelle
5

Hier ist eine 3.5-Variante, die es allen Klassen mithilfe einer Erweiterungsmethode zur Verfügung stellt.

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

Ich habe die Antwort auf diese Frage und die Antwort hier genommen und sie so geändert, dass Sie eine Liste der Typen erhalten, die nicht serialisierbar sind. Auf diese Weise können Sie leicht erkennen, welche Sie markieren müssen.

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

Und dann nennst du es ...

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

Wenn es ausgeführt wird, hat nonSerializableTypes die Liste. Es gibt möglicherweise einen besseren Weg, dies zu tun, als eine leere Liste an die rekursive Methode zu übergeben. Jemand korrigiert mich, wenn ja.

Kanalisationle
quelle
0

Das Ausnahmeobjekt ist möglicherweise serialisierbar, verwendet jedoch eine andere Ausnahme, die dies nicht ist. Dies ist, was ich gerade mit WCF System.ServiceModel.FaultException hatte: FaultException ist serialisierbar, ExceptionDetail jedoch nicht!

Also benutze ich folgendes:

// 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
quelle
0

Meine Lösung in VB.NET:

Für Objekte:

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

Für Typen:

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