Support for XSD and Validation of
Typed Nodes
In many of the previous examples, we used explicit casting
and accessed nodes through their names as quoted strings. All those casts and
quotes are not type safe and cannot be checked at compile time. However, XML
document structure is often defined using an XML Schema Definition (XSD). There
is a LINQ to XSD project that allows access to XML nodes using a typed and
self-describing approach, and it is supported by Microsoft IntelliSense. For
instance, the last query you saw in the previous section
would be written like the one in Listing 6-35.
Listing 6-35: A
LINQ query over XML, based on an XSD typed approach
var ordersWithCustomersFromXml =
from c in xmlCustomers.customerCollection
join o in orders
on c.Name equals o.Name
orderby c.Name
select new {
Name = c.Name,
City = c.City,
IdProduct = o.IdProduct,
Quantity = o.Quantity };
As you can see, when using this approach the XML nodes graphs look
like any other object graphs-regardless of whether they are made of elements,
attributes, or nodes instead of objects. Keep in mind that the LINQ to XSD
project is still under construction at the time of writing this book.
XML schema support is also offered through some extension methods
defined in the System.Xml.Schema.Extensions class of
the System.Xml.Linq assembly. There are just a couple
of methods with a few overloads. Those methods are GetSchemaInfo,
which extends any XElement or XAttribute
instance, and Validate, which extends XDocument,
XElement,and XAttribute. The
first method (GetSchemaInfo) returns an annotation of
type System.Xml.Schema.IXmlSchemaInfo taken from the
current node, if present. It retrieves a schema definition mapped to the
current node by using LINQ to XML annotations. The Validate
method, as you can figure out from its name, validates the source XML node
using an XmlSchemaSet containing the schemas to use.
Consider the XML schema shown in Listing 6-36.
Listing 6-36: An
XML Schema Definition for our sample list of customers
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema id="Customer"
targetNamespace="http://schemas.devleap.com/Customer"
elementFormDefault="qualified"
xmlns="http://schemas.devleap.com/Customer"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="customers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="customer" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="city" type="xsd:string" use="required" />
<xsd:attribute name="country">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Italy" />
<xsd:enumeration value="USA" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
You can define an XML graph with this structure, using LINQ to XML
as usual, and map the nodes to the previous schema using an XNamespace
instance. An example is shown in Listing 6-37.
Listing 6-37: An
XML document with the schema of Listing 6-36,
built using the LINQ to XML API
XNamespace ns = "http://schemas.devleap.com/Customer";
XDocument xmlCustomers = new XDocument(
new XElement(ns + "customers",
from c in customers
select new XElement(ns + "customer",
new XAttribute("city", c.City),
new XAttribute("name", c.Name),
new XAttribute("country", c.Country))));
At this point, you have the xmlCustomers variable
that represents an XML Infoset instance related to the schema of
Listing 6-36 using its corresponding XML namespace.
In Listing 6-38, you can
see how to validate this XDocument using the
Validate extension method.
Listing 6-38: XML
validation using the Validate extension method
static void validateXDocument() {
// ...
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(XmlSchema.Read(new StreamReader(@"..\..\customer.xsd"), null));
xmlCustomers.Validate(schemas, xmlCustomers_validation);
}
static void xmlCustomers_validation(Object source, ValidationEventArgs args) {
// In case of validation messages
Console.WriteLine(args.Message);
}
The Validate method internally uses
all the standard and common classes and tools of the System.Xml.Schema
namespace.