Click here to Skip to main content
13,250,043 members (65,133 online)
Click here to Skip to main content
Add your own
alternative version

Stats

31.8K views
1.1K downloads
68 bookmarked
Posted 31 Aug 2016
MIT

Extended XML Serializer for .NET

, 6 Jun 2017
Rate this:
Please Sign up or sign in to vote.
Extended XML Serializer for .NET 4 and .NET CORE with WebApi and ASP Core integration.

Introduction

Extended XML Serializer for .NET 4 and .NET CORE. ExtendedXmlSerializer

Support platforms:

  • .NET 4.5
  • .NET Platform Standard 1.6 (.NET Core)

Support framework:

  • ASP.NET Core
  • WebApi

Support features:

  • Deserialization XML from standard XMLSerializer
  • 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 and XmlRootAttribute
  • POCO - all configurations (migrations, custom serializer...) are outside the class

Background

Standard XML Serializer in .NET is very limited.

  1. Does not support serialization of class with circular reference or class with interface property.
  2. There is no mechanism for reading the old version of XML.
  3. If you want create custom serializer, your class must inherit from IXmlSerializable. This means that your class will not be a POCO class.
  4. Does not support IoC

Serialization

You can serialize your object:

ExtendedXmlSerializer serializer = new ExtendedXmlSerializer();
var obj = new TestClass();
var xml = serializer.Serialize(obj);

Output XML will look like:

<?xml version="1.0" encoding="UTF-8"?>
<TestClass type="Samples.TestClass">
   <Id>1</Id>
</TestClass>

Deserialization

var obj2 = serializer.Deserialize<TestClass>(xml);

Serialization of Dictionary

You can serialize generic dictionary, that can store any type.

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"},
    }
};

Output XML will look like:

<TestClass type="Samples.TestClass">
  <Dictionary>
    <Item>
        <Key>1</Key>
        <Value>First</Value>
    </Item>
    <Item>
        <Key>2</Key>
        <Value>Second</Value>
    </Item>
    <Item>
        <Key>3</Key>
        <Value>Other</Value>
    </Item>
  </Dictionary>
</TestClass>

Custom Serialization

If your class has to be serialized in a non-standard way:

public class TestClass
{
    public TestClass(string paramStr)
    {
        PropStr = paramStr;
    }

    public string PropStr { get; private set; }
}

You must configure custom serializer:

public class TestClassConfig : ExtendedXmlSerializerConfig<TestClass>
{
    public TestClassConfig()
    {
        CustomSerializer(Serializer, Deserialize);
    }

    public TestClass Deserialize(XElement element)
    {
        return new TestClass(element.Element("String").Value);
    }

    public void Serializer(XmlWriter writer, TestClass obj)
    {
        writer.WriteElementString("String", obj.PropStr);
    }
}

Then, you must register your TestClassConfig class. See point configuration.

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:

public class TestClass
{
    public int Id { get; set; }
    public string Type { get; set; }
}

and generated XML that looks like:

<?xml version="1.0" encoding="utf-8"?>
<TestClass type="Samples.TestClass">
  <Id>1</Id>
  <Type>Type</Type>
</TestClass>

Then you renamed property:

public class TestClass
{
    public int Id { get; set; }
    public string Name { get; set; }
}

and generated XML that looks like:

<?xml version="1.0" encoding="utf-8"?>
<TestClass type="Samples.TestClass" ver="1">
  <Id>1</Id>
  <Name>Type</Name>
</TestClass>

Then, you added new property and you wanted to calculate a new value during deserialization.

public class TestClass
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}

and new XML should look like:

<?xml version="1.0" encoding="utf-8"?>
<TestClass type="Samples.TestClass" ver="2">
  <Id>1</Id>
  <Name>Type</Name>
  <Value>Calculated</Value>
</TestClass>

You can migrate (read) old version of XML using migrations:

public class TestClassConfig : ExtendedXmlSerializerConfig<TestClass>
{
    public TestClassConfig()
    {
        AddMigration(MigrationV0).AddMigration(MigrationV1);
    }

    public static void MigrationV0(XElement node)
    {
        var typeElement = node.Elements().FirstOrDefault(x => x.Name == "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"));
    }
}

Then, you must register your TestClassConfig class. See point configuration.

Object Reference and Circular Reference

If you have a class:

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; }
}

then you create object with circular reference, like this:

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
    }
};

You must configure Person class as reference object:

public class PersonConfig : ExtendedXmlSerializerConfig<Person>
{
    public PersonConfig()
    {
        ObjectReference(p => p.Id);
    }
}

Then, you must register your PersonConfig class. See point configuration.

Output XML will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<Company type="Samples.Company">
   <Employees>
      <Person type="Samples.Person" id="2">
         <Id>2</Id>
         <Name>Oliver</Name>
         <Boss type="Samples.Person" ref="1" />
      </Person>
      <Person type="Samples.Person" id="1">
         <Id>1</Id>
         <Name>John</Name>
         <Boss type="Samples.Person" ref="1" />
      </Person>
   </Employees>
