适用于 .NET 的扩展 XML 序列化器






4.81/5 (26投票s)
适用于 .NET 4 和 .NET CORE 的扩展 XML 序列化器,支持 WebApi 和 ASP Core 集成。
引言
适用于 .NET 4 和 .NET CORE 的扩展 XML 序列化器。
支持平台
- .NET 4.5
- .NET Platform Standard 1.6
支持功能
- 反序列化标准
XMLSerializer
的 xml - 序列化类、结构体、泛型类、原始类型、泛型列表和字典、数组、枚举
- 序列化带有属性接口的类
- 序列化循环引用和引用 ID
- 反序列化旧版本 xml
- 属性加密
- 自定义序列化器
- 支持
XmlElementAttribute
和XmlRootAttribute
- POCO - 所有配置(迁移、自定义序列化器...)都位于类之外
标准 .NET XML 序列化器功能非常有限
- 不支持带有循环引用或接口属性的类的序列化。
- 没有读取旧版本 XML 的机制。
- 不支持用接口类型定义的属性。
- 不支持只读集合属性(如 Xaml 所支持的)。
- 不支持带参数的构造函数。
- 不支持私有构造函数。
- 如果您想创建自定义序列化器,您的类必须继承自
IXmlSerializable
。这意味着您的类将不是 POCO 类。 - 不支持 IoC
基础知识
在 ExtendedXmlSerializer
中,一切都始于一个配置容器,您可以使用它来配置序列化器并最终创建它。
var serializer = new ConfigurationContainer()
// Configure...
.Create();
使用这个简单的示例类
public sealed class Subject
{
public string Message { get; set; }
public int Count { get; set; }
}
默认序列化器的结果如下所示
<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<Count>6776</Count>
</Subject>
我们可以更进一步,通过配置 Subject
的 Type 和 Member 属性来影响其 Xml 的生成方式。以下示例将 Subject
的名称配置为生成 ModifiedSubject
var serializer = new ConfigurationContainer().ConfigureType<Subject>()
.Name("ModifiedSubject")
.Create();
<?xml version="1.0" encoding="utf-8"?>
<ModifiedSubject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<Count>6776</Count>
</ModifiedSubject>
进一步深入,我们还可以配置类型的成员信息。例如,将 Subject.Message
配置为生成 Text
而不是原来的名字
var serializer = new ConfigurationContainer().ConfigureType<Subject>()
.Member(x => x.Message)
.Name("Text")
.Create();
<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples">
<Text>Hello World!</Text>
<Count>6776</Count>
</Subject>
Xml 设置
如果您想通过 XmlWriterSettings
和 XmlReaderSettings
分别配置 XML 的写入和读取设置,您可以通过我们创建的扩展方法来实现。
var subject = new Subject{ Count = 6776, Message = "Hello World!" };
var serializer = new ConfigurationContainer().Create();
var contents = serializer.Serialize(new XmlWriterSettings {Indent = true}, subject);
// ...
以及用于读取的设置
var instance = serializer.Deserialize<Subject>(new XmlReaderSettings{IgnoreWhitespace = false}, contents);
// ...
序列化
现在您的配置容器已经配置完成,并且序列化器也已创建,是时候开始序列化了。
var serializer = new ConfigurationContainer().Create();
var obj = new TestClass();
var xml = serializer.Serialize(obj);
反序列化
var obj2 = serializer.Deserialize<TestClass>(xml);
Fluent API
ExtendedXmlSerializer 使用流式 API 进行配置。示例
var serializer = new ConfigurationContainer()
.UseEncryptionAlgorithm(new CustomEncryption())
.Type<Person>() // Configuration of Person class
.Member(p => p.Password) // First member
.Name("P")
.Encrypt()
.Member(p => p.Name) // Second member
.Name("T")
.Type<TestClass>() // Configuration of another class
.CustomSerializer(new TestClassSerializer())
.Create();
字典的序列化
您可以序列化任何类型的泛型字典。
public class TestClass
{
public Dictionary<int, string> Dictionary { get; set; }
}
var obj = new TestClass
{
Dictionary = new Dictionary<int, string>
{
{1, "First"},
{2, "Second"},
{3, "Other"},
}
};
输出的 XML 将会是这样
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Dictianary;assembly=ExtendedXmlSerializer.Samples">
<Dictionary>
<Item xmlns="https://extendedxmlserializer.github.io/system">
<Key>1</Key>
<Value>First</Value>
</Item>
<Item xmlns="https://extendedxmlserializer.github.io/system">
<Key>2</Key>
<Value>Second</Value>
</Item>
<Item xmlns="https://extendedxmlserializer.github.io/system">
<Key>3</Key>
<Value>Other</Value>
</Item>
</Dictionary>
</TestClass>
如果您使用 UseOptimizedNamespaces 函数,xml 将会是这样
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:sys="https://extendedxmlserializer.github.io/system" xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Dictianary;assembly=ExtendedXmlSerializer.Samples">
<Dictionary>
<sys:Item>
<Key>1</Key>
<Value>First</Value>
</sys:Item>
<sys:Item>
<Key>2</Key>
<Value>Second</Value>
</sys:Item>
<sys:Item>
<Key>3</Key>
<Value>Other</Value>
</sys:Item>
</Dictionary>
</TestClass>
自定义序列化
如果您的类需要以非标准方式进行序列化
public class TestClass
{
public TestClass(string paramStr, int paramInt)
{
PropStr = paramStr;
PropInt = paramInt;
}
public string PropStr { get; private set; }
public int PropInt { get; private set; }
}
您必须创建自定义序列化器
public class TestClassSerializer : IExtendedXmlCustomSerializer<TestClass>
{
public TestClass Deserialize(XElement element)
{
var xElement = element.Member("String");
var xElement1 = element.Member("Int");
if (xElement != null && xElement1 != null)
{
var strValue = xElement.Value;
var intValue = Convert.ToInt32(xElement1.Value);
return new TestClass(strValue, intValue);
}
throw new InvalidOperationException("Invalid xml for class TestClassWithSerializer");
}
public void Serializer(XmlWriter writer, TestClass obj)
{
writer.WriteElementString("String", obj.PropStr);
writer.WriteElementString("Int", obj.PropInt.ToString(CultureInfo.InvariantCulture));
}
}
然后,您必须将自定义序列化器添加到 TestClass 的配置中
var serializer = new ConfigurationContainer().Type<TestClass>()
.CustomSerializer(new TestClassSerializer())
.Create();
反序列化旧版本 xml
在标准的 XMLSerializer
中,如果您更改了模型,就无法反序列化 XML。在 ExtendedXMLSerializer
中,您可以为每个类单独创建迁移器。例如:如果您有一个大类,它使用了小类,而这个小类被更改了,您可以只为这个小类创建一个迁移器。您无需修改整个大 XML。现在我将向您展示一个简单的例子
如果您有一个类
public class TestClass
{
public int Id { get; set; }
public string Type { get; set; }
}
生成的 XML 如下所示
<? xml version="1.0" encoding="utf-8"?>
<TestClass xmlns="clr-namespace:ExtendedXmlSerialization.Samples.MigrationMap;assembly=ExtendedXmlSerializer.Samples">
<Id>1</Id>
<Type>Type</Type>
</TestClass>
然后您重命名了属性
public class TestClass
{
public int Id { get; set; }
public string Name { get; set; }
}
生成的 XML 如下所示
<? xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:version="1" xmlns="clr-namespace:ExtendedXmlSerialization.Samples.MigrationMap;assembly=ExtendedXmlSerializer.Samples">
<Id>1</Id>
<Name>Type</Name>
</TestClass>
然后,您添加了一个新属性,并且想在反序列化时计算新值。
public class TestClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
新的 XML 应该看起来像这样
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:version="2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.MigrationMap;assembly=ExtendedXmlSerializer.Samples">
<Id>1</Id>
<Name>Type</Name>
<Value>Calculated</Value>
</TestClass>
您可以使用迁移来迁移(读取)旧版本的 XML
public class TestClassMigrations : IEnumerable<Action<XElement>>
{
public static void MigrationV0(XElement node)
{
var typeElement = node.Member("Type");
// Add new node
node.Add(new XElement("Name", typeElement.Value));
// Remove old node
typeElement.Remove();
}
public static void MigrationV1(XElement node)
{
// Add new node
node.Add(new XElement("Value", "Calculated"));
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Action<XElement>> GetEnumerator()
{
yield return MigrationV0;
yield return MigrationV1;
}
}
然后,您必须在配置中注册您的 TestClassMigrations
类
var serializer = new ConfigurationContainer().ConfigureType<TestClass>()
.AddMigration(new TestClassMigrations())
.Create();
可扩展性
在完成了类型和成员配置之后,我们可以将注意力转移到真正让 ExtendedXmlSeralizer 运转起来的关键:可扩展性。顾名思义,ExtendedXmlSeralizer 提供了一个非常灵活(但毕竟是新的)的扩展模型,您可以在此基础上构建自己的扩展。您在 ExtendedXmlSeralizer 中遇到的几乎所有功能都是通过扩展实现的。本文档的其余部分将展示 ExtendedXmlSerializer 通过其扩展系统实现的主要功能。
对象引用和循环引用
如果您有一个类
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Person Boss { get; set; }
}
public class Company
{
public List<Person> Employees { get; set; }
}
然后您创建了一个具有循环引用的对象,如下所示
var boss = new Person {Id = 1, Name = "John"};
boss.Boss = boss; //himself boss
var worker = new Person {Id = 2, Name = "Oliver"};
worker.Boss = boss;
var obj = new Company
{
Employees = new List<Person>
{
worker,
boss
}
};
您必须将 Person 类配置为引用对象
var serializer = new ConfigurationContainer().ConfigureType<Person>()
.EnableReferences(p => p.Id)
.Create();
输出的 XML 将会是这样
<?xml version="1.0" encoding="utf-8"?>
<Company xmlns="clr-namespace:ExtendedXmlSerializer.Samples.ObjectReference;assembly=ExtendedXmlSerializer.Samples">
<Employees>
<Capacity>4</Capacity>
<Person Id="2">
<Name>Oliver</Name>
<Boss Id="1">
<Name>John</Name>
<Boss xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:entity="1" />
</Boss>
</Person>
<Person xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:entity="1" />
</Employees>
</Company>
属性加密
如果您有一个属性需要加密的类
public class Person
{
public string Name { get; set; }
public string Password { get; set; }
}
您必须实现 IEncryption 接口。例如,这里将展示 Base64 编码,但在实际应用中最好使用更安全的方式,例如 RSA。
public class CustomEncryption : IEncryption
{
public string Parse(string data)
=> Encoding.UTF8.GetString(Convert.FromBase64String(data));
public string Format(string instance)
=> Convert.ToBase64String(Encoding.UTF8.GetBytes(instance));
}
然后,您需要指定哪些属性将被加密,并注册您的 IEncryption 实现。
var serializer = new ConfigurationContainer().UseEncryptionAlgorithm(new CustomEncryption())
.ConfigureType<Person>()
.Member(p => p.Password)
.Encrypt()
.Create();
自定义转换
ExtendedXmlSerializer 在组合和分解对象方面做得相当不错(如果我们自己说的话),但如果您有一个类型需要以特定方式进行序列化,并且该类型可以被分解为一个 string
,那么您可以为其注册一个自定义转换器。
使用以下代码
public sealed class CustomStructConverter : IConverter<CustomStruct>
{
public static CustomStructConverter Default { get; } = new CustomStructConverter();
CustomStructConverter() {}
public bool IsSatisfiedBy(TypeInfo parameter) => typeof(CustomStruct).GetTypeInfo()
.IsAssignableFrom(parameter);
public CustomStruct Parse(string data) =>
int.TryParse(data, out var number) ? new CustomStruct(number) : CustomStruct.Default;
public string Format(CustomStruct instance) => instance.Number.ToString();
}
public struct CustomStruct
{
public static CustomStruct Default { get; } = new CustomStruct(6776);
public CustomStruct(int number)
{
Number = number;
}
public int Number { get; }
}
注册转换器
var serializer = new ConfigurationContainer().Register(CustomStructConverter.Default).Create();
var subject = new CustomStruct(123);
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<CustomStruct xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">123</CustomStruct>
优化命名空间
默认情况下,Xml 命名空间是“按需”生成的
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="Object" xmlns="https://extendedxmlserializer.github.io/system">
<Capacity>4</Capacity>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>First</Message>
</Subject>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Second</Message>
</Subject>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Third</Message>
</Subject>
</List>
但只需一次调用 UseOptimizedNamespaces
,命名空间就会放在文档的根部,从而减小文档的大小。
var serializer = new ConfigurationContainer().UseOptimizedNamespaces()
.Create();
var subject = new List<object>
{
new Subject {Message = "First"},
new Subject {Message = "Second"},
new Subject {Message = "Third"}
};
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:ns1="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples" xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="Object" xmlns="https://extendedxmlserializer.github.io/system">
<Capacity>4</Capacity>
<ns1:Subject>
<Message>First</Message>
</ns1:Subject>
<ns1:Subject>
<Message>Second</Message>
</ns1:Subject>
<ns1:Subject>
<Message>Third</Message>
</ns1:Subject>
</List>
隐式命名空间/类型
如果您根本不喜欢命名空间,您可以注册类型,使其在渲染到文档时不会生成命名空间。
var serializer = new ConfigurationContainer().EnableImplicitTyping(typeof(Subject))
.Create();
var subject = new Subject{ Message = "Hello World! No namespaces, yay!" };
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<Subject>
<Message>Hello World! No namespaces, yay!</Message>
</Subject>
自动格式化(属性)
在 XML 文档中生成数据的默认行为是使用元素,这可能会有点冗长和啰嗦。
var serializer = new ConfigurationContainer().UseOptimizedNamespaces()
.Create();
var subject = new List<object>
{
new Subject {Message = "First"},
new Subject {Message = "Second"},
new Subject {Message = "Third"}
};
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<SubjectWithThreeProperties xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Number>123</Number>
<Message>Hello World!</Message>
<Time>2017-11-21T10:55:38.0990077+01:00</Time>
</SubjectWithThreeProperties>
使用 UseAutoFormatting
调用将启用所有已注册 IConverter
(转换为字符串和反之)的类型以属性形式生成。
<?xml version="1.0" encoding="utf-8"?>
<SubjectWithThreeProperties Number="123" Message="Hello World!" Time="2017-11-21T10:55:38.0990077+01:00" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples" />
私有构造函数
经典 XmlSerializer
的一个限制是它不支持私有构造函数,但 ExtendedXmlSerializer
通过其 EnableAllConstructors
调用支持这一点。
public sealed class SubjectByFactory
{
public static SubjectByFactory Create(string message) => new SubjectByFactory(message);
SubjectByFactory() : this(null) {} // Used by serializer.
SubjectByFactory(string message) => Message = message;
public string Message { get; set; }
}
var serializer = new ConfigurationContainer().EnableAllConstructors()
.Create();
var subject = SubjectByFactory.Create("Hello World!");
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<SubjectByFactory xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
</SubjectByFactory>
带参数的成员和内容
将这个概念进一步延伸,就得到了 ExtendedXmlSerlializer
中我们最喜欢的一个功能。经典序列化器只支持无参公共构造函数。使用 ExtendedXmlSerializer
,您可以使用 EnableParameterizedContent
调用来启用构造函数中的带参数参数,这些参数按照约定命名方式与它们要赋值的属性同名。
public sealed class ParameterizedSubject
{
public ParameterizedSubject(string message, int number, DateTime time)
{
Message = message;
Number = number;
Time = time;
}
public string Message { get; }
public int Number { get; }
public DateTime Time { get; }
}
var serializer = new ConfigurationContainer().EnableParameterizedContent()
.Create();
var subject = new ParameterizedSubject("Hello World!", 123, DateTime.Now);
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<ParameterizedSubject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<Number>123</Number>
<Time>2017-11-21T10:55:38.3180296+01:00</Time>
</ParameterizedSubject>
元组
通过启用带参数内容,可以实现许多可能性,例如序列化 Tuples。当然,可序列化的 Tuples 最近才在最新版本的 C# 中引入。在这里,您可以将其与我们的成员命名功能结合起来,为您的 tuple 属性提供更好的命名。
var serializer = new ConfigurationContainer().EnableParameterizedContent()
.Type<Tuple<string>>()
.Member(x => x.Item1)
.Name("Message")
.Create();
var subject = Tuple.Create("Hello World!");
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<Tuple xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="string" xmlns="https://extendedxmlserializer.github.io/system">
<Message>Hello World!</Message>
</Tuple>
实验性 Xaml 特性:附加属性
在 v2 版本中,我们对 ExtendedXmlSerializer
做了一些有趣的改进,增加了对序列化对象图中的对象附加属性的支持。但我们并没有将其限制在继承自 DependencyObject
的对象上,*所有* 对象都可以从中受益。看看这个
sealed class NameProperty : ReferenceProperty<Subject, string>
{
public const string DefaultMessage = "The Name Has Not Been Set";
public static NameProperty Default { get; } = new NameProperty();
NameProperty() : base(() => Default, x => DefaultMessage) {}
}
sealed class NumberProperty : StructureProperty<Subject, int>
{
public const int DefaultValue = 123;
public static NumberProperty Default { get; } = new NumberProperty();
NumberProperty() : base(() => Default, x => DefaultValue) {}
}
var serializer = new ConfigurationContainer().EnableAttachedProperties(NameProperty.Default,
NumberProperty.Default)
.Create();
var subject = new Subject {Message = "Hello World!"};
subject.Set(NameProperty.Default, "Hello World from Attached Properties!");
subject.Set(NumberProperty.Default, 123);
var contents = serializer.Serialize(subject);
// ...
<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<NameProperty.Default>Hello World from Attached Properties!</NameProperty.Default>
<NumberProperty.Default>123</NumberProperty.Default>
</Subject>
(请注意,此功能是实验性的,但请尝试使用并告诉我们您的想法!)
实验性 Xaml 特性:标记扩展
将最棒的功能留到最后,我们对 Xaml 最重要的功能之一——标记扩展——提供了实验性支持。
sealed class Extension : IMarkupExtension
{
const string Message = "Hello World from Markup Extension! Your message is: ", None = "N/A";
readonly string _message;
public Extension() : this(None) {}
public Extension(string message)
{
_message = message;
}
public object ProvideValue(IServiceProvider serviceProvider) => string.Concat(Message, _message);
}
var contents =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<Subject xmlns=""clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples""
Message=""{Extension 'PRETTY COOL HUH!!!'}"" />";
var serializer = new ConfigurationContainer().EnableMarkupExtensions()
.Create();
var subject = serializer.Deserialize<Subject>(contents);
Console.WriteLine(subject.Message); // "Hello World from Markup Extension! Your message is: PRETTY COOL HUH!!!"
(请注意,此功能是实验性的,但请尝试使用并告诉我们您的想法!)
如何从 v1.x 升级到 v2
最后,如果您有 v1 的文档,您需要将它们升级到 v2 才能工作。这包括在 v1 序列化器的实例中读取文档,然后在 v2 序列化器的实例中写入。我们提供了 ExtendedXmlSerializer.Legacy
nuget 包来协助完成此目标。
<?xml version="1.0" encoding="utf-8"?><ArrayOfSubject><Subject type="ExtendedXmlSerializer.Samples.Introduction.Subject"><Message>First</Message><Count>0</Count></Subject><Subject type="ExtendedXmlSerializer.Samples.Introduction.Subject"><Message>Second</Message><Count>0</Count></Subject><Subject type="ExtendedXmlSerializer.Samples.Introduction.Subject"><Message>Third</Message><Count>0</Count></Subject></ArrayOfSubject>
var legacySerializer = new ExtendedXmlSerialization.ExtendedXmlSerializer();
var content = File.ReadAllText(@"bin\Upgrade.Example.v1.xml"); // Path to your legacy xml file.
var subject = legacySerializer.Deserialize<List<Subject>>(content);
// Upgrade:
var serializer = new ConfigurationContainer().Create();
var contents = serializer.Serialize(new XmlWriterSettings {Indent = true}, subject);
File.WriteAllText(@"bin\Upgrade.Example.v2.xml", contents);
// ...
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:ns1="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples" xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="ns1:Subject" xmlns="https://extendedxmlserializer.github.io/system">
<Capacity>4</Capacity>
<ns1:Subject>
<Message>First</Message>
<Count>0</Count>
</ns1:Subject>
<ns1:Subject>
<Message>Second</Message>
<Count>0</Count>
</ns1:Subject>
<ns1:Subject>
<Message>Third</Message>
<Count>0</Count>
</ns1:Subject>
</List>
历史
- 2017-11-14 - v2.0.0 - 重写版本,包含许多新功能。
- 2016-12-06 - v1.5.0
- 支持没有 set 访问器的集合属性。
- 支持
IDictionary
、IList
和ISet
属性。 - 属性类型仅在必要时添加。
- 2016-11-18 - v1.4.1 - 支持排序参数和修复 Object 属性的序列化
- 2016-11-15 - v1.4.0 - 支持
XmlElementAttribute
和XmlRootAttribute
- 2016-10-18 - v1.3.0 - 属性加密
- 2016-09-22 - v1.2.0 - 支持
Dictionary<TKey, TSource>
- 2016-09-14 - v1.1.0 - 与 ASP.NET Core 和 WebApi 集成
- 2016-08-31 - v1.0.0 - 第一个版本
贡献者
- Wojciech Nagórski - 原始版本
- Mike-EEE - v1.5.0 以来的许多改进和 v2.0 的大量工作。