位置: 文档库 > C#(.NET) > 文档下载预览

《C#的SerializationException是什么?序列化失败处理.doc》

1. 下载的文档为doc格式,下载后可用word或者wps进行编辑;

2. 将本文以doc文档格式下载到电脑,方便收藏和打印;

3. 下载后的文档,内容与下面显示的完全一致,下载之前请确认下面内容是否您想要的,是否完整.

点击下载文档

C#的SerializationException是什么?序列化失败处理.doc

### C#的SerializationException是什么?序列化失败处理

在C#开发中,序列化(Serialization)是将对象转换为可存储或传输格式(如二进制、JSON、XML)的过程,而反序列化(Deserialization)则是将数据还原为对象的过程。这一机制广泛应用于分布式系统、数据持久化、跨进程通信等场景。然而,当序列化或反序列化过程中出现异常时,系统会抛出`SerializationException`。本文将深入探讨该异常的成因、常见场景及处理策略,帮助开发者高效解决序列化问题。

#### 一、SerializationException的本质

`SerializationException`是.NET框架中定义的一个异常类,继承自`System.Exception`。它表示在序列化或反序列化过程中发生了不可恢复的错误。常见的触发场景包括:

  • 对象包含不可序列化的字段(如未标记`[Serializable]`的类)。
  • 版本不兼容(如类结构修改后未实现`ISerializable`或未定义`SerializationInfo`)。
  • 数据损坏(如文件被截断或网络传输中丢失数据)。
  • 安全限制(如部分托管代码不允许访问特定类型)。

#### 二、常见序列化方式与异常场景

##### 1. 二进制序列化(BinaryFormatter)

二进制序列化通过`BinaryFormatter`将对象转换为字节流,适用于内存中的高效存储。但因其安全性问题(如反序列化漏洞),微软已不建议在不受信环境中使用。

using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 序列化
var person = new Person { Name = "Alice", Age = 30 };
var formatter = new BinaryFormatter();
using (var stream = new FileStream("data.bin", FileMode.Create))
{
    formatter.Serialize(stream, person); // 可能抛出SerializationException
}

异常原因:若`Person`类未标记`[Serializable]`,或包含非序列化字段(如`Thread`对象),会抛出异常。

##### 2. JSON序列化(Newtonsoft.Json/System.Text.Json)

JSON是跨平台数据交换的常用格式。使用`Newtonsoft.Json`或`.NET Core`内置的`System.Text.Json`时,异常可能源于类型不匹配或循环引用。

using Newtonsoft.Json;

public class Order
{
    public int Id { get; set; }
    public Customer Customer { get; set; } // 若Customer为null或不可序列化
}

try
{
    var order = new Order { Id = 1, Customer = null };
    var json = JsonConvert.SerializeObject(order);
}
catch (JsonSerializationException ex) // 继承自SerializationException
{
    Console.WriteLine($"JSON序列化失败: {ex.Message}");
}

##### 3. XML序列化(XmlSerializer)

XML序列化要求类具有无参构造函数,且属性需为可读写。若违反规则,会抛出`InvalidOperationException`(部分场景下包装为`SerializationException`)。

using System.Xml.Serialization;

public class Book
{
    [XmlElement("Title")]
    public string Title { get; private set; } // 只有get会抛出异常
}

var serializer = new XmlSerializer(typeof(Book));
using (var writer = new StringWriter())
{
    try
    {
        serializer.Serialize(writer, new Book());
    }
    catch (InvalidOperationException ex)
    {
        if (ex.InnerException is SerializationException)
        {
            Console.WriteLine("XML序列化失败");
        }
    }
}

#### 三、SerializationException的深层原因与解决方案

##### 1. 不可序列化类型

问题:默认情况下,只有标记`[Serializable]`的类或实现`ISerializable`的接口可被序列化。若类包含非序列化字段(如`Socket`、`FileStream`),会抛出异常。

解决方案

  • 为类添加`[Serializable]`特性。
  • 对非序列化字段标记`[NonSerialized]`。
  • 实现`ISerializable`自定义序列化逻辑。
[Serializable]
public class Account : ISerializable
{
    private decimal balance;
    [NonSerialized] private FileStream logFile; // 不序列化

    public Account(decimal initialBalance)
    {
        balance = initialBalance;
        logFile = new FileStream("log.txt", FileMode.Create);
    }

    // 自定义序列化
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Balance", balance);
        // 不序列化logFile
    }

    // 反序列化构造函数
    protected Account(SerializationInfo info, StreamingContext context)
    {
        balance = info.GetDecimal("Balance");
    }
}

##### 2. 版本兼容性问题

问题:当类结构修改(如新增字段)后,旧版序列化数据无法正确反序列化。

解决方案

  • 使用`[OptionalField]`标记可选字段。
  • 实现`IDeserializationCallback`在反序列化后执行初始化逻辑。