</Company>

Property Encryption

If you have a class with a property that needs to be encrypted:

public class Person
{
    public string Name { get; set; }
    public string Password { get; set; }
}

You must implement interface IPropertyEncryption. For example, it will show the Base64 encoding, but in the real world better to use something safer, eg. RSA.:

public class Base64PropertyEncryption : IPropertyEncryption
{
    public string Encrypt(string value)
    {
        return Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
    }

    public string Decrypt(string value)
    {
        return Encoding.UTF8.GetString(Convert.FromBase64String(value));
    }
}

In the Person class configuration you need to specify which properties are to be encrypted:

public class PersonConfig : ExtendedXmlSerializerConfig<Person>
{
    public PersonConfig()
    {
        Encrypt(p => p.Password);
    }
}

Then, you must register your PersonConfig class and your implementation of IPropertyEncryption. See point configuration.

Configuration

For using config class, you must register them in ExtendedXmlSerializer. You can do this in two ways.

Use SimpleSerializationToolsFactory Class

var toolsFactory = new SimpleSerializationToolsFactory();

// Register your config class
toolsFactory.Configurations.Add(new TestClassConfig());

// If you want to use property encryption you must register your implementation of IPropertyEncryption, e.g.:
toolsFactory.EncryptionAlgorithm = new Base64PropertyEncryption();

ExtendedXmlSerializer serializer = new ExtendedXmlSerializer(toolsFactory);

Use Autofac Integration

var builder = new ContainerBuilder();
// Register ExtendedXmlSerializer module
builder.RegisterModule<AutofacExtendedXmlSerializerModule>();

// Register your config class
builder.RegisterType<TestClassConfig>().As<ExtendedXmlSerializerConfig<TestClass>>().SingleInstance();

// If you want to use property encryption you must register your implementation of IPropertyEncryption, e.g.:
builder.RegisterType<Base64PropertyEncryption>().As<IPropertyEncryption>().SingleInstance();

var containter = builder.Build();

// Resolve ExtendedXmlSerializer
var serializer = containter.Resolve<IExtendedXmlSerializer>()

ASP.NET Core Integration

You can integrate the ExtendedXmlSerializer with ASP.NET Core, so that your services will generate XML using a ExtendedXmlSerializer. You only need to install ExtendedXmlSerializer.AspCore and configure it in Startup.cs.

Use SimpleSerializationToolsFactory Class

This configuration is very simple. You just need to create configuration for ExtendedXmlSerializer and add formatters to MVC.

public void ConfigureServices(IServiceCollection services)
{
    // Custom create ExtendedXmlSerializer
    SimpleSerializationToolsFactory factory = new SimpleSerializationToolsFactory();
    factory.Configurations.Add(new TestClassConfig());
    IExtendedXmlSerializer serializer = new ExtendedXmlSerializer(factory);

    // Add services to the collection.
    services.AddMvc(options =>
    {
        options.RespectBrowserAcceptHeader = true; // false by default

        //Add ExtendedXmlSerializer's formatter
        options.OutputFormatters.Add(new ExtendedXmlSerializerOutputFormatter(serializer));
        options.InputFormatters.Add(new ExtendedXmlSerializerInputFormatter(serializer));
    });
}

Use Autofac Integration

This configuration is more difficult but recommended. You have to install Autofac.Extensions.DependencyInjection and read Autofac documentation. The following code adds an MVC service and creates a container AutoFac.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // Add services to the collection.
    services.AddMvc(options =>
    {
        options.RespectBrowserAcceptHeader = true; // false by default

        //Resolve ExtendedXmlSerializer
        IExtendedXmlSerializer serializer = ApplicationContainer.Resolve<IExtendedXmlSerializer>();

        //Add ExtendedXmlSerializer's formatter
        options.OutputFormatters.Add(new ExtendedXmlSerializerOutputFormatter(serializer));
        options.InputFormatters.Add(new ExtendedXmlSerializerInputFormatter(serializer));
    });

    // Create the container builder.
    var builder = new ContainerBuilder();

    // Register dependencies, populate the services from
    // the collection, and build the container. If you want
    // to dispose of the container at the end of the app,
    // be sure to keep a reference to it as a property or field.
    builder.Populate(services);
    builder.RegisterModule<AutofacExtendedXmlSerializerModule>();
    builder.RegisterType<TestClassConfig>().As<ExtendedXmlSerializerConfig<TestClass>>().SingleInstance();
    this.ApplicationContainer = builder.Build();

    // Create the IServiceProvider based on the container.
    return new AutofacServiceProvider(this.ApplicationContainer);
}

In this case, you can also inject IExtendedXmlSerializer into your controller:

