Welcome!
========
Welcome to ExtendedXmlSerializer's source repository, our little home on the webternets for ExtendedXmlSerializer. Please have a look around. If you have a question about our serializer, please do not hesitate to post a question in our issues:
https://github.com/wojtpl2/ExtendedXmlSerializer/issues/new/
We will mark it as a question/discussion and talk it through with you using sample code. This process is currently being used to fill in the gaps with our documentation, which could use a little love.
Additionally, please make use of our documentation tag seen here:
https://github.com/wojtpl2/ExtendedXmlSerializer/issues?q=is%3Aissue+label%3ADocumentation
You can see the history of questions asked that we feel could eventually be added to our documentation. We'll eventually get to it, we swear. 😆
Information
===========
Support platforms:
* .NET 4.5
* .NET Standard 2.0
Support features:
* Deserialization xml from standard `XMLSerializer` (mostly, see: https://github.com/wojtpl2/ExtendedXmlSerializer/issues/240)
* Serialization class, struct, generic class, primitive type, generic list and dictionary, array, enum
* Serialization class with property interface
* Serialization circular reference and reference Id
* Deserialization of old version of xml
* Property encryption
* Custom serializer
* Support `XmlElementAttribute`, `XmlRootAttribute`, and `XmlTypeAttribute` for identity resolution.
* Additional attribute support: `XmlIgnoreAttribute`, `XmlAttributeAttribute`, and `XmlEnumAttribute`.
* POCO - all configurations (migrations, custom serializer...) are outside the clas
Standard XML Serializer in .NET is very limited:
* Does not support serialization of class with circular reference or class with interface property.
* There is no mechanism for reading the old version of XML.
* Does not support properties that are defined with interface types.
* Does not support read-only collection properties (like Xaml does).
* Does not support parameterized constructors.
* Does not support private constructors.
* If you want create custom serializer, your class must inherit from `IXmlSerializable`. This means that your class will not be a POCO class.
* Does not support IoC
The Basics
==========
Everything in `ExtendedXmlSerializer` begins with a configuration container, from which you can use to configure the serializer and ultimately create it:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer()
// Configure...
.Create();
Using this simple subject class:
.. sourcecode:: csharp
public sealed class Subject
{
public string Message { get; set; }
public int Count { get; set; }
}
The results of the default serializer will look like this:
.. sourcecode:: xml
Hello World!6776
We can take this a step further by configuring the `Subject`'s Type and Member properties, which will effect how its Xml is emitted. Here is an example of configuring the `Subject`'s name to emit as `ModifiedSubject`:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType()
.Name("ModifiedSubject")
.Create();
.. sourcecode:: xml
Hello World!6776
Diving a bit further, we can also configure the type's member information. For example, configuring `Subject.Message` to emit as `Text` instead:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType()
.Member(x => x.Message)
.Name("Text")
.Create();
.. sourcecode:: xml
Hello World!6776
Xml Settings
============
In case you want to configure the XML write and read settings via `XmlWriterSettings` and `XmlReaderSettings` respectively, you can do that via extension methods created for you to do so:
.. sourcecode:: csharp
Subject subject = new Subject{ Count = 6776, Message = "Hello World!" };
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
string contents = serializer.Serialize(new XmlWriterSettings {Indent = true}, subject);
// ...
And for reading:
.. sourcecode:: csharp
Subject instance = serializer.Deserialize(new XmlReaderSettings{IgnoreWhitespace = false}, contents);
// ...
Serialization
=============
Now that your configuration container has been configured and your serializer has been created, it's time to get to the serialization.
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
TestClass obj = new TestClass();
string xml = serializer.Serialize(obj);
Deserialization
===============
.. sourcecode:: csharp
TestClass obj2 = serializer.Deserialize(xml);
Classic Serialization/Deserialization
=====================================
Most of the code examples that you see in this documentation make use of useful extension methods that make serialization and deserialization a snap with `ExtendedXmlSerializer. However, if you would like to break down into the basic, classical pattern of serialization, and deserialization, this is supported too, as seen by the following examples. Here's serialization:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
TestClass instance = new TestClass();
MemoryStream stream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(stream))
{
serializer.Serialize(writer, instance);
writer.Flush();
}
stream.Seek(0, SeekOrigin.Begin);
string contents = new StreamReader(stream).ReadToEnd();
And here's how to deserialize:
.. sourcecode:: csharp
TestClass deserialized;
MemoryStream contentStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
using (XmlReader reader = XmlReader.Create(contentStream))
{
deserialized = (TestClass)serializer.Deserialize(reader);
}
Serialization/Deserialization with Settings Overrides
=====================================================
Additionally, `ExtendedXmlSerializer` also supports overrides for serialization and deserialization that allow you to pass in `XmlWriterSettings` and `XmlReaderSettings` respectively. Here's an example of this for serialization:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
TestClass instance = new TestClass();
MemoryStream stream = new MemoryStream();
string contents = serializer.Serialize(new XmlWriterSettings { /* ... */ }, stream, instance);
And for deserialization:
.. sourcecode:: csharp
MemoryStream contentStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
TestClass deserialized = serializer.Deserialize(new XmlReaderSettings{ /* ... */ }, contentStream);
Fluent API
==========
ExtendedXmlSerializer use fluent API to configuration. Example:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer()
.UseEncryptionAlgorithm(new CustomEncryption())
.Type() // Configuration of Person class
.Member(p => p.Password) // First member
.Name("P")
.Encrypt()
.Member(p => p.Name) // Second member
.Name("T")
.Type() // Configuration of another class
.CustomSerializer(new TestClassSerializer())
.Create();
Serialization of dictionary
===========================
You can serialize generic dictionary, that can store any type.
.. sourcecode:: csharp
public class TestClass
{
public Dictionary Dictionary { get; set; }
}
.. sourcecode:: csharp
TestClass obj = new TestClass
{
Dictionary = new Dictionary
{
{1, "First"},
{2, "Second"},
{3, "Other"},
}
};
Output XML will look like:
.. sourcecode:: xml
1First2Second3Other
If you use UseOptimizedNamespaces function xml will look like:
.. sourcecode:: xml
1First2Second3Other
Custom serialization
====================
If your class has to be serialized in a non-standard way:
.. sourcecode:: csharp
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; }
}
You must create custom serializer:
.. sourcecode:: csharp
public class TestClassSerializer : IExtendedXmlCustomSerializer
{
public TestClass Deserialize(XElement element)
{
XElement xElement = element.Member("String");
XElement xElement1 = element.Member("Int");
if (xElement != null && xElement1 != null)
{
string strValue = xElement.Value;
int 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));
}
}
Then, you have to add custom serializer to configuration of TestClass:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().Type()
.CustomSerializer(new TestClassSerializer())
.Create();
Deserialize old version of xml
==============================
In standard `XMLSerializer` you can't deserialize XML in case you change model. In `ExtendedXMLSerializer` you can create migrator for each class separately. E.g.: If you have big class, that uses small class and this small class will be changed you can create migrator only for this small class. You don't have to modify whole big XML. Now I will show you a simple example:
If you had a class:
.. sourcecode:: csharp
public class TestClass
{
public int Id { get; set; }
public string Type { get; set; }
}
and generated XML look like:
.. sourcecode:: xml
1Type
Then you renamed property:
.. sourcecode:: csharp
public class TestClass
{
public int Id { get; set; }
public string Name { get; set; }
}
and generated XML look like:
.. sourcecode:: xml
1Type
Then, you added new property and you wanted to calculate a new value during deserialization.
.. sourcecode:: csharp
public class TestClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
and new XML should look like:
.. sourcecode:: xml
1TypeCalculated
You can migrate (read) old version of XML using migrations:
.. sourcecode:: csharp
public class TestClassMigrations : IEnumerable>
{
public static void MigrationV0(XElement node)
{
XElement 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> GetEnumerator()
{
yield return MigrationV0;
yield return MigrationV1;
}
}
Then, you must register your `TestClassMigrations` class in configuration
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType()
.AddMigration(new TestClassMigrations())
.Create();
Extensibility
=============
With type and member configuration out of the way, we can turn our attention to what really makes ExtendedXmlSeralizer tick: extensibility. As its name suggests, ExtendedXmlSeralizer offers a very flexible (but albeit new) extension model from which you can build your own extensions. Pretty much all if not all features you encounter with ExtendedXmlSeralizer are through extensions. There are quite a few in our latest version here that showcase this extensibility. The remainder of this document will showcase the top features of ExtendedXmlSerializer that are accomplished through its extension system.
Object reference and circular reference
=======================================
If you have a class:
.. sourcecode:: csharp
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Person Boss { get; set; }
}
public class Company
{
public List Employees { get; set; }
}
then you create object with circular reference, like this:
.. sourcecode:: csharp
Person boss = new Person {Id = 1, Name = "John"};
boss.Boss = boss; //himself boss
Person worker = new Person {Id = 2, Name = "Oliver"};
worker.Boss = boss;
Company obj = new Company
{
Employees = new List
{
worker,
boss
}
};
You must configure Person class as reference object:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType()
.EnableReferences(p => p.Id)
.Create();
Output XML will look like this:
.. sourcecode:: xml
4OliverJohn
Property Encryption
===================
If you have a class with a property that needs to be encrypted:
.. sourcecode:: csharp
public class Person
{
public string Name { get; set; }
public string Password { get; set; }
}
You must implement interface IEncryption. For example, it will show the Base64 encoding, but in the real world better to use something safer, eg. RSA.:
.. sourcecode:: csharp
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));
}
Then, you have to specify which properties are to be encrypted and register your IEncryption implementation.
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().UseEncryptionAlgorithm(new CustomEncryption())
.ConfigureType()
.Member(p => p.Password)
.Encrypt()
.Create();
Custom Conversion
=================
ExtendedXmlSerializer does a pretty decent job (if we do say so ourselves) of composing and decomposing objects, but if you happen to have a type that you want serialized in a certain way, and this type can be destructured into a `string`, then you can register a custom converter for it.
Using the following:
.. sourcecode:: csharp
public sealed class CustomStructConverter : IConverter
{
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 int 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; }
}
Register the converter:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().Register(CustomStructConverter.Default).Create();
CustomStruct subject = new CustomStruct(123);
string contents = serializer.Serialize(subject);
// ...
.. sourcecode:: xml
123
Optimized Namespaces
====================
By default Xml namespaces are emitted on an "as needed" basis:
.. sourcecode:: xml
4FirstSecondThird
But with one call to the `UseOptimizedNamespaces` call, namespaces get placed at the root of the document, thereby reducing document footprint:
.. sourcecode:: csharp
IExtendedXmlSerializer serializer = new ConfigurationContainer().UseOptimizedNamespaces()
.Create();
List