Nerdy tidbits from my life as a software engineer

Friday, June 12, 2009

XmlSerializer Can’t Handle Interfaces (With a Workaround)

It didn’t occur to me until it was a bit too late, but XmlSerializer can’t handle interfaces.  I understand why this is: how would the serializer know what class to instantiate that would return an instance of the interface type?  But the lack of a built-in workaround here is a bit of an annoyance.  Since I like to abstract things in ways that allow me to mock them, I separated the implementation of some of objects into interfaces, without realizing that this would break serialization.  Even having your interface implement IXmlSerializable doesn’t solve the problem – you just end up with a different error that says something to the effect of, “System.InvalidOperationException: [class] cannot be serialized because it does not have a parameterless constructor.”  This is because the serializer is expecting that whatever implements IXmlSerializable is a class and that it has a parameterless constructor.  Obviously, interfaces won’t work in these cases.

For instance, this property will fail to serialize:

/// <summary>
/// Gets / sets the working folder shims.
/// </summary>
[XmlElement]
public virtual IWorkingFolderShim[] Folders
{
get;
set;
}

So in order to get around this, I had to do something hackish.  It’s not that pretty, but it works:

/// <summary>
/// Gets / sets the working folder shims.
/// </summary>
[XmlElement]
public virtual WorkingFolderShim[] FolderShims
{
get;
set;
}

/// <summary>
/// Gets / sets the folder shims.
/// </summary>
[XmlIgnore]
public virtual IWorkingFolderShim[] Folders
{
get
{
return this.FolderShims;
}
}

The object where these properties are defined implements an interface that exposes the folders property – not the FolderShims property. That way, so long as people reference the object by its abstraction, the FolderShims property is hidden from them. The proper thing to do is to make the FolderShims property internal, but I hate unit testing with InternalsVisibleTo unless it’s really absolutely necessary.

0 comments: