Mark Wilson I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
Until now we only considered scalar members, but what about
arrays and collection types? The
XmlSerializer will process them just fine, as long as it
created type mappings for the types inside the collection or the array. In
section 9.4 we learned how to declare additional types for scalar fields. The
next sections will show us how to declare additional types for arrays and
collections. We will also read about attributes to customize XML mappings for
arrays and collections.
Serializing arrays works just like serializing scalar objects.
Everything we’ve learned so far about declaring types and customizing type
mappings applies just the same. We can pass an array type to the
XmlSerializer constructor, call Serialize() or Deserialize()
and the XmlSerializer will process an array just like it would process a scalar
object:
XmlSerializer xs = new XmlSerializer(typeof(Vehicle[]));
Vehicle[] vehicles = new Vehicles[2];
// …
Serializer.Serialize( writer, vehicles );
A Vehicle array with the classes from the previous section
serialized to XML would result in a structure like this:
<ArrayOfVehicle>
<Vehicle xsi:type="Car">…</Vehicle>
<Vehicle
xsi:type="Motorcycle">…</Vehicle>
</ArrayOfVehicle>
The serializer added a parent node around all the elements to
group all array elements, otherwise the output would not be well-formed XML.
The objects inside the array were serialized just like they were in section
9.4.2, because the declared type of the array items was Vehicle, but in this
case the items were of type Car and Motorcycle. If a class defines a field of
an array type this parent node is not created automatically.
There are a number of options to customize how the XmlSerializer maps array types to XML types. Annotating a
field of an array type with a plain XmlElement attribute, without a type or
element name specification, causes the array elements to be serialized as
direct children of the class root. No additional parent node is inserted around
the items in the array. Just like we did with scalar members, we can also
specify as many instances of the XmlElement attribute if we are mapping to a
choice model group instead of a simple sequence.
The next listing shows a ParkingLot class with an array for two Vehicles and the XML
structure of a serialized instance. Again, just like we did for a scalar Vehicle
field in 9.4.3 we attach an XmlElement attribute for each type derived from Vehicle.
The XML layout for this ParkingLot class is also described by a <choice>
model group. In contrast to the schema in listing 9.5 the maxOccurs attribute
for the group would be greater than 1.
OK, now we know how to omit a parent element around array items
and how to customize the array items themselves. But what if we want to
customize element names and still want that intermediate node around the array
items?
That’s what the XmlArray attribute and the XmlArrayItem attributes are for.
The former creates a parent node around the array items and optionally
specifies the node’s name, the latter is similar to the XmlElement attribute,
but provides additional functionality specific to arrays. One use of the XmlArray
attribute is mapping a field to an element like ParkedVehicles in the following
schema, which can contain zero or more Vehicle elements.
The XmlArray attribute in combination with the XmlArrayItem
attribute allows us to control the element names of the array items. The
XmlArrayItem attribute also allows us to write classes mapping to more complex
XSD constructs like a choice model group which can occur more than once. With a
schema definition like the one below the ParkedVehicles element can contain any
number of ParkedCar and ParkedMotorcycle elements in any order.
The following code listing shows a class, which maps to the
schema above. The
XmlArray attribute on the Vehicles field changes the name of the
array’s parent node from the default Vehicles to “ParkedVehicles” as required
by the schema. Since XmlElement and XmlArray attributes are mutually exclusive
we have to resort to the XmlArrayItem attribute to declare the different choice
particles to the XmlSerializer. We have to describe each choice particle with
an XmlArrayItem attribute.
1
Listing 9.6: An
application of theXmlArray attribute and the XmlArrayItem attribute
The XmlSerializer needs to know about the type of all items
inside an array to correctly map them to XML. If an array contains an element
of a type that was not specified either by an
XmlInclude attribute or an XmlElement attribute the
serializer will throw an exception. In addition to these two attributes we can
declare additional types by attaching XmlArrayItem attributes with the Type
property set.