[Route("api/[controller]")]
public class TestClassController : Controller
{
    private readonly IExtendedXmlSerializer _serializer;

    public TestClassController(IExtendedXmlSerializer serializer)
    {
        _serializer = serializer;
    }

    ...
}

WebApi Integration

You can integrate ExtendedXmlSerializer with WebApi, so that your services will generate XML using an ExtendedXmlSerializer. You only need to install ExtendedXmlSerializer.WebApi and configure it in WebApi configuration. You can do it using autofac or SimpleSerializationToolsFactory, e.g.:

public static void Register(HttpConfiguration config)
{
    // Manual creation of IExtendedXmlSerializer or resolve it from AutoFac.
    var simpleConfig = new SimpleSerializationToolsFactory();
    simpleConfig.Configurations.Add(new TestClassConfig());
    var serializer = new ExtendedXmlSerializer(simpleConfig);

    config.RegisterExtendedXmlSerializer(serializer);

    // Web API routes
    config.MapHttpAttributeRoutes();

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

Links

There is ExtendedXmlSerializer's web page.

You can download this lib from NuGet.

Source code and samples are available on GitHub.

Full documentation is there.

History

  • vnext version - v2.0.0 - Rewritten version with many new features.
  • 2016-12-06 - v1.5.0
    • Support property without set accessor for collections.
    • Support IDictionary, IList and ISet properties.
    • Attribute type is added only when necessary.
  • 2016-11-18 - v1.4.1 - Support sorting parameters and fix serialization of Object property
  • 2016-11-15 - v1.4.0 - Support XmlElementAttribute and XmlRootAttribute
  • 2016-10-18 - v1.3.0 - Property encryption
  • 2016-09-22 - v1.2.0 - Support of Dictionary<TKey, TSource>
  • 2016-09-14 - v1.1.0 - Integration with ASP.NET Core and WebApi
  • 2016-08-31 - v1.0.0 - First version

Contributors

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Wojciech Nagórski
Software Developer
Poland Poland
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionGood job! What about the performance? Pin
AlexeyYakovlev22-Feb-17 14:15
professionalAlexeyYakovlev22-Feb-17 14:15 
AnswerRe: Good job! What about the performance? Pin
Wojciech Nagórski22-Feb-17 22:27
memberWojciech Nagórski22-Feb-17 22:27 
GeneralRe: Good job! What about the performance? Pin
AlexeyYakovlev23-Feb-17 1:20
professionalAlexeyYakovlev23-Feb-17 1:20 
QuestionIndentation? Pin
Din0cheap21-Feb-17 21:44
memberDin0cheap21-Feb-17 21:44 
AnswerRe: Indentation? Pin
Wojciech Nagórski22-Feb-17 2:30
memberWojciech Nagórski22-Feb-17 2:30 
GeneralMy vote of 5 Pin
Tokinabo14-Feb-17 3:10
professionalTokinabo14-Feb-17 3:10 
GeneralRe: My vote of 5 Pin
Wojciech Nagórski14-Feb-17 3:17
memberWojciech Nagórski14-Feb-17 3:17 
QuestionPCL Support Pin
delfo14-Feb-17 2:52
memberdelfo14-Feb-17 2:52 
AnswerRe: PCL Support Pin
Wojciech Nagórski14-Feb-17 3:22
memberWojciech Nagórski14-Feb-17 3:22 
PraiseLovely Pin
zafirov2-Jan-17 0:50
memberzafirov2-Jan-17 0:50 
GeneralRe: Lovely Pin
Wojciech Nagórski13-Feb-17 23:33
memberWojciech Nagórski13-Feb-17 23:33 
QuestionIssue with xml attributes Pin
vishnu9er20-Oct-16 3:56
membervishnu9er20-Oct-16 3:56 
AnswerRe: Issue with xml attributes Pin
Wojciech Nagórski21-Oct-16 3:49
memberWojciech Nagórski21-Oct-16 3:49 
AnswerRe: Issue with xml attributes Pin
Wojciech Nagórski15-Nov-16 1:40
memberWojciech Nagórski15-Nov-16 1:40 
SuggestionSo why should I care to use this Pin
Clifford Nelson22-Sep-16 13:00
memberClifford Nelson22-Sep-16 13:00 
GeneralRe: So why should I care to use this Pin
Wojciech Nagórski14-Feb-17 3:26
memberWojciech Nagórski14-Feb-17 3:26 
QuestionFeautres Pin
Assil22-Sep-16 12:38
professionalAssil22-Sep-16 12:38 
AnswerRe: Feautres Pin
Wojciech Nagórski22-Sep-16 21:57
memberWojciech Nagórski22-Sep-16 21:57 
GeneralRe: Feautres Pin
Assil23-Sep-16 1:45
professionalAssil23-Sep-16 1:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171114.1 | Last Updated 6 Jun 2017
Article Copyright 2016 by Wojciech Nagórski
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid