Cách kiểm tra xem một đối tượng có thể tuần tự hóa trong C # hay không


94

Tôi đang tìm một cách dễ dàng để kiểm tra xem một đối tượng trong C # có thể tuần tự hóa hay không.

Như chúng ta biết, bạn làm cho một đối tượng có thể tuần tự hóa bằng cách triển khai giao diện ISerializable hoặc bằng cách đặt [Serializable] ở đầu lớp.

Những gì tôi đang tìm kiếm là một cách nhanh chóng để kiểm tra điều này mà không cần phải phản ánh lớp để lấy các thuộc tính của nó. Giao diện sẽ nhanh chóng bằng cách sử dụng câu lệnh is .

Sử dụng gợi ý của @ Flard, đây là mã mà tôi đã nghĩ ra, hét lên là có cách nào tốt hơn không.

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

Hoặc thậm chí tốt hơn chỉ cần lấy loại của đối tượng và sau đó sử dụng thuộc tính IsSerializable trên loại:

typeof(T).IsSerializable

Hãy nhớ rằng điều này dường như chỉ dành cho lớp mà chúng ta đang xử lý nếu lớp đó chứa các lớp khác mà bạn có thể muốn kiểm tra tất cả hoặc thử tuần tự hóa và chờ lỗi như @pb đã chỉ ra.


1
Rất tiếc, không thành công khi một trường trong obj không thể tuần tự hóa, hãy xem mẫu của tôi.
Paul van Brenk 17/09/08

Tôi nghĩ đây là một cách tiếp cận tốt hơn nhiều: stackoverflow.com/questions/236599/…
xero

Tuyên bố "bạn làm cho một đối tượng có thể tuần tự hóa bằng cách triển khai giao diện ISerializable hoặc bằng cách đặt [Serializable] ở đầu lớp" là sai. Để một đối tượng có thể tuần tự hóa, lớp của nó phải khai báo SerializableAttribute. Việc triển khai ISerializable chỉ giúp bạn kiểm soát nhiều hơn quy trình.
Mishax

Câu trả lời:


115

Bạn có một thuộc tính đáng yêu trên Typelớp được gọi là IsSerializable.


7
Điều này sẽ chỉ cho bạn biết nếu một thuộc tính của Serializable được gắn vào lớp của bạn.
Fatema

37
quan điểm của anh ấy là các thành viên của đối tượng đó có thể không được tuần tự hóa mặc dù kiểu chứa là. đúng? Đó không phải là trường hợp mà chúng ta phải đi sâu vào các thành viên đối tượng đó một cách đệ quy và kiểm tra từng đối tượng, nếu không chỉ cần thử tuần tự hóa nó và xem nó có bị lỗi không?
Brian Sweeney

3
Ví dụ cho một danh sách <SomeDTO> các IsSerializable thậm chí còn đúng nếu SomeDTO là KHÔNG serializable
Simon Dowdeswell

43

Bạn sẽ phải kiểm tra tất cả các loại trong biểu đồ của các đối tượng đang được tuần tự hóa cho thuộc tính có thể tuần tự hóa. Cách dễ nhất là thử tuần tự hóa đối tượng và bắt ngoại lệ. (Nhưng đó không phải là giải pháp sạch nhất). Type.IsSerializable và kiểm tra thuộc tính serializalbe không tính đến biểu đồ.

Mẫu vật

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

Nếu chi phí không quá lớn, tôi nghĩ cách làm này là tốt nhất. Nó có thể kiểm tra các yêu cầu tuần tự hóa khác nhau (nhị phân, xml). Ngoài ra, một đối tượng có thể có một thành viên chung có thể được hoán đổi với các loại lớp kế thừa có thể phá vỡ tuần tự hóa và có thể thay đổi trong thời gian chạy. Danh sách (Trong số baseclass) có thể có thêm các mục của lớp conA mà không thể tuần tự hóa, trong đó baseclass và lớp conB có thể tuần tự hóa.
VoteCoffee

Câu trả lời này sử dụng nhân bản để kiểm tra xem quá trình tuần tự hóa có thể quay vòng. Nó có thể quá mức cần thiết trong một số trường hợp mặc dù quá trình tuần tự hóa không được mong đợi để thiết lập một số thành viên: stackoverflow.com/questions/236599/…
VoteCoffee

18

Đây là một câu hỏi cũ, có thể cần được cập nhật cho .NET 3.5+. Type.IsSerializable thực sự có thể trả về false nếu lớp sử dụng thuộc tính DataContract. Đây là một đoạn tôi sử dụng, nếu nó có mùi hôi, hãy cho tôi biết :)

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

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

}

1
Câu hỏi cũ và câu trả lời cũ nhưng điều này RẤT đúng! Type.IsSerializable chỉ là một giải pháp chức năng một phần. Trên thực tế, với số lượng WCF và DataContracts sử dụng ngày nay, nó thực sự là một giải pháp rất kém!
Jaxidian

Điều gì sẽ xảy ra nếu obj đến là null?
N73k

@ N73k làm nullkiểm tra và trả lại nếu true?
FredM

9

Sử dụng Type.IsSerializable như những người khác đã chỉ ra.

Có lẽ không đáng để cố gắng phản ánh và kiểm tra xem tất cả các thành viên trong biểu đồ đối tượng có thể tuần tự hóa hay không.

Một thành viên có thể được khai báo là một kiểu có thể tuần tự hóa, nhưng trên thực tế được khởi tạo như một kiểu dẫn xuất không thể tuần tự hóa, như trong ví dụ sau:

[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

Do đó, ngay cả khi bạn xác định rằng một phiên bản cụ thể thuộc loại của bạn có thể được tuần tự hóa, thì nói chung, bạn không thể chắc chắn rằng điều này sẽ đúng với tất cả các phiên bản.


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

Có lẽ liên quan đến phản xạ dưới nước, nhưng cách đơn giản nhất?


5

Đây là một biến thể 3.5 cung cấp nó cho tất cả các lớp bằng phương thức mở rộng.

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

2

Tôi đã lấy câu trả lời cho câu hỏi này và câu trả lời ở đây và sửa đổi nó để bạn có được Danh sách các loại không thể tuần tự hóa. Bằng cách đó, bạn có thể dễ dàng biết những cái nào để đánh dấu.

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

Và sau đó bạn gọi nó là ...

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

Khi nó chạy, nonSerializableTypes sẽ có danh sách. Có thể có một cách tốt hơn để thực hiện việc này hơn là chuyển một Danh sách trống sang phương thức đệ quy. Ai đó sửa cho tôi nếu vậy.


0

Đối tượng ngoại lệ có thể được tuần tự hóa, nhưng sử dụng một ngoại lệ khác thì không. Đây là những gì tôi vừa có với WCF System.ServiceModel.FaultException: FaultException có thể tuần tự hóa nhưng ExceptionDetail thì không!

Vì vậy, tôi đang sử dụng như sau:

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

Giải pháp của tôi, trong VB.NET:

Đối với các đối tượng:

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

Đối với các loại:

''' <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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.