65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 隔离存储中的二进制序列化 - BinaryFormatter 与 sharpSerializer 对比

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (7投票s)

2010年10月6日

CPOL

5分钟阅读

viewsIcon

132782

downloadIcon

820

如何在 Silverlight 客户端的 IsolatedStorage 中序列化二进制数据,因为 Silverlight 中没有 BinaryFormatter?为什么不使用 sharpSerializer? - 一个开源的 .NET 和 Silverlight 的 XML 和二进制序列化器

引言

默认情况下,Silverlight 应用程序在其隔离存储中只有 1MB 的可用空间来存储本地数据。如果作为“浏览器外”窗口启动,此金额会增加到 25MB,即使应用程序没有提升权限。您可以通过在 Silverlight 程序上按鼠标右键,然后转到“应用程序存储”注册表来检查可用内存量。

不幸的是,有时需要更多的存储空间。例如 - 公文包模型 - 销售代理从服务器下载数据,在本地与客户一起处理数据,最后将数据同步到服务器。

您始终可以使用 IsolatedStorageFile 类的 IncreaseQuotaTo() 方法为应用程序请求更多可用空间,如以下文章所述:

但是,仅仅增加存储空间大小并不是最优的。比序列化到 XML 更有效的解决方案是更有效地保存数据。

背景

