使用 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:根据 sharpSerializerv.2.16 的更改
- 2011-10-24:更新了下载文件
- 2011-07-31:更新了下载文件 SharpSerializerv.2.12
- 2011-07-28:更新了下载文件 sharpSerializerv.2.11
- 2011-05-08:根据 sharpSerializerv.2.9、Guid序列化、AttributesToIgnore和对同一对象的多个引用的优化序列化进行的更改
- 2010-10-07:更新了下载文件
- 2010-10-03:根据 sharpSerializerv.2.0 进行的更改
- 2010-05-05:根据 sharpSerializerv.1.2 进行的更改
- 2010-05-04:增加了对 .NET Compact Framework 的支持
- 2010-04-30:格式更改 (删除了大型 XML 示例的一些部分)
- 2010-04-29:首次发布


