Writing a Class/Struct to be XML Serialized and Creating a Class from a Schema
f you read my last article ( Introduction to XML Serialization in .NET ), you may have been left wanting--wanting to know and do more with serialization. I know I was. I was left wanting to describe more than just a simple introduction to serialization in .NET, but I quickly realized that the topic was going to be lengthy. I didn't want you to have to scroll through pages and pages of text just to get to the parts you really care about--the code. As such, I decided to break up the topic into different parts. The last part was meant to be an introduction; this is meant to be continuation and further expansion of that introduction. This article is going to describe how to go about creating a class or structure that can be serialized using XML. It will also describe how to use existing schemas to generate class files using the xsd.exe tool which is included with Visual Studio.
Creating the Class or Structure
In keeping with the grocery theme that I introduced in my last article, here is the Groceries object:
[ C# ]
Creating the Class or Structure
In keeping with the grocery theme that I introduced in my last article, here is the Groceries object:
[ C# ]
[ VB ]
Unlike binary serialization in .NET, we do not have to explicitly denote a class or structure as being serializable. As you will notice in the attached projects, this functionality is already enabled within the runtime--I did not need to add any special attributes or configure the class in any special way. All we have to do is make sure that we set up our class or structure in a manner that will only output the data we want to be in the XML file--assuming we don't want everything.
So when we serialize our object, what will actually get output to the file? Is it all the public objects? All the private objects? The methods? The properties? Well, it is all the publicly accessible data. What do we mean by "publicly accessible data"? We are talking about public fields and private fields accessible through public properties. Notice here that I said "public properties," not "public accessors." If you decide to write traditional OO getter and setter methods, your private data accessed by these methods will not be put into the serialized object. Both your "get" and "set" must be public in order for you class to be serialized.
But what if there is data which we do not want to output to a file--maybe a login/password combination or perhaps a computed field which could easily be recalculated by some other fields? The answer is that we can instruct the runtime to ignore such fields by using an attribute: XmlIgnore. Fields decorated with XmlIgnore will not be output in the resulting XML when the class or structure is serialized.
You may or may not have seen attributes before--you may have seen them and not realized what they were. Attributes are a kind of declarative programming in .NET. They typically add something extra, or they change the behavior of a class or structure or method. For example, if you've ever had to work with the Win32 API via P\Invoke calls, you have had to add the DllImport attribute to the imported function. This lets the runtime know that you are calling a function defined in an external Dll. So for its use here, the attribute in a way changed the default behavior of the function--"don't look in this class file for the function's definition, rather find it in another file/library." For further information on attributes in .NET, please see Attributes (C# and Visual Basic).
Here is an example of not outputting a particular field. Here we have a property within our GroceryItem class and its public SubTotal property points to the "_subTotal" field. But "_subTotal" is the result of multiplying "_count" by "_price". We will ignore this field, usingXmlIgnore, because we will design this class to calculate the SubTotal when either Price or Count are set.
[ C# ]
[ VB ] (1)
Unfortunately, unlike binary serialization in .NET, there are no serialization/deserializat
[ C# ]
[ VB ]
We have now set up our class to be serialized. What next? Well, we set up our serialization code as demonstrated in the last article. I will not re-post that code here, but the link at the top of the page will take you back, should you need a refresher. I will, however, provide the complete projects (C# and VB) for download.
Now, if we set up our application to create an instance of Groceries as such:
[ C# ]
[ VB ]
what should we expect to see in our output? Well...
[ Results ]
Again, you'll notice that SubTotal was not output due to the use of XmlIgnore. Had we not used XmlIgnore, our output would look like:
[ Results ]
We have our SubTotal again!
Making a Class from a Schema
Thus far, we have created an XML document from our data object. This, in a way, gave us an implicit schema for our data.(2) What if we already have a schema which represents our data object(s) and we want to generate a class file from this so we can work with our object in code without the need of parsing the XML manually? Well, Visual Studio ships with a tool called xsd.exe, which is instantly accessible(3) if you open a Visual Studio [YYYY] Command Prompt.(4) Using this tool, you can pass it a schema file and generate classes which represent your data. If you base schema is dependent on other schemas, you pass those as well.
The xsd.exe tool has several options you can use when generating new class files. Of particular interest to you will probably be:
- the "\c" switch to indicate we want to generate a class file
- the "\l" (lowercase "L") switch to indicate the language the class file is in (C# or VB, with a default of C#)
- the "\o" switch to indicate the output directory of the code file
- the "\n" switch to indicate the namespace for the resulting code file.
As an example, if we had the following schema:
[ Grcoeries.xsd ]
We could supply the following command line to the xsd.exe utility to generate a class file:
[ CS ]
xsd.exe /c /l:CS Groceries.xsd (5)
[ VB ]
xsd.exe /c /l:VB Groceries.xsd
We could then use this generated class to deserialize our object(s) found in an XML file (assuming it validates against our schema) back to an object in memory.
Limitations
While being able to serialize objects to XML is a neat feature in .NET, there are some limitations to be aware of. Firstly, there is no solid maintaining of types when you serialize an object to XML. What does that mean? Well, when you output say a generic list of strings, your XML will not reflect that your list is actually a generic list of strings. It will most likely be output as a parent member with several child nodes, and your generated class, if you use xsd.exe against a schema, will most likely have a class which has a member array of child elements. The runtime will typically convert this to your generic list behind the scenes (using list initializers, I imagine).
Other limitations include:
- The XmlSerializer cannot deserialize arrays of ArrayLists, arrays of generic lists or collections which implement IDictionary.
- The XmlSerializer cannot serialize enumerations with a type of unsigned long and a value of 9,223,372,036,854,775,807 or greater.
- Items marked with the Obsolete attribute are not serialized.
Hopefully this has given you a quick introduction to creating classes compatible with XML serialization/deserializat
For more information on serialization and XML validation in .NET, have a look at:
Files
C# Solution Demo
VB Solution Demo
Footnotes
- 1
- In VB, you must either put the attribute on the same line as the function signature or put it above the signature and use a line-continuation character ( _ ). This does not apply in C#.
- 2
- That is not to say that we actually have a schema. If we serialized our object, modified the XML by renaming some nodes and then tried to deserialize the XML, we would most likely receive an exception or unexpected data. Always make sure to validate your data to prevent incorrect modifications to the object outside of you code. See the complete project code for an example of applying a schema to the data for validation.
- 3
- "Instantly accessible" because when you use the Visual Studio Command Prompt, the PATH environment variable is modifed for that particular command window. You can get to the xsd.exe tool without using the VSCP, but you will either need to modify your PATH variable or navigate to the directory housing xsd.exe.
- 4
- "YYYY" corresponds to the version of Visual Studio you are running (e.g. 2005, 2008, 2010, etc.).
- 5
- Remember, for the C# version, we could leave the "\l" switch off since the tool defaults to a language of C#.
References
Northrup, Tony and Shawn Wildermuth. .NET Framework 2.0 Application Development Foundation. Redmond: Microsoft Press. 2006.
http://msdn.microsoft.com/
No comments
Post a Comment