DataContractSerializer 是 Silverlight 客户端序列化业务对象的标准方法。但它会序列化为 XML,而 XML 以浪费数据而闻名。更好的选择是二进制序列化。但有一个问题 - Silverlight 中根本没有 BinaryFormatter。 :-(

需要第三方软件。让我们以 sharpSerializer 为例 - 一个开源的 .NET、.NET Compact 和 Silverlight 的 XML 和二进制序列化器 - 并将其与完整 .NET Framework 中的 BinaryFormatter 进行比较。

BinaryFormatter 与 sharpSerializer 对比

正如我之前所说 - Silverlight 中没有 BinaryFormatter。为了获得一些结果,我检查了完整版 .NET Framework 的 BinaryFormatter 的序列化速度和输出文件大小,并将这些值与 sharpSerializer v.2.6 的结果进行了比较。

序列化的是一个由 10,000 个 RootContainer 类型元素组成的数组 - 这是 sharpSerializerHelloWorldDemo 中一个非常复杂的示例类。我不得不将 RootContainer 及其所有子类标记为 [Serializable],因为 BinaryFormatter 的工作需要这样做。顺便说一句,SharpSerializer 不需要对序列化对象进行任何额外属性,也不需要在其 Serialize() 方法中进行类型定义,就像 XmlSerializer 需要的那样。

下面是 RootContainer 的主要部分。要查看其所有子类,请参阅 sharpSerializer源代码(还有另一篇文章描述了如何将像 RootContainer 这样的奇怪类序列化为 XML - 使用 sharpSerializer .NET 进行通用字典、多维数组和继承类型的 XML 序列化)。

// This is an example from the http://www.sharpserializer.com
// Please download the full source code with HelloWorld demo to see more details.
[Serializable]
public class RootContainer
{
    public SByte SimpleSByte { get; set; }
    public int SimpleInt { get; set; }
    public Single SimpleSingle { get; set; }
    public double SimpleDouble { get; set; }
    public DateTime SimpleDateTime { get; set; }
    public TimeSpan SimpleTimeSpan { get; set; }

    /// Every enumeration is simple type
    public SimpleEnum SimpleEnum { get; set; }

    /// Enumeration with FlagsAttribute is SimpleType.
    /// It is correct serialized if the result of the flag combination 
    /// has unique int value,
    /// i.e. Flag1 = 2, Flag2 = 4, Flag3 = 8 ...
    public FlagEnum FlagsEnum { get; set; }

    /// Decimal is 16 bytes long
    public decimal SimpleDecimal { get; set; }
    public string SimpleString { get; set; }
    public string EmptyString { get; set; }

    /// Structures are handled as objects during serialization
    /// They are serialized as ComplexProperty
    public AdvancedStruct AdvancedStruct { get; set; }

    /// One dimensional array of simple type.
    /// It is serialized as SingleDimensionalArrayProperty
    public string[] SingleArray { get; set; }

    /// Multidimensional array of simple type.
    /// Is is serialized as MultiDimensionalArrayProperty
    public string[,] DoubleArray { get; set; }

    /// Single array of derived objects.
    /// This is polymorphic collection - Items derive from the interface
    public IComplexObject[] PolymorphicSingleArray { get; set; }

    /// Generic list is serialized as a collection.
    /// It is serialized as CollectionProperty
    public IList<string> GenericList { get; set; }

    /// Generic Dictionary of simple types.
    /// Is is serialized as DictionaryProperty
    public IDictionary<int, string> GenericDictionary { get; set; }

    /// Generic dictionary where values are inherited from the value type
    public IDictionary<int, 
	IComplexObject> GenericDictionaryOfPolymorphicValues { get; set; }

    /// Polymorphic property. Object instance derives from the property type
    /// Is serialized as ComplexProperty
    public IComplexObject ComplexObject { get; set; }

    /// Collection where item values are
    /// derived from the collection item type
    public ComplexObjectPolymorphicCollection ComplexObjectCollection { get; set; }

    /// Dictionary where values are derived
    /// from the predefined dictionary value type
    public ComplexObjectPolymorphicDictionary ComplexObjectDictionary { get; set; }

    /// List items are derived from the generic attribute.
    /// This is polymorphic attribute.
    public IList<IComplexObject> GenericListOfComplexObjects { get; set; }

    /// Generic object with polymorphic attribute.
    /// It is serialized as ComplexProperty
    public GenericObject<IComplexObject> GenericObjectOfComplexObject { get; set; }

    /// Multidimensional array of generic object with polymorphic attribute
    public GenericObject<IComplexObject>[,] 
	MultiArrayOfGenericObjectWithPolymorphicArgument { get; set; }

    /// Array of objects where every item can be of other type
    /// It is serialized as SingleDimensionalArrayProperty
    public object[] SingleArrayOfObjects { get; set; }
}

为了测试目的,我制作了一个 小型基准测试控制台程序 来测量序列化和反序列化时间,并将序列化结果保存为文件 - 以比较它们的大小。

Main”方法生成 10,000 个 RootContainer 元素并调用单个测试方法。

static void Main(string[] args)
{
    // create 10.000 items
    RootContainer[] containerArray = createContainerArray(10000);

    // Serialization tests
    Console.WriteLine("Serializing");
    serializeWithBinaryFormatter(containerArray, "BinaryFormatter.bin");
    serializeWithSharpSerializer(containerArray, 
	BinarySerializationMode.Burst, "sharpSerializerBurst.bin");
    serializeWithSharpSerializer(containerArray, 
	BinarySerializationMode.SizeOptimized, "sharpSerializerOptimized.bin");
    serializeWithXmlSharpSerializer(containerArray, "sharpSerializer.xml");

    // Deserialization tests
    Console.WriteLine();
    Console.WriteLine("Deserializing");
    deserializeWithBinaryFormatter("BinaryFormatter.bin");
    deserializeWithSharpSerializer(BinarySerializationMode.Burst, 
				"sharpSerializerBurst.bin");
    deserializeWithSharpSerializer(BinarySerializationMode.SizeOptimized, 
				"sharpSerializerOptimized.bin");
    deserializeWithXmlSharpSerializer("sharpSerializer.xml");
}

以下方法使用 BinaryFormatter 进行序列化。注意!Stopwatch 是 .NET 中测量时间的最佳类。

private static void serializeWithBinaryFormatter
	(RootContainer[] containerArray, string shortFilename)
{
    // convert short filename to full filename
    string filename = getFilename(shortFilename);

    // creating a stream, as BinaryFormatter can not serialize directly to a file
    using (var stream = new FileStream(filename, FileMode.Create))
    {
        var formatter = new BinaryFormatter();

        // Stopwatch as a stopper is enough accurate
        Stopwatch watch = new Stopwatch();
        Console.WriteLine("Starting serialization with BinaryFormatter");
        watch.Start();

        // Serializing
        formatter.Serialize(stream, containerArray);

        watch.Stop();
        Console.WriteLine(string.Format
		("Stopped after {0}ms", watch.ElapsedMilliseconds));
    }
}

为了使用 sharpSerializer 序列化测试对象,我创建了以下方法。顺便说一句,SharpSerializer 的语法比 BinaryFormatter 容易得多。

private static void serializeWithSharpSerializer
    (RootContainer[] containerArray, BinarySerializationMode mode, string shortFilename)
{
    string filename = getFilename(shortFilename);

    // create sharpSerializer with an overloaded constructor
    var serializer = new SharpSerializer(mode)

    Stopwatch watch = new Stopwatch();
    Console.WriteLine(string.Format("Starting serialization with SharpSerializer ({0})", 
		Enum.GetName(typeof(BinarySerializationMode), mode)));
    watch.Start();

    // Serializing
    serializer.Serialize(containerArray, filename);

    watch.Stop();
    Console.WriteLine(string.Format("Stopped after {0}ms", watch.ElapsedMilliseconds));
}

程序屏幕上的序列化和反序列化时间

Serialization and deserialization times

序列化和反序列化结果表格(对于序列化其他对象,结果及其比例可能会有所不同)

序列化器 序列化时间 [秒] 文件大小 [MB] 反序列化时间 [秒]
.NET BinaryFormatter 2.1 19.3 7.9
sharpSerializer - 二进制 (Burst) 3.1 3.8 3.7
sharpSerializer - 二进制 (SizeOptimized) 3.8 8.5 2.6
sharpSerializer - XML 3.7 86.0 17.6

最快的序列化时间是 2.1 秒,由 BinaryFormatter 提供,但其文件大小为 19.3MB,是 sharpSerializer 在其二进制 SizeOptimized 模式下产生的两倍(您可以在项目页面的 教程 中找到更多关于 sharpSerializer 二进制模式的信息)。

BinaryFormatter 的反序列化时间是 sharpSerializer 的最佳时间 2.6 秒的 3 倍多。

结果分析

服务器端

在可以使用完整 .NET Framework 的地方,您可以在 BinaryFormattersharpSerializer 之间进行选择来进行二进制序列化。如果您只写入数据一次,然后多次读取(例如照片库或 MP3 音乐商店)- 那么 sharpSerializer 比内置的 BinaryFormatter 是更好的选择。它比 BinaryFormatter 慢 80%,但读取速度快 300%。更不用说文件大小,使用 sharpSerializer 序列化时文件大小会减小 50%。

Silverlight 客户端侧

在 Silverlight 中,如果您想使用二进制序列化,别无选择。由于 Silverlight 中没有 BinaryFormatter,唯一的解决方案是 sharpSerializer

sharpSerializer 的优势

  • 语法非常简单
  • 无需用额外属性标记序列化对象
  • 无需在 Serialize() 方法中声明序列化类型
  • 生成的文件大小很小
  • 对象反序列化速度快
  • .NET Full、.NET Compact 和 Silverlight 之间的通用算法和序列化兼容性
  • 开源且免费用于任何目的

sharpSerializer 的缺点

  • 只序列化 public 属性
  • 只能反序列化具有标准构造函数的对象
  • XML 和二进制格式与其他 .NET 标准不兼容

结论

除了许多优点和良好的基准测试之外,sharpSerializer 也有一些缺点。这取决于具体情况,这些缺点是否是绊脚石。

由于我主要使用 sharpSerializer 来存储我自己的业务对象,所以为每个对象提供标准构造函数对我来说不是问题。使用仅 public 属性来存储对象的状态也没有问题。

如果需要序列化复杂的三方类,应该使用适配器模式将其转换为简单的类。适配器模式在大多数情况下可以简化输出并减小其大小,因为只序列化了需要的属性。

sharpSerializer 已不再是 Beta 版,当前版本是 2.6。它稳定可靠。

sharpSerializer 未来里程碑之一是数据压缩。实际上,在为 Silverlight 找到 DeflateStream 的开源替代品方面存在问题,因为 GZipStreamDeflateStream 在 Silverlight 4 及更早版本中不受支持。实现数据压缩后,sharpSerializer 的优势列表可以扩展为“生成的文件大小非常非常小”。 :-)

来自作者

还有另一篇关于使用 sharpSerializer 进行 XML 序列化的文章

当然,鼓励您自己对 sharpSerializer Framework 进行测试。如果您喜欢这篇文章,请评分,如果不喜欢 - 请在下面的评论区告诉我原因。 ;-)

历史

  • 2010 年 10 月 6 日 - 首次发布
© . All rights reserved.