Apr 22 2011

Fluent XML Serialization–Part 3: How Serialization Is Performed

Category: FluentlyXMLMatt @ 00:57

Reflection is a powerful tool when used correctly.  In this penultimate chapter of Fluent XML Serialization, I will show you how Fluently-XML uses configuration data built up from a domain-specific language to perform serialization at runtime.  Keep in mind that the code is still a work-in-progress, but it does indeed meet (most) of my original requirements.  At the very least, it meets the business requirements it was built for. :)

Previous Posts

Where we left off…

When we last visited Fluently-XML, we had taken configuration data from the domain-specific language (DSL) and stored it in configuration objects.  These objects were ‘compiled’ at runtime into type serializers and deserializers that know how to serialize and deserialize specific types.  Additional type serializers and deserializers are created on demand whenever Fluently-XML encounters a new type that wasn’t explicitly configured through the DSL.

So, we have configured Fluently-XML, and today we’ll look at an end-to-end example of actually using Fluently-XML.  We’ll start with configuring behaviors, look at how to obtain a serializer and convert an instance of a type to XML, and how to convert back from XML to an instance of the original type.

Configuring a Factory

Serialization (and deserialization) in Fluently-XML begins with a FluentSerializerFactory.  Through the factory’s constructor, you can directly specify how types should be serialized and deserialized.  However, the preferred approach is to put such specifications in their own serialization specifications, and to load any specifications from the factory, like so:

public static class Application
{
    public static void Main()
    {
        var factory = new FluentSerializerFactory(x =>
        {
            x.ApplyConfigFrom<BarSerialization>();
        });
    }
}

public class BarSerialization : FluentSerializationSpec
{
    /// <summary> </summary>
    public BarSerialization()
    {
        WhenSerializing<Bar>()
            .SerializeAllAncestorsAsThisType();

        WhenDeserializing<Bar>()
            .DetermineIdentityBy(r => r.BarId);
    }
}

Remember that you don’t actually need to specify how to serialize most types.  You only need to specify serialization behavior when you want to alter the default serialization or deserialization behavior. 

Serializing Objects

The type serializers introduced in the last post are not exposed to the outside world.  Instead, serialization operations are performed through the public IFluentSerializer interface that can be obtained from a  FluentSerializerFactory.

public static class Application
{
    public static void Main()
    {
        var factory = new FluentSerializerFactory(x =>
        {
            x.ApplyConfigFrom<BarSerialization>();
        });

        var serializer = factory.CreateSerializer();
        ...
    }
}

Instead of returning an XML string or writing to a stream, FluentSerializer returns an XElement.  XElement’s can be converted to a string representation of the XML by simply calling the XElement.ToString method.  They can also be saved to a stream or file using the various overloads that are exposed for the XElement.Save method:

public static class Application
{
    public static void Main()
    {
        ...
        
        var serializer = factory.CreateSerializer();
        
        var originalBars = new[] { new Bar { BarId = 1 }, new Bar { BarId = 1 }, new Bar { BarId = 2 } };

        var xElement = _serializer.Serialize(originalBars);
        
        //Get a string containing the serialized XML
        var xml = xElement.ToString();
        
        //Save the XML to a file
        xElement.Save("testfile.xml");
    }
}

An instance of IFluentSerializer is capable of serializing *any* type using the default conventions.  This is markedly different than the behavior of the BCL’s XmlSerializer class, which is only capable of serializing types that it is configured for.

Deserializing Types

Deserialization works in much the same way as serialization: simply request an IFluentDeserializer from the factory and pass in the XML to be deserialized:

public static class Application
{
    public static void Main()
    {
        var factory = new FluentSerializerFactory(x =>
        {
            x.ApplyConfigFrom<BarSerialization>();
        });

        var deserializer = factory.CreateDeserializer();

        var xml = File.ReadAllText("bars.xml");

        var deserializedBar = deserializer.Deserialize<Bar>(xml);
    }
}

Note: The current version of the library still uses a raw string for the XML instead of an XElement.  This will be changed in the near future.

The deserializer will perform deserialization according to whatever rules you’ve specified for the type being deserialized.  If no rules are configured for the type, it will simply use the default deserialization conventions.  Again, this is very different from the behavior of the BCL’s XmlSerializer. 

What’s happening internally?

Not much, actually.  When you ask Fluently-XML to serialize or deserialize a type, it looks for an ITypeSerializer or ITypeDeserializer respectively that was built when the configuration data was “compiled” (see the last post).  If no such serializer/deserializer exists, one is created and cached for reuse in the future. 

If you’re really curious, I recommend checking out the code

What’s Next?

There’s no official release of Fluently-XML yet, but the project page is out there at fluentlyxml.codeplex.com.  My focus as far as open-source projects goes remains on LiteGrid and getting its 1.0 release out the door.  After that, my efforts will turn towards polishing up Fluently-XML and its 1.0 release.  For version 1.0, I’m planning on adding better support for specifying and overriding the default conventions and creating NuGet packages for both the library and a corresponding sample application.   While I’ve found performance to be acceptable for my use cases, some optimization is almost certainly needed to support other use cases.  I plan to focus on performance improvements sometime after the 1.0 release. 

In the meantime, please let me know what you think, and check out the code for more information.

Tags:

blog comments powered by Disqus