Recently, tools have found that a filenotfoundexception error is always reported when an object is serialized, but the code function is not affected.
However, to improve it, find Microsoft's own article on this aspect. Copy it here.
Xmlserializer FAQs
Release date: 6/30/2004 | update date: 6/30/2004
Christoph schittko
Applicable:
Microsoft Visual Studio. NET
Abstract:Christoph schittko discusses a variety of related techniques to diagnose common problems that occur when XML is converted to an object (and reverse conversion) using the XML serialization Technology in the. NET Framework.
Content on this page
Introduction
Internal working mode of xmlserializer
Serialization Error
Declare serialization type
Deserialization of XML
Exception from Constructor
Summary
Thank you
Introduction
Xmlserializer In the. NET Framework is a great tool that maps highly structured XML data to. Net objects. Xmlserializer calls a single API in a program to convert XML documents and objects. The conversion ing rules are represented by metadata attributes in the. NET class. This programming model has its own error category, and developers need to understand how to diagnose these errors. For example, the metadata attribute must describe all variants of the XML format that the serializer can process. This article studies the various errors that may occur when using xmlserializer to build an XML-based solution, and discusses the techniques and tools used to diagnose these errors.
Back to Top internal working method of xmlserializer
To effectively solve XML serialization problems, you need to know what happened inside the very simple xmlserializer interface. In contrast to the traditional analysis model, the xmlserializer of the system. xml. serialization namespace in the. NET Framework binds the XML document to an instance of the. NET class. Programmers no longer need to write Dom or sax analysis code. Instead, they can explicitly set binding rules by attaching the. NET metadata attribute to these classes. Because all analysis rules are represented by attributes, the xmlserializer interface is very simple. It mainly consists of two methods: serialize () is used to generate xml from the object instance; deserialize () is used to analyze the XML document into an object graph.
This method is very effective when using a strongly typed XML format that perfectly maps to programming objects. If the format is defined by W3C architecture and the architecture is composed of complextype that does not contain mixed content or does not use wildcards (XS: Any and Xs; anyattribute, XML serialization is a good way to process the data.
Message-oriented applications are a good example. The interchange formats between these applications have been pre-defined. Because many messaging-driven enterprise applications have very high throughput requirements, the serialize () and deserialize () methods are designed to have high execution speeds. In fact, xmlserializer powers highly scalable libraries, ASP. NET Web Services, and BizTalk Server 2004 in the system. messaging namespace.
To achieve high performance of xmlserializer, you have to pay a double price. The first is the flexibility related to the XML format that the given xmlserializer can process, and the second is that the construction of the instance requires a lot of processing.
When you instantiate xmlserializer, you must pass the type of the object you are trying to serialize and deserialize through the serialization program instance. The serialization program checks all common fields and attributes of this type to see which types are referenced by an instance at runtime. Next, it creates C # code for a group of classes to use the classes in the system. codedom namespace to process serialization and deserialization. In this process, xmlserializer checks the reflection type of the XML serialization attribute to customize the created class according to the XML format. These classes are then compiled into temporary assembly and called by the serialize () and deserialize () Methods to execute XML-to-object conversion.
The elaborate process and Declarative Programming Model of xmlserializer cause three types of errors, some of which may be difficult to solve:
The generated serialization class expects that the serialized object fully complies with the type structure defined by the metadata attribute. If xmlserializer encounters a type that is not declared (explicitly declared or declared through the XML serialization attribute), the object cannot be serialized.
The XML document cannot be deserialized in the following circumstances: the root element of this document cannot map object types; the format of this document is incorrect, for example, containing characters defined as invalid in the XML specification; this document violates infrastructure restrictions (in some cases ).
Finally, the creation of the serialization class and subsequent compilation may fail for a variety of reasons. When the type passed to the constructor or the type referenced by this type implements unsupported interfaces or cannot meet the restrictions imposed by xmlserializer, the creation of the class may fail.
When additional properties are used to generate C # code that cannot be compiled, the compilation process may fail. Compilation steps may also fail due to security-related reasons.
The following sections will take a deeper look at these situations and provide guidance and suggestions on how to solve these problems.
Returns the serialization error of the header.
The first type of error that we want to study occurs in the serialize () method. This type of error occurs when the type in the object graph passed to this method at runtime does not match the type declared in the class during design. You can use the field or attribute type definition to implicitly declare the type, or you can explicitly declare the type by attaching the serialization attribute.
Figure
1.
Type Declaration in object graph
It should be pointed out that inheritance is not enough. Developers must either append the xmlinclude attribute to the base class or the xmlelement attribute to the field (these fields can accommodate objects of the type derived from the declared type ), to declare the derived type of xmlserializer.
For example, take a look at the following class hierarchies:
Public class base {public string field;} public class derived {Public String anotherfield;} public class container {public base myfield ;} if you inherit dependencies and write serialization code similar to the following: Container OBJ = new container (); obj. myfield = new derived (); // legal assignment in //. net type system //... xmlserializer serializer = new xmlserializer (typeof (container); serializer. serialize (writer, OBJ); // kaboom!
You will get an exception from the serialize () method because there is no explicit type declaration for xmlserializer.
Exception from xmlserializer
It may be difficult to diagnose the root cause of these problems at the beginning, because exceptions from xmlserializer do not seem to provide a large amount of information about the cause; at least, they do not provide information at the location that developers normally view.
In most cases, when an error occurs, the serialize, deserialize, or even the xmlserializer constructor will trigger a fairly common system. invalidoperationexception. This exception type can appear in many places in the. NET Framework; it is not unique to xmlserializer at all. Worse, the message attribute of this exception only produces very common information. In the above example, the serialize () method will cause an exception with the following message:
There was an error generating the XML document.
This message is annoying at most, because you have guessed this when you see that xmlserializer raises an exception. Now, you have to find that the exception message cannot help you solve the problem.
The strange exception message and non-descriptive exception types reflect the internal working method of xmlserializer described earlier in this article. The serialize () method captures all exceptions thrown in the serialization class, wraps them into invalidoperationexception, and then passes the exception packet up the stack.
Read exception messages
The trick to get the "actual" exception information is to check the innerexception attribute of the exception. Innerexception references the actual exceptions caused inside the serialization class. It contains detailed information about the problem and its location. The exception you caught when running the preceding example contains the innerexception with the following message:
The type Derived was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
You can obtain the message by checking innerexception directly or calling the tostring () method of the exception. The following code snippet demonstrates an exception handler that writes information about all exceptions that occur during the deserialization of an object:
public void SerializeContainer( XmlWriter writer, Container obj ){ try { // Make sure even the construsctor runs inside a // try-catch block XmlSerializer ser = new XmlSerializer( typeof(Container)); ser.Serialize( writer, obj ); } catch( Exception ex ) { DumpException( ex ); } }public static void DumpException( Exception ex ){ Console.WriteLine( "--------- Outer Exception Data ---------" ); WriteExceptionInfo( ex ); ex = ex.InnerException; if( null != ex ) { Console.WriteLine( "--------- Inner Exception Data ---------" ); WriteExceptionInfo( ex.InnerException ); ex = ex.InnerException; }}public static void WriteExceptionInfo( Exception ex ){ 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( "TargetSite: {0}", ex.TargetSite ); }
Return to the header to declare the serialization type
To solve the problem in the preceding example, you only need to read the innerexception message and implement the recommended solution. A field in the object graph passed to the serialize method references an object of the derived type, but this field is not declared as a serialized object of the derived type. Although the object graph is in. the net type system is completely legal, but when the xmlserializer constructor traverses the fields of the container type, it does not know that the serialization code is created for the derived type object, this is because it does not find a reference to the derived type.
You have multiple options to declare other fields and attribute types to xmlserializer. You can use the xmlinclude attribute (prompted by an exception message) to declare the derived type of the base class, as shown below:
[System.Xml.Serialization.XmlInclude( typeof( Derived ) )]public class Base{ // ...}
By attaching the xmlinclude attribute, you can enable xmlserializer to serialize the fields that reference the derived object when the field or attribute is defined as the base type.
Alternatively, you can declare a valid type on a single field or attribute, rather than declaring a derived type on the base class. You can append the xmlelement, xmlattribute, or xmlarrayitem attribute to a field and declare the type that the field or attribute can reference. The xmlserializer constructor then adds the Code required for serialization and deserialization to the serialization class.
Read stacktrace
The message attribute of innerexception is not the only attribute that contains valuable information. The stacktrace attribute provides more details about the error source. At the top of the stack trace, you can find the name of the method that first raises an exception. The method name in the temporary assembly follows the write _ format for the serialization class and the read _ format for the deserialization class _. In the example with the above error namespace, you can see that the exception originated from the method named readmediamyclass. Later, I will show you how to use the Visual Studio debugger to set breakpoints and perform this method in one step. However, let's take a look at the common problems that occur around deserialization of XML documents.
Problems with XML deserialization when the header is returned
Deserializing an XML document to an object graph is not as error-prone as serializing an object graph to XML. When the object does not match the type definition very well, xmlserializer will be very sensitive, but if the deserialization XML document does not match the object very well, it will be very tolerant. For XML elements that do not correspond to fields or attributes in the deserialization object, xmlserializer does not cause exceptions, but simply triggers events. If you want to track the matching degree between deserialized XML documents and XML formats, you can register the processing programs for these events. However, you do not need to register an event handler with xmlserializer to correctly process the XML nodes that are not mapped.
In the deserialization process, only several error conditions can cause exceptions. The most common conditions are:
The name of the root element or its namespace does not match the expected name.
The enumerated data type shows undefined values.
The document contains invalid XML.
As in serialization, The deserialize () method will cause an invalidoperation exception with the following message whenever a problem occurs.
There is an error in XML document (, ).
This exception usually contains a real exception in the innerexception attribute. The innerexception type varies with the actual errors that occur when reading the XML document. If the serialization program cannot use the type passed to the constructor, the type specified through the xmlinclude attribute, or the type specified in a more complex overloaded type [] passed to the xmlserializer constructor match the root element of the document, innerexception is invalidcastexception. Remember, xmlserializer will view the QNAME (that is, the name of the element) and namespace to determine the class to deserialize the document. They must all match the declaration in the. NET class so that xmlserializer correctly identifies the type that corresponds to the root element of the document.
Let's look at an example:
[XmlRoot( Namespace="urn:my-namespace" )]public class MyClass{ public string MyField;}
Deserialization of the following XML document will cause an exception because the XML namespace of the myclass element is not as urn: My-namespace as declared through the xmlroot attribute on the. NET class.
<MyClass> <MyField>Hello, World</MyField> </MyClass>
Let's take a closer look at this exception. The exception message is more descriptive than the message you captured from the serialize () method; at least it references the location that causes the deserialize () failure in the document. However, when you process large XML documents, it may not be that easy to view the document and identify errors. Innerexception again provides better information. This time, it shows:
<MyClass xmlns=''> was not expected.
The message is still vague, but it does indicate the elements that cause the problem to you. You can check the myclass class carefully and compare the element name and XML namespace with the XML serialization attribute in the. NET class.
Invalid XML deserialization
Another frequently reported problem is the inability to deserialize invalid XML documents. The XML specification prohibits the use of certain control characters in XML documents. However, you may still receive XML documents containing these characters. As you guessed, the problem is exposed in invalidoperationexception. However, in this special case, the innerexception type is xmlexception. The message of innerexception is crucial:
hexadecimal value <value>, is an invalid character
If you set the normalization attributeTrueTo avoid this problem. Unfortunately, the xmltextreader used internally by ASP. NET Web Services sets its normalization attributeFalseThat is, it will not deserialize soap messages containing these invalid characters.
Returns the top error from the constructor.
The last type of problem discussed in this article occurs when the xmlserializer constructor analyzes the input types. Remember that the constructor recursively checks each common field and attribute in the type hierarchy to create classes for processing serialization and deserialization. Then, it will compile these classes instantly and load the resulting assembly.
In this complex process, many different problems may occur:
The declared type of the root element or the type referenced by the attribute or field does not provide the default constructor.
A type in the hierarchy implements the set interface idictionary.
When you execute a type of constructor or attribute accessors in the object graph, you need to increase the security permission.
The generated serialization Class Code cannot be compiled.
An attempt to pass an unserializable type to the xmlserializer constructor will also cause invalidoperationexception, but this exception will not wrap other exceptions. The message attribute fully explains the reason the constructor rejects "type. When you try to serialize an instance of a class that does not implement a constructor without parameters (default constructor), an exception with the following message will be generated:
Test.NonSerializable cannot be serialized because it does not have a default public constructor.
On the other hand, it is very complicated to solve compilation errors. These problems are exposed in filenotfoundexception with the following message:
File or assembly name abcdef.dll, or one of its dependencies, was not found. File name: "abcdef.dll" at System.Reflection.Assembly.nLoad( ... ) at System.Reflection.Assembly.InternalLoad( ... ) at System.Reflection.Assembly.Load(...) at System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly() ....
You may not know the relationship between the "file not found" exception and the instantiated serialization program object, but remember: the constructor writes the C # file and tries to compile these files. The call stack for this exception provides some useful information, providing a basis for such suspicion. This exception occurs when xmlserializer tries to load the codedom generated by codedom that calls the system. reflection. Assembly. Load Method. This exception does not provide an explanation of the reason why xmlserializer guessed that the Assembly to be created does not exist. Generally, this Assembly does not exist because of compilation failure. This is because the serialization property generates code that the C # compiler cannot compile, but this rarely happens.
NoteThis error also occurs when the account or security environment of the xmlserializer runtime cannot access the temp directory.
Any exception error messages caused by xmlserializer do not contain actual compilation errors, or even innerexception does not contain actual compilation errors. This makes it very difficult to solve these exceptions until Chris sells released his xmlserializerprecompiler tool.
Xmlserializerprecompiler
Xmlserializer precompiler is a command line program that executes the same steps as the xmlserializer constructor. It can analyze types, generate serialization classes, and compile these classes-because it is purely designed as a troubleshooting tool, it can safely write any compilation errors to the console.
This tool is very convenient to use. You only need to direct the tool to an Assembly that contains the type that caused the exception and specify the pre-compiled type. Let's look at an example. When you attach the xmlelement or xmlarrayitem attribute to a field defined as a staggered array, a frequently reported problem occurs, as shown in the following example:
namespace Test{ public class StringArray { [XmlElement( "arrayElement", typeof( string ) )] public string [][] strings; }}
When the xmlserializer object is instantiated for the test. stringarray type, the xmlserializer constructor triggers filenotfoundexception. If you compile the class and try to serialize the instance of the class, you will get filenotfoundexception, but you will not get any clue about the problem. Xmlserializerprecompiler can provide you with missing information. In my example, the stringarray class is compiledXmlser.exeAnd I must use the following command line to run the tool:
XmlSerializerPreCompiler.exe XmlSer.exe Test.StringArray
The first command line parameter specifies the assembly, and the second parameter defines the class to be precompiled in the Assembly. This tool writes a large amount of information to the Command window.
Figure
2. xmlserializerprecompiler
Command window output
The important lines of code to be viewed are the lines of code with compilation errors and two lines similar to the following:
XmlSerializer-produced source:C:\DOCUME~1\\LOCALS~1\Temp\.cs
Now, xmlserializerprecompiler provides us with location for compilation errors and source files containing codes that cannot be compiled.
Debug serialization code
Generally, xmlserializer will delete the C # source file of the serialization class when it is no longer needed. However, there is an unconfirmed diagnostic switch that can be used to instruct xmlserializer to keep these files on the hard disk. You can set this switch in the. config file of the application:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <switches> <add name="XmlSerialization.Compilation" value="4" /> </switches> </system.diagnostics> </configuration>
If this switch appears in the. config file, the C # source file will be kept in the temp directory. If your computer is running Windows 2000 or later, the default location of the temp directory is \ Documents and Settings \ localsettings \ Temp or \ Temp (.. Net account ). These C # files are easily lost because their file names look strange and are randomly generated, for example, bdz6lq-t.0.cs. Xmlserializerprecompiler can set this diagnostic switch, so you can open these files in notepad or Visual Studio to check the lines of code that xmlserializerprecompiler reports compilation errors to it.
You can even execute these temporary serialization classes one by one, because the diagnostic switch can also retain the. PDB file containing debugging symbols on the hard disk. If you need to set breakpoints in the serialization class, you can run the application under the Visual Studio debugger. Once you see the relevant message in the output window, it indicates that the application has loaded the Assembly with these strange names from the temp directory, you can open the C # file with the corresponding name, set the breakpoint as in your own code.
Figure
3.
Compilation error output from diagnostic Switch
After a breakpoint is set in the serialization class, you need to execute code to call the serialize () or deserialize () Methods on the xmlserializer object.
NoteYou can only debug serialization and deserialization, but not the code generation process running in the constructor.
By performing a single step in the serialization class, you can identify each serialization problem. If you want to perform deserialization of soap messages in one step, you can use the above technique because ASP. NET web services and Web Service proxies are built on xmlserializer. All you need to do is add the diagnostic switch to your CONFIG file, and set breakpoints in the deserialization message class. If the WSDL does not accurately reflect the Message format when generating the proxy class, I occasionally use the above techniques to determine the correct serialization attribute set.
Back to Top Summary
These prompts should help you diagnose serialization problems in xmlserializer. Most of the problems you encounter are caused by incorrect combinations of XML serialization attributes or XML that does not match the type to be deserialized. Serialization attributes control the generation of serialized class code, and may cause compilation errors or runtime exceptions. By carefully checking exceptions caused by xmlserializer, you can identify the root cause of exceptions during runtime. If you need to further diagnose the problem, you can use the xmlserializerprecompiler tool to help you find compilation errors. If neither method can help you find the root cause of the problem, you can check the automatically created serialization class code and execute the code one by one in the debugger.
Back to Top
We would like to thank dare Obasanjo and Daniel cazzulino for their feedback and suggestions on editing this article.