[Serializable]
public class Product : IDeserializationCallback
{
    public string Name { get; set; }
    [OptionalField] // 允许字段缺失
    public decimal Price { get; set; }

    void IDeserializationCallback.OnDeserialization(object sender)
    {
        if (Price == 0) Price = 10.0m; // 默认值
    }
}

##### 3. 数据损坏或格式错误

问题:文件被截断、网络传输错误或手动修改数据导致格式无效。

解决方案

  • 捕获异常并验证数据完整性。
  • 使用校验和或哈希值验证数据。
try
{
    using (var stream = new FileStream("data.bin", FileMode.Open))
    {
        var formatter = new BinaryFormatter();
        var obj = formatter.Deserialize(stream);
    }
}
catch (SerializationException ex)
{
    Console.WriteLine($"数据损坏: {ex.Message}");
    // 尝试从备份恢复
}

##### 4. 安全限制

问题:部分类型(如`SafeHandle`)因安全策略无法序列化。

解决方案:避免序列化敏感类型,或使用替代方案(如存储ID而非对象引用)。

#### 四、高级处理技巧

##### 1. 自定义序列化代理

通过`ISerializable`接口实现复杂对象的序列化控制。

[Serializable]
public class ComplexObject : ISerializable
{
    private string _data;

    public ComplexObject(string data) => _data = data;

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("EncryptedData", Encrypt(_data)); // 加密后存储
    }

    protected ComplexObject(SerializationInfo info, StreamingContext context)
    {
        _data = Decrypt(info.GetString("EncryptedData"));
    }

    private string Encrypt(string data) => ...;
    private string Decrypt(string data) => ...;
}

##### 2. 使用SurrogateSelector处理非序列化类型

为不可序列化的类型提供替代对象。

public class SocketSurrogate : ISerializationSurrogate
{
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        var socket = (Socket)obj;
        info.AddValue("Endpoint", socket.RemoteEndPoint?.ToString());
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        var endpoint = info.GetString("Endpoint");
        // 反序列化时重新创建Socket(实际需更复杂逻辑)
        return new Socket(...);
    }
}

// 注册Surrogate
var surrogateSelector = new SurrogateSelector();
surrogateSelector.AddSurrogate(typeof(Socket), new StreamingContext(StreamingContextStates.All), new SocketSurrogate());
var formatter = new BinaryFormatter { SurrogateSelector = surrogateSelector };

##### 3. 异步序列化与性能优化

对于大对象,使用异步方法避免UI线程阻塞。

using System.IO;
using System.Threading.Tasks;
using System.Text.Json;

public async Task SaveDataAsync(object data, string filePath)
{
    using (var stream = new FileStream(filePath, FileMode.Create))
    {
        await JsonSerializer.SerializeAsync(stream, data);
    }
}

#### 五、最佳实践总结

  1. 明确序列化需求:根据场景选择二进制、JSON或XML。
  2. 标记可序列化类型:使用`[Serializable]`或实现接口。
  3. 处理非序列化字段:通过`[NonSerialized]`或代理模式。
  4. 版本控制:使用`[OptionalField]`和`IDeserializationCallback`。
  5. 异常处理**:捕获`SerializationException`并记录详细错误信息。
  6. 安全审计**:避免反序列化不受信数据,防止漏洞。

#### 六、案例分析:分布式系统中的序列化故障

某电商系统使用二进制序列化传输订单数据,某日出现大量`SerializationException`。调查发现:

  1. 订单类新增了`List`字段但未标记`[OptionalField]`。
  2. 旧版客户端发送的数据缺少该字段,导致反序列化失败。

解决方案

[Serializable]
public class Order
{
    public int Id { get; set; }
    [OptionalField] // 解决版本问题
    public List Logs { get; set; }

    [OnDeserialized] // 反序列化后初始化
    private void OnDeserialized(StreamingContext context)
    {
        Logs ??= new List();
    }
}

#### 七、未来趋势:Serialization的演进

.NET 5+推荐使用`System.Text.Json`替代`BinaryFormatter`,因其更高的性能和安全性。同时,`DataContractSerializer`(WCF常用)和`Protobuf-net`(高效二进制)也是可选方案。

### 关键词

SerializationException、C#序列化、反序列化异常、BinaryFormatter、JSON序列化、XML序列化、ISerializable、版本兼容性、数据损坏、安全限制

### 简介

本文详细解析C#中SerializationException的成因、常见场景及处理策略,涵盖二进制、JSON、XML序列化方式,深入探讨不可序列化类型、版本兼容性、数据损坏等问题的解决方案,并提供自定义序列化、SurrogateSelector等高级技巧,最后总结最佳实践与案例分析。

《C#的SerializationException是什么?序列化失败处理.doc》
将本文以doc文档格式下载到电脑,方便收藏和打印
推荐度:
点击下载文档