Як перевірити, чи об'єкт серіалізується в C #


94

Я шукаю простий спосіб перевірити, чи об'єкт у C # можна серіалізувати.

Як ми знаємо, ви робите об’єкт серіалізованим, реалізуючи інтерфейс ISerializable або розміщуючи [Serializable] у верхній частині класу.

Я шукаю швидкий спосіб перевірити це без необхідності відображати клас, щоб отримати його атрибути. Інтерфейс був би швидким, використовуючи оператор is .

Використовуючи пропозицію @ Flard, це код, який я придумав, крик, чи є кращий спосіб.

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

Або ще краще просто отримати тип об'єкта, а потім використовувати властивість IsSerializable для типу:

typeof(T).IsSerializable

Пам'ятайте, хоча це здається лише для класу, з яким ми маємо справу, якщо клас містить інші класи, ви, ймовірно, хочете перевірити їх усі або спробувати серіалізувати і чекати помилок, як зазначив @pb.


1
На жаль, це не вдається, коли поле в obj не можна серіалізувати, див. Мій зразок.
Пол ван Бренк,

Я думаю, що це набагато кращий підхід: stackoverflow.com/questions/236599/…
xero

Твердження "ви робите об’єкт серіалізованим, реалізуючи інтерфейс ISerializable або розміщуючи [Serializable] у верхній частині класу", є хибним. Щоб об’єкт можна було серіалізувати, його клас повинен оголосити SerializableAttribute. Впровадження ISerializable лише надає вам більше контролю над процесом.
Мішакс

Відповіді:


115

У вас є чудова власність у Typeкласі під назвою IsSerializable.


7
Це просто повідомить вас, якщо до вашого класу додано атрибут Serializable.
Fatema

37
його суть полягає в тому, що члени цього об'єкта можуть не мати серіалізації, навіть якщо тип вмісту є. правильно? хіба не так ми повинні рекурсивно проаналізувати члени цих об’єктів і перевірити кожного з них, якщо не просто спробувати його серіалізувати і перевірити, чи не вдається?
Brian Sweeney

3
Наприклад, для списку <SomeDTO> IsSerializable є істинним, навіть якщо SomeDTO НЕ можна серіалізувати
Саймон Даудесуелл

43

Вам доведеться перевірити всі типи на графіку об’єктів, що серіалізуються, для атрибута serializable. Найпростіший спосіб - спробувати серіалізувати об’єкт і вловити виняток. (Але це не найчистіше рішення). Type.IsSerializable та перевірка на атрибут serializalbe не враховують графік.

Зразок

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

Якщо вартість не надто велика, я вважаю, що такий підхід є найкращим. Він може перевірити різні вимоги до серіалізації (двійковий файл, xml). Крім того, об'єкт може мати загальний член, який можна поміняти місцями на успадковані типи класів, які можуть порушити серіалізацію і можуть змінитися під час виконання. У список (базового класу) можуть бути додані елементи підкласу A, який не можна серіалізувати, де базовий клас і підклас B можна серіалізувати.
VoteCoffee

Ця відповідь використовує клонування, щоб перевірити, чи може серіалізація відбуватися в обидва кінці. Це може бути зайвим в деяких випадках , хоча були сериализации не очікується , встановити деякі члени: stackoverflow.com/questions/236599 / ...
VoteCoffee

18

Це старе запитання, яке, можливо, доведеться оновити для .NET 3.5+. Type.IsSerializable може фактично повернути false, якщо клас використовує атрибут DataContract. Ось фрагмент, який я використовую, якщо смердить, дайте мені знати :)

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

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

}

1
Старе питання та старі відповіді, але це ДУЖЕ правда! Type.IsSerializable - це лише частково функціональне рішення. Насправді, з огляду на те, скільки в даний час використовують WCF та DataContracts, насправді це дуже погане рішення!
Jaxidian

Що робити, якщо obj входить як null?
N73k,

@ N73k зробити nullперевірку і повернути, якщо true?
FredM

9

Використовуйте Type.IsSerializable, як вказували інші.

Ймовірно, не варто намагатися відобразити та перевірити, чи всі члени в графі об'єктів серіалізуються.

Член може бути оголошений як серіалізуваний тип, але насправді може бути створений як похідний тип, який не можна серіалізувати, як у наступному надуманому прикладі:

[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

Отже, навіть якщо ви визначите, що певний екземпляр вашого типу можна серіалізувати, ви загалом не можете бути впевнені, що це буде справедливо для всіх екземплярів.


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

Можливо, передбачається відображення під водою, але найпростіший спосіб?


5

Ось версія 3.5, яка робить її доступною для всіх класів із використанням методу розширення.

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

2

Я взяв відповідь на це питання і відповідь тут і змінив його, щоб ви отримали Список типів, які не можна серіалізувати. Таким чином, ви легко зможете дізнатись, які з них позначити.

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

І тоді ти називаєш це ...

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

Коли він буде запущений, nonSerializableTypes матиме список. Це може бути кращим способом, ніж передання порожнього списку на рекурсивний метод. Хтось виправте мене, якщо так.


0

Об'єкт винятку може бути серіалізованим, але використовуючи інший виняток, який не є. Це те, що я щойно мав з WCF System.ServiceModel.FaultException: FaultException можна серіалізувати, але ExceptionDetail - ні!

Тому я використовую наступне:

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

0

Моє рішення у VB.NET:

Для об’єктів:

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

Для типів:

''' <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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.