views:

330

answers:

2

In a previous question about serialising an object to an XmlDocument in C#, I needed to serialise some fault information to an XmlDocument that was returned from a asmx-style webservice call. On the client I need to de-serialise the XmlDocument back to an object.

This is straightforward enough if you know the type, but I realised I wanted a flexible approach where the type to de-serialise to is also encoded in the XmlDocument. I'm currently doing it manually by adding an XmlNode to the XmlDocument that has the type name, calculated as follows:

    Type type = fault.GetType();
    string assemblyName = type.Assembly.FullName;

    // Strip off the version and culture info
    assemblyName = assemblyName.Substring(0, assemblyName.IndexOf(",")).Trim();

    string typeName = type.FullName + ", " + assemblyName;

Then on the client I first get this type name back from the XmlDocument, and create the type object that is passed into the XmlSerialiser thus:

        object fault;
        XmlNode faultNode = e.Detail.FirstChild;
        XmlNode faultTypeNode = faultNode.NextSibling;

        // The typename of the fault type is the inner xml of the first node
        string typeName = faultTypeNode.InnerXml;
        Type faultType = Type.GetType(typeName);

        // The serialised data for the fault is the second node
        using (var stream = new StringReader(faultNode.OuterXml))
        {
            var serialiser = new XmlSerializer(faultType);
            objectThatWasSerialised = serialiser.Deserialize(stream);
        }

        return (CastToType)fault;

So this is a brute-force approach, and I was wondering if there's a more elegant solution that somehow includes the typename of the serialised type automatically, rather than manually recording it elsewhere?

A: 

Neil, why do you need it to be the same type on both the client and the server?

Are you still using ASMX on the client? That would be a reason, as ASMX does not properly support faults.

Also, do you have so many different fault types that a simple switch statement can't determine the correct type to use?

John Saunders
I'm using asmx on the server and client, yes. I'm sort've implementing a kind of half-baked WCF-like fault system because on the client I'm using an interface to the asmx service and I don't want users of that interface to get SoapExceptions, so they get something I've called a ServiceException instead, with a Fault property. That fault property is returned to the asmx client in the SoapException.Detail property, hence the XmlDocument/XmlNode serialisation. I don't have many fault types at the moment, but I want flexibility to add more to the server without breaking deployed clients.
Neil Barnwell
So, you're not reinventing the wheel; you're reinventing WCF.
John Saunders
+2  A: 

I had faced a similar problem and I came up with the same solution. As far as I am concerned, that is the only way to keep types together with values in XML serialization.

I see you are cutting assembly version out as I did too. But I'd like to mention, that you will have troubles with generic types as theirs signature looks like that:

System.Nullable`1[[System.Int, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

So I made a function to only cut out the assembly version(s), which seems to be enough to get rid of versioning problems:

    private static string CutOutVersionNumbers(string fullTypeName)
    {
        string shortTypeName = fullTypeName;
        var versionIndex = shortTypeName.IndexOf("Version");
        while (versionIndex != -1)
        {
            int commaIndex = shortTypeName.IndexOf(",", versionIndex);
            shortTypeName = shortTypeName.Remove(versionIndex, commaIndex - versionIndex + 1);
            versionIndex = shortTypeName.IndexOf("Version");
        }
        return shortTypeName;
    }
Yacoder