Skip to content

Mapping documentation

Muhamamd Miftah edited this page Oct 22, 2019 · 5 revisions

LINQ to XSD Mapping Documentation

Who should read this?

The following document systematically describes the mapping of XML schemas to .NET object models. The normal user of the LINQ to XSD technology is not necessarily assumed to work with this documentation. The idea is that the implemented mapping is mostly intuitive and that intellisense and object browsing provide sufficient help for the developer. The documentation mainly serves as an informal specification.

Notational conventions for OO APIs

The resulting .NET classes are described in C#-like pseudo code so as to hide routine technicalities.

All shown classes and members are implicitly public.

  • Hence, the keyword public is universally omitted.
  • Constructors are marked by the keyword constructor -- for clarity.
  • We are not using C#'s verbose notation "static explicit operator".
  • Instead we use the short notation explicit cast.
  • We elide the implementations of all property members.
  • To this end, we use "interface notation": { get; set; }.

OO API (Illustration of notation)

class Foo : XTypedElement
{
   	constructor Foo();
   	explicit cast Foo(System.Xml.Linq.XElement xe);
   	property string Bar	{ get; set; }
}

Global types and elements

Subsections

Further reading

  • XSD's forms of element and type substitution are discussed separately.
  • Some other abstraction forms such as attribute declarations are mapped trivially.

Complex-type definitions

Complex-type definitions are mapped to classes.Each generated class comprises the following members:

  • a default constructor;
  • an explicit cast from XElement to the defined class;
  • some inherited API members such as Save; see the LINQ to XSD manual;
  • further members depending on the content model. The class that is generated for a complex type subclasses XTypedElement. In fact, a derived complex-type is mapped to a subclass of the class that is generated for its base type.

Schema sample (Complex-type definitions)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- A complex type for addresses -->
     <xs:complexType name="Address">
          <!-- address details omitted -->
     </xs:complexType>
</xs:schema>

OO API

class Address : XTypedElement
{
   	constructor Address();
   	explicit cast Address(System.Xml.Linq.XElement xe);
}

Roots with anonymous, complex types

We use the term "root" to refer to global element declarations in an XML schema.

We recall that a valid instance must be necessarily rooted in an element of that kind.

Such roots are essentially mapped in the same way as complex-type definitions.

Schema sample (Roots with anonymous, complex types)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- A global element declaration for addresses -->
     <xs:element name="Address">
          <xs:complexType>
               <!-- address details omitted -->
          </xs:complexType>
     </xs:element>
</xs:schema>

OO API

class Address : XTypedElement
{
   	constructor	  	Address();
   	explicit cast	  	Address(System.Xml.Linq.XElement xe);
}

The conceptual difference between complex-type definitions and root element declarations is worth recalling: The latter define proper types in the sense of sets of elements, whereas the former model "incomplete" elements because element tags are not prescribed. This conceptual difference is reflected in the behavior of the generated object types. That is, the complex-type name serves as "preliminary" element tag for any instance of the class that corresponds to the complex type in question; this preliminary tag is eventually resolved to a proper element tag, when the instance is parented or when it is wrapped in a root element.

Note: Save and Load members are potentially misleading for classes that were generated from complex-type definitions since de-/serialization is only well-defined for root-element declarations. It is conceivable that a future release of LINQ to XSD makes explicit the conceptual difference between complex-type definitions and root element declarations.

Roots with type references to complex types

This pattern makes combined use of a complex-type definition and a global element declaration.

Schema sample (Roots with type references to complex types)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:element name="BillTo" type="Address"/>
     <xs:element name="ShipTo" type="Address"/>
     <xs:complexType name="Address">
          <xs:sequence>
               <xs:element name="Name" type="xs:string"/>
               <!-- ... -->
          </xs:sequence>
     </xs:complexType>
</xs:schema>

This style is mapped as follows:

  • The following classes are generated in this case:
    • There is one class for each element declaration ("the envelope").
    • There is one class for each complex type ("the content").
  • Envelopes can be constructed from content by means of a non-default constructor.
  • The content can be extracted from the envelope by means of the Content member.
  • The "envelope" class implements the API of the "content" class by forwarding.

OO API (Roots with type references to complex types)

class Address : XTypedElement
{
   	constructor	  	Address();
   	explicit cast	  	Address(System.Xml.Linq.XElement xe);
   	property	  	string	  	Name	  	{ get; set; }
}
class BillTo : XTypedElement
{
   	constructor	  	BillTo();
   	constructor	  	BillTo(Address content);
   	explicit cast	  	BillTo(System.Xml.Linq.XElement xe);
   	property	  	Address	  	Content	  	{ get;  }
   	property	  	string	  	Name	  	{ get; set; }
}
class ShipTo : XTypedElement
{
   	constructor	  	ShipTo();
   	constructor	  	ShipTo(Address content);
   	explicit cast	  	ShipTo(System.Xml.Linq.XElement xe);
   	property	  	Address	  	Content	  	{ get;  }
   	property	  	string	  	Name	  	{ get; set; }
}

The combined use of element declarations and complex-type definitions leads to potentially convoluted object models, when the most direct mapping is applied. Hence, this usage pattern of XSD may suggest schema normalizations. Note: The current release of LINQ to XSD does not yet provide any relevant schema normalization.

Simple-type definitions

Schema sample (Simple-type definitions)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:simpleType name="Month">
          <xs:restriction base="xs:token">
               <!-- restriction facets omitted -->
          </xs:restriction>
     </xs:simpleType>
</xs:schema>

By default, simple-type definitions are not mapped to any class that the programmer is supposed to instantiate.

Note: The current release of LINQ to XSD generates a vacuous class for each simple-type definition. This class should be omitted. By customization, one can also request a class that is designated to a simple-type definition.

Note: This option not supported by the current release of LINQ to XSD. The use of designated classes offers two benefits: (i) simple-type names from the XML schema can be used in program code; (ii) substitutability for simple types (say, subtyping in OO terms) can be precisely expressed.

Roots with anonymous, simple types

Root element declarations (with anonymous types or not) are mapped to classes.

Schema sample (Roots with anonymous, simple types)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:element name="Month">
          <xs:simpleType>
               <xs:restriction base="xs:string">
                    <!-- restriction facets omitted -->
               </xs:restriction>
          </xs:simpleType>
     </xs:element>
</xs:schema>

OO API

class Month : XTypedElement
{
   	constructor	  	Month();
   	explicit cast	  	Month(System.Xml.Linq.XElement xe);
   	property	  	string	  	TypedValue	  	{ get; set; }
}

The (simple) content of such root elements can be access with a designated TypedValue property (akin to LINQ to XML's untyped value property). The type of this property is the built-in base type of the anonymous simple type. As usual, the class also provides an explicit coercion (cast) from XElement to the class.

Roots with type references to simple types

Schema sample (Roots with type references to simple types)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:element name="Month" type="MonthType"/>
     <xs:simpleType name="MonthType">
          <xs:restriction base="xs:string">
               <!-- restriction facets omitted -->
          </xs:restriction>
     </xs:simpleType>
</xs:schema>

OO API

class Month : XTypedElement
{
   	constructor	  	Month();
   	constructor	  	Month(string content);
   	explicit cast	  	Month(System.Xml.Linq.XElement xe);
   	property	  	string	  	TypedValue	  	{ get; set; }
}

The (simple) content of such root elements can be access with a designated TypedValue property (akin to LINQ to XML's untyped value property). The type of this property is the OO counterpart for the built-in XSD base type of the referenced simple type. As usual, the class also provides an explicit coercion (cast) from XElement to the class.

Clone this wiki locally