使用 sharpSerializer .NET 实现泛型字典、多维数组和继承类型的 XML 序列化






4.92/5 (47投票s)
如何序列化 XML、任何泛型、派生或其他 XMLSerializer 无法序列化的复杂类型
- 下载 sharpSerializer Windows DLL - 285.43 KB
- 下载 sharpSerializer 源代码和 HelloWorld 演示 - 293.55 KB
- 下载最新源代码 (外部链接)
目录
- 引言
- 背景
- Using the Code
- 使用
sharpSerializer
的 HelloWorld - 排除属性不进行序列化
- 自定义用于序列化的属性列表
- 将类型序列化为
AssemblyQualifiedName
或简短类型名称“TypeName
,AssemblyName
” - 自定义
DateTime
和float
值的格式 - 将数据序列化为 XML 以外的其他格式
- 使用
- sharpSerializer 如何工作?
- 高级序列化示例
- 序列化多态属性 (属性值继承自属性类型)
- 序列化泛型 Dictionary,其值继承自接口
- 序列化多维数组
- 序列化不同对象类型的数组和数组的数组 (嵌套数组)
- 序列化其他类型
- sharpSerializer 的限制
- 使用场景
- 下载当前源代码
- 作者注
- 历史
引言
sharpSerializer
是一个适用于 .NET Framework、.NET Compact Framework 和 Silverlight 的开源对象序列化器。它的目的很简单——将对象快速地从 A 传输到 B,无需考虑安全性。在本文中,我将尝试说服您,sharpSerializer
比内置的 XMLSerializer 功能更强大且更简单。
背景
内置的 XmlSerializer
在对象序列化方面存在一些限制
- 它无法序列化多维数组。
- 它无法序列化泛型
Dictionary<TKey,TValue>
。 - 它无法直接序列化多态属性 (属性值继承自属性类型)。
- 它需要在其
Serialize(...)
方法中指定被序列化对象的类型。 - 它需要大量属性来定义业务对象,例如:
XmlArrayAttribute
、XmlArrayItemAttribute
来定义继承类型的数组。
sharpSerializer
克服了上述限制,并且在序列化对象时无需用额外的属性标记您的对象,也无需关心被序列化的类型。
Using the Code
使用 sharpSerializer 的 HelloWorld
假设我们有一个对象
public class SomeObject
{
public int SimpleInt { get; set; }
public DateTime SimpleDateTime { get; set; }
public TimeSpan SimpleTimeSpan { get; set; }
public SimpleEnum SimpleEnum { get; set; }
public string SimpleString { get; set; }
}
public enum SimpleEnum {One,Two,Three}
我们初始化并序列化这个对象
// instantiate the object
var obj = new SomeObject(){
SimpleDateTime = new DateTime(2010,4,28),
SimpleEnum = SimpleEnum.Three,
SimpleInt = 42,
SimpleString = "nothing",
SimpleTimeSpan = new TimeSpan(1,2,3)};
// instantiate the sharpSerializer
// with standard constructor it serializes to XML
// overloaded constructors activate binary serialization
var serializer = new SharpSerializer();
// serialize
serializer.Serialize(obj, "test.xml");
// deserialize (to check the serialization)
var obj2 = serializer.Deserialize("test.xml");
注意!您无需用任何属性标记您的对象,也无需在 Serialize
方法中提供对象类型。
此对象将被序列化为以下 XML
<Complex name="Root" type="SharpSerializerTestApp.SomeObject, SharpSerializerTestApp">
<Properties>
<Simple name="SimpleInt" value="42" />
<Simple name="SimpleDateTime" value="04/28/2010 00:00:00" />
<Simple name="SimpleTimeSpan" value="01:02:03" />
<Simple name="SimpleEnum" value="Three" />
<Simple name="SimpleString" value="nothing" />
</Properties>
</Complex>
如您所见,对象类型被序列化为“TypeName
, AssemblyName
”,DateTime
被序列化为 CultureInfo.InvariantCulture
。稍后,我将向您展示如何自定义类型名称 (例如,作为 AssemblyQualifiedName
) 以及如何以您想要的格式保存 DateTime
和 float
数字。
排除属性不进行序列化
默认情况下,所有 public
、instance
且非只读的属性都会被序列化。所有是数组 (Type.IsArray==true
) 或继承自 IEnumerable
、ICollection
或 IDictionary
的属性也会被序列化。出于性能原因,字段不会被序列化。
- 要排除自定义类型中的属性,您需要用属性标记它们。
ExcludeFromSerializationAttribute
是开箱即用的支持。public class MyClass { [ExcludeFromSerialization] public int SimpleInt { get; set; } }
- 如果您的对象是用常见的 .NET 属性标记的,例如
XmlIgnore
,您可以将这些属性添加到AttributesToIgnore
列表中。// remove default ExcludeFromSerializationAttribute for performance gain serializer.PropertyProvider.AttributesToIgnore.Clear(); serializer.PropertyProvider.AttributesToIgnore.Add(typeof(XmlIgnore));
或使用
settings
类// for binary mode var settings = new SharpSerializerBinarySettings(); // for xml mode var settings = new SharpSerializerXmlSettings(); // remove default ExcludeFromSerializationAttribute for performance gain settings.AdvancedSettings.AttributesToIgnore.Clear(); settings.AdvancedSettings.AttributesToIgnore.Add(typeof(XmlIgnore));
- 要排除内置 .NET 类型中的属性,或者如果您无法用属性扩展属性,可以将类型及其属性名称添加到
SharpSerializer.PropertyProvider.PropertiesToIgnore
列表中。
例如,System.Collections.Generic.List<T>
有一个Capacity
属性,这对序列化来说是不相关的,因此应该被忽略。serializer.PropertyProvider.PropertiesToIgnore.Add (typeof(List<string>), "Capacity");
也可以从
sharpSerializer
的设置中访问PropertiesToIgnore
。// create the settings var settings = new SharpSerializerBinarySettings(); // for binary mode var settings = new SharpSerializerXmlSettings(); // for xml mode settings.AdvancedSettings.PropertiesToIgnore.Add(typeof(List), "Capacity");
自定义用于序列化的属性列表
如果仅使用 ExcludeFromSerializationAttribute
、AttributesToIgnore
或 PropertiesToIgnore
过滤属性还不够,您可以构建自己的 CustomPropertyProvider
。有一个基类 PropertyProvider
,它有两个虚拟方法 GetAllProperties()
和 IgnoreProperty(...)
。可以重写它们来自定义逻辑。
serializer.PropertyProvider = new MyCustomPropertyProvider();
将类型序列化为 AssemblyQualifiedName 或简短类型名称“TypeName, AssemblyName”
在 SharpSerializer v.2.12 之前,所有类型都序列化为简短类型名称“TypeName
, AssemblyName
”;例如:
type="System.String, mscorlib"
这样阅读简单,输出大小也小。但在处理签名程序集或其特定版本时,反序列化会出现问题。
自 SharpSerializer v.2.12 起,所有类型都序列化为 AssemblyQualifiedName
;例如:
type="System.String, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089"
您可以通过修改 sharpSerializer 的设置并设置 IncludeAssemblyVersionInTypeName
、IncludeCultureInTypeName
和 IncludePublicKeyTokenInTypeName
属性来更改类型命名。默认情况下,这些属性设置为 true
。
var settings = new SharpSerializerXmlSettings();
settings.IncludeAssemblyVersionInTypeName = false;
settings.IncludeCultureInTypeName = false;
settings.IncludePublicKeyTokenInTypeName = false;
var serializer = new SharpSerializer(settings);
自定义 DateTime 和 Float 值的格式
默认情况下,所有基本类型和 DateTime
都根据 CultureInfo.InvariantCulture
转换为 string
。如果需要自定义格式或区域设置,可以通过修改设置类来实现:
var settings = new SharpSerializerXmlSettings();
settings.Culture = System.Globalization.CultureInfo.CurrentCulture;
var serializer = new SharpSerializer(settings);
将数据序列化为 XML 以外的其他格式
实际上,sharpSerializer
可以序列化为 XML 和它自己的二进制格式。但是,通过注入自定义的 IXmlWriter
或 IBinaryWriter
,它可以将数据序列化为其他文本格式,如 Json,或其他加密、压缩、优化等二进制流。有关二进制序列化的详细信息,请参阅项目站点。
sharpSerializer 如何工作?
它分三个步骤工作
步骤 1
PropertyFactory
将对象转换为 Property
。abstract
类 Property
包含 PropertyCollection
。PropertyCollection
代表对象结构及其数据。以下类继承自 Property
类
SimpleProperty
用于描述所有基本类型和string
、DateTime
、TimeSpan
以及所有枚举ComplexProperty
用于非列表类的其他类和结构ComplexReferenceProperty
用于引用已被序列化的类 (可以节省空间)SingleDimensionalArrayProperty
用于一维数组MultiDimensionalArrayProperty
用于多维数组CollectionProperty
用于继承自ICollection
但不继承自IDictionary
的列表DictionaryProperty
用于继承自IDictionary
的列表NullProperty
用于所有为null
的对象/字符串 (需要注意它们是null
)
第二步
Property
由 XmlPropertySerializer
序列化。序列化的格式取决于使用的写入器。
步骤 3
对于 XML 序列化,由 DefaultXmlWriter 负责
。但作为接收端,可以使用任何实现了 IXmlWriter
的写入器。这样,您可以编写自己的写入器,将数据序列化为 Json 等其他格式。
在反序列化过程中,过程是相反的。IXmlReader
的一个实例读取数据并将其转发给 XmlPropertyDeserializer
,后者将数据反序列化为 Property
。最后,ObjectFactory
将 Property
转换为对象。
请下载源代码以获取更多详细信息。
高级序列化示例
以下示例是 HelloWorldDemo
应用程序的一部分,您可以与源代码一起下载。
序列化多态属性 (属性值继承自属性类型)
有一个类型为 IComplexObject
的属性。该属性值包含一个继承自 IComplexObject
的类 ComplexObject
。
public IComplexObject ComplexObject { get; set; }
无需输入要序列化的类型。sharpSerializer
可以开箱即用地序列化预期的类型和任何继承类型。
<Complex name="ComplexObject"
type="HalloWorldApp.BusinessObjects.ComplexObject, HalloWorldApp">
<Properties>
<Simple name="SimpleInt" value="33" />
</Properties>
</Complex>
序列化泛型 Dictionary,其值继承自接口
有一个泛型字典,其值是继承自接口 IComplexObject
的某个复杂类 (多态参数)
public IDictionary<int, IComplexObject> GenericDictionary { get; set; }
它被序列化为
<Dictionary name="GenericDictionaryOfPolymorphicValues"
type="System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],
[HalloWorldApp.BusinessObjects.IComplexObject, HalloWorldApp]],
mscorlib" keyType="System.Int32, mscorlib"
valueType="HalloWorldApp.BusinessObjects.IComplexObject, HalloWorldApp">
<Items>
<Item>
<Simple value="2012" />
<Complex type="HalloWorldApp.BusinessObjects.ComplexObject, HalloWorldApp">
<Properties>
<Simple name="SimpleInt" value="2012000" />
</Properties>
</Complex>
</Item>
</Items>
</Dictionary>
如您所见,键和值的类型无关紧要。sharpSerializer
可以序列化基本类型、复杂对象,甚至嵌套列表,例如字典。
序列化多维数组
这是一个二维数组 (可以有更多维度)
public string[,] DoubleArray { get; set; }
它被序列化为
<MultiArray name="DoubleArray" elementType="System.String, mscorlib">
<Dimensions>
<Dimension length="3" />
<Dimension length="2" />
</Dimensions>
<Items>
<Item indexes="0,0">
<Simple value="k1" />
</Item>
<Item indexes="0,1">
<Simple value="k2" />
</Item>
<Item indexes="1,0">
<Simple value="b1" />
</Item>
<Item indexes="1,1">
<Simple value="b2" />
</Item>
<Item indexes="2,0">
<Simple value="z1" />
</Item>
<Item indexes="2,1">
<Simple value="z2" />
</Item>
</Items>
</MultiArray>
数组的类型是什么并不重要。sharpSerializer
可以序列化任何对象的数组、数组的数组、集合的数组或字典的数组。它可以序列化真正深度嵌套的数组。
序列化不同对象类型的数组和数组的数组 (嵌套数组)
这是一个包含以下对象的数组
root.SingleArrayOfObjects = new object[]
{
42,
"nothing to say",
false,
BusinessObjects.SimpleEnum.Three,
null,
new object[]
{
42,
"nothing to say",
false,
BusinessObjects.SimpleEnum.Three,
null
}
};
它被序列化为
<SingleArray name="SingleArrayOfObjects" elementType="System.Object, mscorlib">
<Items>
<Simple type="System.Int32, mscorlib" value="42" />
<Simple type="System.String, mscorlib" value="nothing to say" />
<Simple type="System.Boolean, mscorlib" value="False" />
<Simple type="HalloWorldApp.BusinessObjects.SimpleEnum,
HalloWorldApp" value="Three" />
<Null />
<SingleArray type="System.Object[], mscorlib"
elementType="System.Object, mscorlib">
<Items>
<Simple type="System.Int32, mscorlib" value="42" />
<Simple type="System.String, mscorlib" value="nothing to say" />
<Simple type="System.Boolean, mscorlib" value="False" />
<Simple type="HalloWorldApp.BusinessObjects.SimpleEnum, HalloWorldApp"
value="Three" />
<Null />
</Items>
</SingleArray>
</Items>
</SingleArray>
序列化其他类型
请下载源代码以查看完整的示例以及还可以序列化哪些内容。以下是 HelloWorldDemo
中的一些其他属性
public class RootContainer
{
/// <summary>
/// Structures are handled as objects during serialization
/// They are serialized as ComplexProperty
/// </summary>
public AdvancedStruct AdvancedStruct { get; set; }
/// <summary>
/// Single dimensional array of simple type.
/// It is serialized as SingleDimensionalArrayProperty
/// </summary>
public string[] SingleArray { get; set; }
/// <summary>
/// Multidimensional array of simple type.
/// Is is serialized as MultiDimensionalArrayProperty
/// </summary>
public string[,] DoubleArray { get; set; }
/// <summary>
/// Single array of derived objects.
/// This is polymorphic collection - Items derive from the interface
/// </summary>
public IComplexObject[] PolymorphicSingleArray { get; set; }
/// <summary>
/// Generic list is serialized as a collection.
/// It is serialized as CollectionProperty
/// </summary>
public IList<string> GenericList { get; set; }
/// <summary>
/// Polymorphic property. Object instance derives from the property type
/// Is serialized as ComplexProperty
/// </summary>
public IComplexObject ComplexObject { get; set; }
/// <summary>
/// Collection where item values are
/// derived from the collection item type
/// </summary>
public ComplexObjectPolymorphicCollection ComplexObjectCollection
{ get; set; }
/// <summary>
/// Dictionary where values are derived
/// from the predefined dictionary value type
/// </summary>
public ComplexObjectPolymorphicDictionary ComplexObjectDictionary
{ get; set; }
/// <summary>
/// List items are derived from the generic attribute.
/// This is polymorphic attribute.
/// </summary>
public IList<IComplexObject> GenericListOfComplexObjects { get; set; }
/// <summary>
/// Generic object with polymorphic attribute.
/// It is serialized as ComplexProperty
/// </summary>
public GenericObject<IComplexObject> GenericObjectOfComplexObject
{ get; set; }
/// <summary>
/// Multidimensional array of generic object with polymorphic attribute
/// </summary>
public GenericObject<IComplexObject>[,]
MultiArrayOfGenericObjectWithPolymorphicArgument
{ get; set; }
}
public interface IComplexObject { int SimpleInt { get; set; } }
public class ComplexObject : IComplexObject {public int SimpleInt
{ get; set; }}
public class ComplexObjectPolymorphicCollection :
Collection<IComplexObject>{}
public class ComplexObjectCollection :
Collection<ComplexObject>{}
public class ComplexObjectPolymorphicDictionary :
Dictionary<int, IComplexObject>{}
public class ComplexObjectDictionary :
Dictionary<int, ComplexObject>{}
您认为呢?使用 sharpSerializer
进行 XML 序列化是否足够简单?
sharpSerializer 的限制
在 sharpSerializer
的当前版本中,在序列化和反序列化对象方面存在一些限制。将来,这些限制可能会被消除,但目前它们并没有带来太大的麻烦。它们是:
- 没有
public
标准构造函数的对象无法被反序列化。 对同一复杂对象的多个引用没有进行优化。此类多个引用的对象会被序列化多次,与引用次数相同。(.NET 2.9 及以上版本SharpSerializer
可以优化对同一对象的多个引用的序列化。此类对象只会被序列化一次。这种优化会减小文件大小。)
以下限制适用于 .NET Compact Framework 和 Silverlight 中的序列化
- 数组中的
LowerBound
将始终反序列化为0
,无论它被如何序列化。
数组的 LowerBound
不是 .NET Compact Framework 或 Silverlight 的一部分,因此无法由 sharpSerializer
处理。
使用场景
我开发 sharpSerializer
的主要原因是将应用程序配置保存在 XML 文件中。几年前,我为多态配置而苦恼。我需要一个轻量级的配置存储,支持对象继承。文件应该是易于阅读和手动编辑的。System.Configuration
的开销很大,而且太僵化。
第二个原因是简单——Silverlight 对本地数据存储的支持很差——它应该有一个更好的!:-)
其在 WP7 (Windows Phone 7) 中序列化数据到二进制格式的可能性尤其值得关注。
下载当前源代码
最新的源代码和新闻请访问项目页面:www.sharpserializer.com。
作者笔记
如果您喜欢 sharpSerializer
或本文——请给它评分。如果不喜欢,请在下方留言 ;-)
历史
- 2011-11-09:根据
sharpSerializer
v.2.16 的更改 - 2011-10-24:更新了下载文件
- 2011-07-31:更新了下载文件
SharpSerializer
v.2.12 - 2011-07-28:更新了下载文件
sharpSerializer
v.2.11 - 2011-05-08:根据
sharpSerializer
v.2.9、Guid
序列化、AttributesToIgnore
和对同一对象的多个引用的优化序列化进行的更改 - 2010-10-07:更新了下载文件
- 2010-10-03:根据
sharpSerializer
v.2.0 进行的更改 - 2010-05-05:根据
sharpSerializer
v.1.2 进行的更改 - 2010-05-04:增加了对 .NET Compact Framework 的支持
- 2010-04-30:格式更改 (删除了大型 XML 示例的一些部分)
- 2010-04-29:首次发布