Latest version of this page, available in Luismi Pena's Home page; Author: java_lmp@yahoo.co.uk

xmlobjects - Xml mapping for C#

Description

xmlobjects is a lightweight library to map Xml documents to C# classes, whose main purpose is to simplify the handling of simple Xml files; its objective is not the serialization of objects in Xml (this functionality is built in .Net).

The mention of simple Xml files applies to two restrictions:

With xmlobjects, the programmer must create a hierarchy of classes that represents directly the Xml instances; my main objective is to facilitate the handling of configuration files, although it can obviously read any Xml files -and write them-.

License and download

xmlobjects is delivered as it, without any responsabilities on the author. It is open source, it can be used or modified without any limitations.

The version to download appears only in source format:

Usefulness of xmlobjects

In many cases -even most cases-, the support provided by xmlobjects is redundant, as quite an equivalent functionality is directly build in the .Net framework.

The Xml Serialization support in the framework -defined in the namespace System.Xml.Serialization- already provides a mapping between C# classes and Xml instances. And the System.Configuration namespace provides the required support for handling configuration files in client applications; explanations of these mechanisms can be found in the following URLs:

I wrote xmlobjects beacuse I needed a specific feature, not covered directly in the Xml serialization .Net support: knowing the specific order on which subelements are defined. I know that such functionality can be indirectly covered, adding extra information in the Xml file, or providing my custom reader/writer for the specific Xml class; however, modifying the schema is not always an option, and customizing the reader/writer already requires some effort. Obviously, such effort would always be lesser than writing the xmlobjects library, but having my own implementation could facilitate introducing further requirements.

Specific features supported in xmlobjects:

Interface

The main objective of xmlobjects is the reading of simple files; the programmer defines one or more classes that map the Xml schema, and triggers the creation of C# instances by reading the Xml file. For example, a class with the following fields:

	public class Example {
		public string name;
		public Subname subs;
	}
	
	public class Subname {...}
has the Xml representation as:
	<Example name='...'>
		<Subname ... > ... </Subname>
		...
	</Example>

As with the System.Xml.Serialization namespace, xmlobjects rely on custom attributes to modify the default behaviour.

Any class can be mapped to a Xml type. By default, any public field is mapped to an attribute or a nested element of the Xml type. Note that this differs from the System.Xml.Serialization namespace, where public fields or properties can be mapped.

If the field's type is a primitive, an enumeration or a string, the mapping defaults to Xml attributes; in the rest of the cases, they map to Xml nested elements, as shown on the example above. This behaviour can be customized, for all the fields or for each individual field. The exception is the array fields, mapped always as attributes.

The library knows how to map automatically arrays, and has built-in support for List<T> elements, where T is not a List<T> itself. This means that having an ArrayList field, for example, will not have the expected result (replacing an array of objects)

Type attributes

A type can be decorated with the XmlType attribute, which allows the following modifiers:

The XmlType attribute is not inherited, affects only to the declaring class. A subclass could define different settings for this attribute, but the redefined settings do not affect the parent classes.

For example, if the parent class defines the innerTextField, the subclass can define another innerTextField, and both fields will be set.

There is a limitation on the fields that can be specified in the type attribute: they can only refer to the declaring class. That is, a subclass cannot define its InnerTextField as a field defined above in its hierarchy.

Field attributes

The fields in a type can have two attributes: XmlNoField, meaning that the field has no Xml representation, and XmlField, which has the opposite meaning.

XmlField allows the following modifiers:

The mapping is always case unsensitive. This implies that it is an error to define a class with two fields differing only in case.

Xml serialization

The library allows as well to save an instance as Xml, using the same specifications described for the loading. The objective of the library is to read / store Xml files, not to serialize the passed objects. In special, recursive definitions are not allowed.

Polymorphism support

The library supports polymorphism on the Xml elements. For example, in the following C# classes:

  
  class File { 
    public string name;
  }
  class Folder : File  {
    public File[] file;
  }
when a Folder is stored, if any of the referenced files is itself a folder, it will be stored as a folder (with subfiles). On this example, the stored Xml could look like:
  
  <File name='\'>
    <File name='.' />
    <File name='..' />
  </File>
That is, even when a File object is being stored, information on the fields defined it its real type (Folder), are used in the output (in this case, all the File subelements).

In some cases, this can be all that is needed, specially if the library is only used to save Xml files. However, if the produced file is read again by xmlobjects, using the same C# classes, there will be an error: a File is created with name '\', and then a subelement with name 'File' is required, but a File instance knows nothing about any 'File' field!

As mentioned above, xmlobjects does not intend to do objects serialization, but supporting this scenario makes perfect sense when the same polymorphism ideas are applied to the Xml definitions. In particular, this enables scenarios where the user or 3rd party libraries can extend predefined Xml (configuration or not) files.

To enable this support, the Xml element can include an optional element describing its real type, like in the following uncomplete example:

  <File name='\' type='Folder'>
    <File name='.' />
    <File name='..' />
  </File>
In this case, the 'type' attribute is used to specify the real type. It has no representation on the C# classes, there is no need to specify a field with that name (in fact, it would raise an error).

It is possible to specify the name of the type attribute in the XmlTypeAttribute associated to a class, or in a general way for all the classes. The following restrictions apply:

The type attribute must include the full class type specification; and, if it is defined outside the assembly of the original type, the assembly is required as well. An example without assembly is:

<file name='aname' type='XmlObjectsTestUnits.TestB02TypeAssembly+NewFile>
And the following example shows the assembly information as well:
<file name='aname' type='XmlObjectsTestUnits.TestB02TypeAssembly+NewFile, 
NunitTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'/>

Provided support

In some cases, it is needed to know when the Xml specifies one attribute or not; for example

  
  <given times='0'/>
can be indistinguable from
<given/>
when times is handled as an integer attribute.

If the class defines a boolean type named 'timesAttributeProvided', after the type 'times' is specified, and has no associated [XmlField] attribute, it is handled as a support field, that will contain true or false depending on whether the attribute is specified or not.

If the main type is an element, not an attribute, but is not an array, it can use the same functionality, but the support field must be named like in 'timesElementProvided'