Troubleshooting with the XmlSerializer
The XmlSerializer in the .NET Framework is a great tool to convert Xml into runtime objects and vice versa – when it works. Unfortunately in some cases it doesn’t work right away and those situations can be very frustrating.
This is partially because the XmlSerializer throws exceptions that don’t provide a lot of information why the XmlSerializer wouldn’t do its job. At least it doesn’t provide that information in a prominent spot for programmers to see it right away.
In most cases an exception handler will catch a System.InvalidOperationException thrown in Serialize() or Deserialize(), which makes the StackTrace property useless because it does not offer any more insight into the root cause of the exception. To make matters worse, the exception’s Message property only yields very generic information. If we are trying to serialize an undeclared type for example, the Serialize() method would throw an exception with the following message:
There was an error generating the XML document.
This message is annoying at best, because we already figured that much when we saw an exception and doesn’t help us troubleshooting the problem.
The trick to get to the “real information” about the problem is to examine the exception’s InnerException property, which contains very detailed information about the problem and where it occurred.
The InnerException’s message is usually very descriptive, pinpoints the problem and, in many cases, even offers a possible solution. When we are trying to serialize an undeclared type the InnerExcpetion reads something like this:
The type XmlSerializationApp.XmlCar was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
The following listing demonstrates how to set up the exception handler and how to access the InnerException property.
using System;
public static ParkingLot DeserializeParkingLot( XmlReader reader )
{
ParkingLot lot = null;
try
{
XmlSerializer ser = new XmlSerializer( typeof(ParkingLot));
lot = (ParkingLot) ser.Deserialize( reader );
}
catch( Exception ex )
{
DumpException( ex );
}
return lot;
}
public static void DumpException( Exception ex )
{
WriteExceptionInfo( ex );
if( null != ex.InnerException )
{
WriteExceptionInfo( ex.InnerException );
}
}
public static void WriteExceptionInfo( Exception ex )
{
Console.WriteLine( "--------- Exception Data ---------" );
Console.WriteLine( "Message: {0}", ex.Message );
Console.WriteLine( "Exception Type: {0}", ex.GetType().FullName );
Console.WriteLine( "Source: {0}", ex.Source );
Console.WriteLine( "StrackTrace: {0}", ex.StackTrace );
Console.WriteLine( "
}
Inspecting the InnerException will help you resolve a lot of problems around the XmlSerializer. However, there is group of problems where even the InnerException does not provide enough information to diagnose the problem. The typical exception message in this case is:
System.IO.FileNotFoundException: File or assembly name
abcdef.dll, or one of its dependencies, was not found.
File name: "abcdef.dll"
The call stack of these exceptions show that the exception originated while the XmlSerializer attempted to load an assembly generated by CodeDOM using System.Reflection.Assembly.Load. These exception are typically caused by errors during the compilation of classes that the XmlSerializer creates on-the-fly when you instantiate it. These compilation errors are not part of any exception error message and we would have a really hard time to figure the problem out. Fortunately, Chris Sells has a command-line tool that helps diagnosing these problems. The tool will go through the same steps as the XmlSerializer to reflect over the classes you need to (de-)serialize, compiles them and writes out the errors that occured to the compilation.
Now there are some situations where you can’t get enough information from neither the exception nor from Chris Sells’ tool. In those cases your last chance is an undocumented feature of the XmlSerializer.
You can instruct the XmlSerializer to keep the source code for the temporary serialization classes to our hard disk. Under normal circumstances, the XmlSerializer deletes this classes once it’s done it’s job, but you can set a diagnostics switch in you applications config file to prevent it from doing so.
If your problem happens in a big web application you may want to copy the class that’s causing your serialization troubles into a small, forms-based test application to speed up your troubleshooting efforts. Now add a diagnostics switch like in the example below to the application’s config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="1" />
</switches>
</system.diagnostics>
</configuration>
Now you can run the test application with this switch in place and then search our temp directory for *.cs files after you ran code that would reproduce the problem.
On a computer running Windows 2000 or later, the default location for the temp directory is <System Drive>\Documents and Settings\<Your User Name>\LocalSettings\Temp or <Windows Directory>\Temp for web applications running under the ASPNET account. The .cs files are easy to miss because they have very odd looking, randomly generated file names, something like: oinmcjqh.0.cs.
If you are running your application under the Visual Studio debugger you see messages about assemblies with these names being loaded into your application the first time you instantiate an XmlSerializer instance for a certain type. Once these assemblies are loaded into the application process you can set breakpoints, inspect variables and everything else you can do to your own code. When you have to go that far, chances are that you are diagnosing a bug in the XmlSerializer. In those cases maye sure you post your findings to a place that’s monitored by Microsoft, such as the public newsgroup microsoft.public.dotnet.xml or let me forward it to somebody on the XmlSerialization team. Honestly, I hope you never encounter any of these situations.
You can now find an extended version of that article at the MSDN XML Developer Center