Как проверить, сериализуем ли объект в 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.

FryHard
источник
1
К сожалению, это не удается, если поле в obj не сериализуемо, см. Мой образец.
Пол ван Бренк,
Я думаю, что это гораздо лучший подход: stackoverflow.com/questions/236599/…
xero
Утверждение «вы делаете объект сериализуемым, реализуя интерфейс ISerializable или помещая [Serializable] в начало класса», неверно. Чтобы объект был сериализуемым, в его классе должен быть объявлен атрибут SerializableAttribute. Внедрение ISerializable только дает вам больший контроль над процессом.
Мишакс

Ответы:

115

У вас есть прекрасная недвижимость в Typeназванном классе IsSerializable.

леппи
источник
7
Это просто проинформирует вас, прикреплен ли к вашему классу атрибут Serializable.
Fatema
37
его точка зрения состоит в том, что члены этого объекта могут быть не сериализуемыми, даже если содержащий тип. право? Разве это не тот случай, когда мы должны рекурсивно углубляться в элементы этих объектов и проверять каждый из них, если не просто попытаться сериализовать его и посмотреть, не сработает ли он?
Брайан Суини
3
Например, для List <SomeDTO> IsSerializable истинно, даже если SomeDTO НЕ сериализуем
Саймон Доудсвелл
43

Вам нужно будет проверить все типы в графе сериализуемых объектов на предмет сериализуемого атрибута. Самый простой способ - попытаться сериализовать объект и перехватить исключение. (Но это не самое чистое решение). 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). Кроме того, у объекта может быть общий член, который можно менять местами унаследованных типов классов, которые могут нарушать сериализацию и могут изменяться во время выполнения. Список (Of baseclass) может иметь добавленные элементы подкласса 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)

}
Mike_G
источник
1
Старый вопрос и старые ответы, но это ОЧЕНЬ верно! Type.IsSerializable - это частично функциональное решение. Фактически, учитывая, сколько людей используют WCF и DataContracts в наши дни, это на самом деле очень плохое решение!
Jaxidian
Что, если obj имеет значение null?
N73k 02
@ 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
ElektroStudios
источник