Skip to content

Example Scenarios

Mike-EEE edited this page Nov 29, 2019 · 20 revisions

Create an Extension

TBD

Serialization of Dictionary

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

    public class TestClass
    {
        public Dictionary<int, string> Dictionary { get; set; }
    }
    TestClass obj = new TestClass
    {
        Dictionary = new Dictionary<int, string>
        {
            {1, "First"},
            {2, "Second"},
            {3, "Other"},
        }
    };

Output XML will look like:

    <?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>

If you use UseOptimizedNamespaces function xml will look like:

    <?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>

Register Custom Serializer

You have full control over the serialization of a particular type. Consider the following class structure:

	public class Subject
	{
		public Subject(string text, int number)
		{
			Text   = text;
			Number = number;
		}

		public string Text { get; }
		public int Number { get; }
	}

Now, while it is possible to use EnableParameterizedContent [feature, API] to serialize and deserialize the above contract, what we want to demonstrate here is the ability to have full control over how the above is stored and retrieved.

The following code can be found in a passing test stored here.

First, let's create a serializer:

	sealed class SubjectSerializer : ISerializer<Subject>
	{
		public static SubjectSerializer Default { get; } = new SubjectSerializer();

		SubjectSerializer() {}

		public Subject Get(IFormatReader parameter)
		{
			var parts  = parameter.Content().Split('|');
			var result = new Subject(parts[0], int.Parse(parts[1]));
			return result;
		}

		public void Write(IFormatWriter writer, Subject instance)
		{
			writer.Content($"{instance.Text}|{instance.Number}");
		}
	}

The above is very rudimentary and extremely error-prone (particularly the int.Parse call), but again what we are demonstrating here is full control over the serialization and deserialization process for this particular type. If you feel so inclined (and maybe a little crazy 😅), you could even modify the serializer to send and retrieve data from a database.

That is the point (and power!) of this scenario, as well as demonstrating how to register a serializer for a particular type.

Now that we have the serializer created, let's register it and create a root container:

	IExtendedXmlSerializer serializer = new ConfigurationContainer().Type<Subject>()
	                                                                .Register()
	                                                                .Serializer()
	                                                                .Using(SubjectSerializer.Default)
	                                                                .Create();
    Subject instance = new Subject("Hello World!", 123);
	string contents = serializer.Serialize(instance);

The above will create the following XML:

<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:Namespace;assembly=Assembly">Hello World!|123</Subject>

The above code can be viewed as a passing test here.

Migrate XML Based on Older Class Model

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 look like:

    <?xml version="1.0" encoding="utf-8"?>
    <TestClass xmlns="clr-namespace:ExtendedXmlSerialization.Samples.MigrationMap;assembly=ExtendedXmlSerializer.Samples">
      <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 look like:

    <?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>

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

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

    public class TestClassMigrations : IEnumerable<Action<XElement>>
    {
        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<Action<XElement>> GetEnumerator()
        {
            yield return MigrationV0;
            yield return MigrationV1;
        }
    }

Then, you must register your TestClassMigrations class in configuration

    IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType<TestClass>()
                                                                    .AddMigration(new TestClassMigrations())
                                                                    .Create();