November 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Using Open XML Schema with .NET

  • August 18, 2004
  • By Mickey Williams
  • Send Email »
  • More Articles »

Using Open Elements and Attributes

When an application encounters an XML document that contains some unexpected elements, there are multiple outcomes that you can choose from. If your only requirements are that debugging information be logged during deserialization, you can simply handle the UnknownElement and UnknownAttribute events, as shown earlier. But, what if you actually need to preserve the contents and structure of the data from the XML document so that it can be passed to another system component? The .NET Framework includes two attributes that enable you to capture unknown XML elements and attributes:

  • XmlAnyElementAttribute is used to control how unknown elements are stored in an object.
  • XmlAnyAttributeAttribute is used to control how unknown attributes are stored in an object.

XmlAnyElementAttribute is attached to a field, property, parameter, or return value and is essentially a "wild card" that is capable of converting to and from XML elements during serialization. You'll probably want to use it with an array of XmlNode objects, becaise each unknown element from the XML document will be placed into its own XmlNode object. If you use the attribute with a single XmlNode object, you'll lose data if multiple unknown elements are received. The XmlAnyElement attribute is typically used like this:

XmlNode[] _openElements = null;
[XmlAnyElement()]
public XmlNode[] OpenElements
{
   get { return _openElements; }
   set { _openElements = value; }
}

XmlAnyAttribute can be used with an array of XmlAttribute or XmlNode (it can't be used with single objects of either type). It's typically used much like the XmlAnyElement, as shown in the code below:

XmlNode[] _openAttributes = null;
[XmlAnyAttribute()]
public XmlNode[] OpenAttributes
{
   get { return _openAttributes; }
   set { _openAttributes = value; }
}

When you use XmlAnyElement and XmlAnyAttribute attributes, the serializer will not generate UnknownElement and UnknownAttribute events.

Validating with Schema and XmlAnyElement

Now, let's consider how open elements affect validation. Many systems use XML schema to validate incoming XML documents. For a large number of applications, the default non-validated behavior is undesirable, for some of the same reasons that most developers prefer strongly typed programming languages. When messages arrive at a server application, these developers want to know whether the data arrives with unexpected formatting or content. The standard answer in this case is to use XML schema, which will reject XML documents that are presented with an unrecognized structure.

However, blindly using XML simply exchanges one problem for another. As discussed earlier, XML documents that are agile offer advantages over fixed structures. When XML is used as the common language for data exchange in an enterprise, XML schema can cause undesirable rigidity in the communication infrastructure—if documents don't precisely match the expected structure, they're rejected.

If a typical schema (such as one inferred from an existing XML instance document) is used to validate messages, the schema can become a brake on system evolution. Instead of avoiding schema altogether, or attempting to update all components that use a particular schema simultaneously, you can use an XML schema that defines open elements and attributes to create a schema that has just enough structure to validate specific behavior, while maintaining flexibility for extension.

Using an XML schema with open elements and attributes enables you to balance consistency with extendibility. Open elements and attributes create expansion points that enable clients to send XML documents that follow an updated structure to a server, without the need to update all edges of a system simultaneously. It also enables a client to use XML schema that is shared among multiple applications—if a client is updated prior to the server, the server can simply ignore the updated elements for the purposes of schema validation.

The process begins by defining open elements in your schema, using xs:any type to indicate the part of the XML element that's open for undefined elements, as shown in the following code.

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="order" targetNamespace="http://schema.servergeek.com/
                                              2004/7"
                      xmlns:bks="http://schema.servergeek.com/2004/7"
                      xmlns:xs="http://www.w3.org/2001/XMLSchema"
                      elementFormDefault="qualified"
                      attributeFormDefault="qualified">
   <xs:element name="Shipment">
      <xs:complexType>
         <xs:sequence>
           <xs:element name="OrderNo" type="xs:string"/> 
           <xs:element name="Location">
              <xs:complexType>
                 <xs:sequence>
                   <xs:element name="Addr" type="xs:string" />
                     <xs:element name="City" type="xs:string" />
                     <xs:element name="State" type="xs:string" />
                     <xs:sequence>
                        <xs:any namespace="##any" minOccurs="0"
                                maxOccurs="unbounded"
                                processContents="skip" />
                     </xs:sequence>
                  </xs:sequence>
              </xs:complexType>
            </xs:element>
            <xs:sequence>
              <xs:any namespace="##any" 
                      minOccurs="0" maxOccurs="unbounded"
                      processContents="skip" />
            </xs:sequence>
         </xs:sequence>
      </xs:complexType>
   </xs:element>
</xs:schema>

The definition of a sequence of zero or more xs:any elements in this schema enables any number of additional XML nodes to be included in the XML document inside a Location element as well as immediately after the element.

In this example, the namespace for the open elements are declared as '##any', which is actually the default value. Alternatively, you can supply the specific namespace that is allowed, or another predefined namespace token:

  • ##other The XML must be from any namespace other than the target namespace is allowed.
  • ##local The XML must be must not be in a namespace.
  • ##targetNamespace The XML must be in the target namespace.

A specific allowed namespace can also be provided for this attribute.

The processContents attribute in the xs:any element is set to 'skip' in this example, which instructs a schema validator to ignore the nodes that are part of the open element. A complete list of options follows:

  • lax: Enforce schema if a namespace is declared and the validator has access to the schema definition.
  • skip: No schema enforcement.
  • strict: Always enforce schema for this open element.

Open sets of attributes can also be defined using a similar syntax, as shown below:

<xs:schema id="cars"
           targetNamespace="http://schema.servergeek.com/2004/7"
           xmlns:bks="http://schema.servergeek.com/2004/7"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified"
           attributeFormDefault="qualified">
   <xs:element name="Car">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="Color" type="xs:string" />
            ...
            </xs:sequence>
         <xs:anyAttribute namespace='##any'
                          processContents='skip' />
      </xs:complexType>
   </xs:element>
</xs:schema>

Open attributes follow the same rules as elements with regard to namespace and processContents. This schema fragment defines an element named Car that is allowed to have additional attributes without namespace restriction or schema enforcement.

Taken to an extreme, consider a gateway component that only tests a Shipment XML message for the presence of an OrderNo element. The schema used by this component could be reduced to something like this:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="order"
           targetNamespace="http://schema.servergeek.com/2004/7"
           xmlns:bks="http://schema.servergeek.com/2004/7"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified"
           attributeFormDefault="qualified">
   <xs:element name="Shipment">
      <xs:complexType>
         <xs:sequence>
           <xs:element name="OrderNo" type="xs:string"/>
              <xs:sequence>
                <xs:any namespace="##any" minOccurs="0"
                        maxOccurs="unbounded"
                        processContents="skip" />
              </xs:sequence>
         </xs:sequence>
      </xs:complexType>
   </xs:element>
</xs:schema>

This enables documents to have additional elements that can pass as schema-valid until an updated schema is provided. It's also useful when performing SOA-like validation, where different actors have diverse validation needs. Components and applications have up-to-date knowledge of message requirements. Components that serve as intermediaries can perform coarser validation on those portions of the message schema that are required by the component.

Summary

In this article, I've discussed two complimentary approaches to using XML messaging in enterprise applications. The XmlAnyElement and XmlAnyAttribute attributes are used to capture open XML content in your .NET classes. The xs:any and xs:anyAttribute schema elements are used to define expansion points and enable just enough schema validation for system components.

More Information

More information about XML Schema is available at the W3C's XML Schema page at: http://www.w3.org/XML/Schema

About the Author

Mickey Williams is a Microsoft C# MVP, and the author of Microsoft Visual C# .NET Core Reference for Microsoft Press. He works as a Principal Consultant for Neudesic, LLC in Southern California, building service-oriented applications for enterprise customers. His weblog can be found at http://www.servergeek.com/blogs/mickey.





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel