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.

Content models

Subsections

Executive summary

  • Element particles are mapped to properties.
  • Properties exhibit an XPath-like semantics for the child axis.
  • Sequence groups map to a group of properties for their element particles.
  • Choices are mapped like sequences + mutual exclusion semantics.
  • Optionality is modeled with null or nullable.
  • Repetition is modeled with IList.
  • Element-name recurrence maps to single properties.
  • The nesting of content models is not directly exhibited by the resulting API.
  • The API for nested content models is limited in terms of construction and updates.
  • Local element declarations of an anonymous types imply a nested class.
  • Element and attribute wildcards are mapped to special properties.

Flat sequences

Characteristics of flat sequences:

  • The content model is defined by a sequence group.
  • The sequence compositor occurs directly below a named complex-type element.
  • The children of the sequence group are (local) element declarations.
  • The names of the elements in the sequence are distinct. For now, additional restrictions are assumed, for mere simplicity:
  • The hosting complex type does not declare any attributes.
  • The elements in the sequence are required. Without loss of generality, we use a complex-type definition to host the content model. (That is, we could also use a root-element declaration.)

Schema sample (Flat sequences)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- A very simple type of addresses -->
     <xs:complexType name="Address">
          <xs:sequence>
               <xs:element name="Name" type="xs:string"/>
               <xs:element name="Zip" type="xs:int"/>
               <xs:element name="Street" type="xs:string"/>
          </xs:sequence>
     </xs:complexType>
</xs:schema>

Flat sequences are mapped as follows:

  • (The hosting complex type is mapped to a class.)
  • The (local) element declarations are mapped to properties.
  • There are getters and setters for each property.
  • The precise mapping of element particles to properties is discussed elsewhere.

OO API

class Address : XTypedElement
{
   	constructor	  	Address();
   	explicit cast	  	Address(System.Xml.Linq.XElement xe);
   	property	  	string	  	Name	  	{ get; set; }
   	property	  	int	  	Zip	  	{ get; set; }
   	property	  	string	  	Street	  	{ get; set; }
}

Type and element references

Executive summary

  • There are OO counterparts for the built-in XSD simple types.
  • Element references are mapped to the class name for the referenced element.
  • Likewise for type references for complex types.
  • Derived simple types do not "nominally" participate in the mapping.
  • The name of a local element declaration defines the property name.
  • Element names and names of referenced types are subject to name mapping.

The following schema exercises type references for simple and complex types.

Schema sample (Type references)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- A host for the references to follow -->
     <xs:complexType name="Customer">
          <xs:sequence>
               <xs:element name="Name" type="xs:string"/>
               <xs:element name="Age" type="AgeType"/>
               <xs:element name="Address" type="AddressType"/>
          </xs:sequence>
     </xs:complexType>
     <!-- A complex type to be referenced -->
     <xs:complexType name="AddressType">
          <xs:sequence>
               <xs:element name="Zip" type="xs:int"/>
               <xs:element name="Street" type="xs:string"/>
          </xs:sequence>
     </xs:complexType>
     <!-- A simple type to be referenced -->
     <xs:simpleType name="AgeType">
          <xs:restriction base="xs:int">
               <xs:minInclusive value="0"/>
               <xs:maxInclusive value="200"/>
          </xs:restriction>
     </xs:simpleType>
</xs:schema>

Simple-type references are mapped, by default, to references of the OO counterpart of the built-in XSD base type of the referenced type. The constraints for the simple type are enforced by the properties for the corresponding elements. Note: In the current release of LINQ to XSD, setters are validated, but not getters. This may be change. Complex-type references are always mapped to the class name for the complex type.

OO API (Type references)

class Customer : XTypedElement
{
   	constructor	  	Customer();
   	explicit cast	  	Customer(System.Xml.Linq.XElement xe);
   	property	  	string	  	Name	  	{ get; set; }
   	property	  	int	  	Age	  	{ get; set; }
   	property	  	AddressType	  	Address	  	{ get; set; }
}
class AddressType : XTypedElement
{
   	constructor	  	AddressType();
   	explicit cast	  	AddressType(System.Xml.Linq.XElement xe);
   	property	  	int	  	Zip	  	{ get; set; }
   	property	  	string	  	Street	  	{ get; set; }
}

The following schema varies the above schema such that:

  • It uses element declarations instead of type definitions;
  • It uses element references instead of local element declarations with type references.

Schema sample (Element references)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- A host for the references to follow -->
     <xs:element name="Customer">
          <xs:complexType>
               <xs:sequence>
                    <xs:element name="Name" type="xs:string"/>
                    <xs:element ref="Age"/>
                    <xs:element ref="Address"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
     <!-- A referable element of a complex type -->
     <xs:element name="Address">
          <xs:complexType>
               <xs:sequence>
                    <xs:element name="Zip" type="xs:int"/>
                    <xs:element name="Street" type="xs:string"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
     <!-- A referable element of a simple type -->
     <xs:element name="Age">
          <xs:simpleType>
               <xs:restriction base="xs:int">
                    <xs:minInclusive value="0"/>
                    <xs:maxInclusive value="200"/>
               </xs:restriction>
          </xs:simpleType>
     </xs:element>
</xs:schema>

Element references are always mapped to "wrapper" references. This rule is applied regardless of whether the content model is or complex.

OO API (Element references)

class Customer : XTypedElement
{
   	constructor	  	Customer();
   	explicit cast	  	Customer(System.Xml.Linq.XElement xe);
   	property	  	string	  	Name	  	{ get; set; }
   	property	  	Age	  	Age	  	{ get; set; }
   	property	  	Address	  	Address	  	{ get; set; }
}
class Address : XTypedElement
{
   	constructor	  	Address();
   	explicit cast	  	Address(System.Xml.Linq.XElement xe);
   	property	  	int	  	Zip	  	{ get; set; }
   	property	  	string	  	Street	  	{ get; set; }
}
class Age : XTypedElement
{
   	constructor	  	Age();
   	explicit cast	  	Age(System.Xml.Linq.XElement xe);
   	property	  	int	  	TypedValue	  	{ get; set; }
}

Optional elements

An element is optional, if the (local) element declaration is attributed as follows:

  • There is an attribute minOccurs="0".
  • The value of the maxOccurs is 1 (which is the default). The mapping of optionality depends on the mapping of the involved content type:
  • If the element type is mapped to a CLR-value type, then optionality maps to nullable types.
  • Otherwise, optionality is not explicitly represented by the resulting CLR reference type.
  • Clearly, all complex-content types are mapped to CLR reference types.
  • Hence, optional and required elements of a complex content type are mapped in the same way.

Schema sample (Optional elements)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- Customer information with optional age and shipping-address information -->
     <xs:complexType name="Customer">
          <xs:sequence>
               <xs:element name="Name" type="xs:string"/>
               <xs:element name="Age" type="xs:int" minOccurs="0"/>
               <xs:element name="BillAddress" type="Address"/>
               <xs:element name="ShipAddress" type="Address" minOccurs="0"/>
          </xs:sequence>
     </xs:complexType>
     <!-- Part 1 of street address is required; part 2 is optional -->
     <xs:complexType name="Address">
          <xs:sequence>
               <xs:element name="Zip" type="xs:int"/>
               <xs:element name="Street1" type="xs:string"/>
               <xs:element name="Street2" type="xs:string" minOccurs="0"/>
          </xs:sequence>
     </xs:complexType>
</xs:schema>

OO API (Use of nullable types)

class Customer : XTypedElement
{
   	constructor	  	Customer();
   	explicit cast	  	Customer(System.Xml.Linq.XElement xe);
   	property	  	string	  	Name	  	{ get; set; }
   	property	  	int?	  	Age	  	{ get; set; }
   	property	  	Address	  	BillAddress	  	{ get; set; }
   	property	  	Address	  	ShipAddress	  	{ get; set; }
}
class Address : XTypedElement
{
   	constructor	  	Address();
   	explicit cast	  	Address(System.Xml.Linq.XElement xe);
   	property	  	int	  	Zip	  	{ get; set; }
   	property	  	string	  	Street1	  	{ get; set; }
   	property	  	string	  	Street2	  	{ get; set; }
}

Given the missing disoverability of optionality for reference types, the classes generated by LINQ to XSD may document optionality as part of the tool tips for the relevant properties.

Repeating elements

An element is repeating, if the element declaration is attributed as follows:

  • The value of the maxOccurs attribute is different from 0 and 1 (default).
  • We say that the particle is a possibly empty list particle if minOccurs="0".
  • Otherwise, we say that the particle is a non-empty list particle.

The mapping of repeating elements is defined as follows:

  • Suppose t is result of mapping the element type of the particle.
  • Then, the property type for the mapped element particle is IList appled to t.
  • Hence, the observable CLR type does not resemble the precise occurrence constraints.

Schema sample (Repeating elements)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- A figure is a potentially empty list of lines. -->
     <xs:element name="Figure">
          <xs:complexType>
               <xs:sequence>
                    <xs:element ref="Line" minOccurs="0" maxOccurs="unbounded"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
     <!-- A line consists of 2 or more points. -->
     <xs:element name="Line">
          <xs:complexType>
               <xs:sequence>
                    <xs:element ref="Point" minOccurs="2" maxOccurs="unbounded"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
     <xs:element name="Point">
          <xs:complexType>
               <xs:sequence>
                    <xs:element name="xCoord" type="xs:int"/>
                    <xs:element name="yCoord" type="xs:int"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
</xs:schema>

OO API (Use of IList interface)

class Figure : XTypedElement
{
   	constructor	  	Figure();
   	explicit cast	  	Figure(System.Xml.Linq.XElement xe);
   	property	  	IList<Line>	  	Line	  	{ get; set; }
}
class Line : XTypedElement
{
   	constructor	  	Line();
   	explicit cast	  	Line(System.Xml.Linq.XElement xe);
   	property	  	IList<Point>	  	Point	  	{ get; set; }
}
class Point : XTypedElement
{
   	constructor	  	Point();
   	explicit cast	  	Point(System.Xml.Linq.XElement xe);
   	property	  	int	  	xCoord	  	{ get; set; }
   	property	  	int	  	yCoord	  	{ get; set; }
}

Given the missing disoverability of non-empty versus empty list status (or more specific bounds than 0, 1 and unbounded), the class that are generated by LINQ to XSD may document the status as part of the tool tips for the relevant properties.

Attributes

Attribute declarations are mapped to properties that are added to the class that hosts the complex type of which the attributes are part of. (For simplicity, it is assumed that element and attribute names are distinct in a given scope. Otherwise, special rules of name mapping apply.)

Schema sample (A complex type with an attribute declaration)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:complexType name="Product">
          <xs:sequence>
               <xs:element name="Number" type="xs:integer"/>
               <xs:element name="Name" type="xs:string"/>
          </xs:sequence>
          <xs:attribute name="EffDate" type="xs:date" use="optional"/>
     </xs:complexType>
</xs:schema>

OO API

class Product : XTypedElement
{
   	constructor	  	Product();
   	explicit cast	  	Product(System.Xml.Linq.XElement xe);
   	property	  	decimal	  	Number	  	{ get; set; }
   	property	  	string	  	Name	  	{ get; set; }
   	property	  	System.DateTime?	  	EffDate	  	{ get; set; }
}

OO API (Properties for attributes)

class Product : XTypedElement
{
   	constructor	  	Product();
   	explicit cast	  	Product(System.Xml.Linq.XElement xe);
   	property	  	decimal	  	Number	  	{ get; set; }
   	property	  	string	  	Name	  	{ get; set; }
   	property	  	System.DateTime?	  	EffDate	  	{ get; set; }
}

All attribute declarations can be thought as element particles in a sequence. Hence, the following schema maps to the same object model as the earlier schema. Schema sample (Elements in place of attributes)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:complexType name="Product">
          <xs:sequence>
               <xs:element name="Number" type="xs:integer"/>
               <xs:element name="Name" type="xs:string"/>
               <xs:element name="EffDate" type="xs:date" minOccurs="0"/>
          </xs:sequence>
     </xs:complexType>
</xs:schema>

Flat choices

Characteristics of flat choices:

  • The content model is defined by a choice group.
  • The compositor occurs directly below a complex-type element.
  • The children of the choice group are (local) element declarations.
  • The element names are distinct (required per XSD validity).
  • The element types are described by type references.

Schema sample (Flat choice)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:element name="BillTo">
          <!-- Both street and pobox are of type xs:string. -->
          <xs:complexType>
               <xs:choice>
                    <xs:element name="Street" type="xs:string"/>
                    <xs:element name="Pobox" type="xs:string"/>
               </xs:choice>
          </xs:complexType>
     </xs:element>
</xs:schema>

The choice group is essentially mapped as the same sequence group except that:

  • The particles are mapped to properties as if they were optional particles.
  • Each of the resulting setters unsets all the other setters.

OO API (Flat choices on element particles)

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

Programmatic case discrimination choices can be based on non-null tests per branch that are appropriate casceded in conditionals. For a more restricted form of choices, convenience for construction can be provided. That is, when the choice at hand uses plain element particles with distinct element types that are also mapped to different OO types, then we can use the (mapped) types themselves to provide an overloaded constructor.

Schema sample (A choice with distinct element types for the branches)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- Arithmetic expression forms combined by choice -->
     <xs:complexType name="Exp">
          <xs:choice>
               <xs:element name="Const" type="xs:int"/>
               <xs:element name="Add" type="Add"/>
          </xs:choice>
     </xs:complexType>
     <!-- Addition as an expression form -->
     <xs:complexType name="Add">
          <xs:sequence>
               <xs:element name="Left" type="Exp"/>
               <xs:element name="Right" type="Exp"/>
          </xs:sequence>
     </xs:complexType>
</xs:schema>

Note that there are two constructors, corresponding to a binary choice.

OO API

class Exp : XTypedElement
{
   	constructor	  	Exp();
   	constructor	  	Exp(int? Const);
   	constructor	  	Exp(Add Add);
   	explicit cast	  	Exp(System.Xml.Linq.XElement xe);
   	property	  	int?	  	Const	  	{ get; set; }
   	property	  	Add	  	Add	  	{ get; set; }
}
class Add : XTypedElement
{
   	constructor	  	Add();
   	explicit cast	  	Add(System.Xml.Linq.XElement xe);
   	property	  	Exp	  	Left	  	{ get; set; }
   	property	  	Exp	  	Right	  	{ get; set; }
}

The described mapping rules do not readily enable the discoverability of mutually exclusive properties (in the sense of choice), when compared to the combined use of properties (in the sense of sequence). The classes generated by LINQ to XSD may document the content model as part of the tool tips for the relevant properties.

Recurrent element names

An element name is said to be recurrent in a given content model, if the content model comprises multiple element declarations of the given name. Recurrence is mapped such that all the relevant element declarations are mapped to a single property with a type as in the case of a repeating element particle. That is, there is no 1:1 correspondence of element particles and properties; instead there is 1:1 correspondence of element names and properties.

Schema sample (Recurrent element names)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <!-- A figure as a list of lines -->
     <xs:element name="Figure">
          <xs:complexType>
               <xs:sequence>
                    <xs:element ref="Line" minOccurs="0" maxOccurs="unbounded"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
     <!-- A line that consist of two points. -->
     <xs:element name="Line">
          <xs:complexType>
               <xs:sequence>
                    <xs:element ref="Point"/>
                    <xs:element ref="Point"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
     <xs:element name="Point">
          <xs:complexType>
               <xs:sequence>
                    <xs:element name="xCoord" type="xs:int"/>
                    <xs:element name="yCoord" type="xs:int"/>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
</xs:schema>

OO API

class Figure : XTypedElement
{
   	constructor	  	Figure();
   	explicit cast	  	Figure(System.Xml.Linq.XElement xe);
   	property	  	IList<Line>	  	Line	  	{ get; set; }
}
class Line : XTypedElement
{
   	constructor	  	Line();
   	explicit cast	  	Line(System.Xml.Linq.XElement xe);
   	property	  	IList<Point>	  	Point	  	{ get; set; }
}
class Point : XTypedElement
{
   	constructor	  	Point();
   	explicit cast	  	Point(System.Xml.Linq.XElement xe);
   	property	  	int	  	xCoord	  	{ get; set; }
   	property	  	int	  	yCoord	  	{ get; set; }
}

Nested content models

Schema sample (Nested, repeating sequences)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:complexType name="JobOffer">
          <xs:sequence>
               <xs:element name="JobId" type="xs:string"/>
               <xs:sequence minOccurs="0" maxOccurs="unbounded">
                    <xs:element name="Name" type="xs:string"/>
                    <xs:element name="SSN" type="xs:int"/>
               </xs:sequence>
          </xs:sequence>
     </xs:complexType>
</xs:schema>

The LINQ to XSD mapping is instance-oriented. That is, the nesting of compositors is not represented by the API of the classes generated by LINQ to XSD. Instead, all element names in a content model give rise to properties. We refer to the discussion of "append semantics" in the overview and the manual for LINQ to XSD.

OO API

class JobOffer : XTypedElement
{
   	constructor	  	JobOffer();
   	explicit cast	  	JobOffer(System.Xml.Linq.XElement xe);
   	property	  	string	  	JobId	  	{ get; set; }
   	property	  	IList<string>	  	Name	  	{ get; set; }
   	property	  	IList<int>	  	SSN	  	{ get; set; }
}

Anonymous types

The mapping for root elements with anonymous types was defined elsewhere. The mapping for local elements with anonymous simple types is trivial because the (derived) simple type or the type union or list type is reduced to the base built-in type as far as the API type is concerned. The case of local elements with anonymous complex types remains.

Schema sample (A schema with nested, anonymous, complex types)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:element name="Bib">
          <xs:complexType>
               <xs:sequence>
                    <xs:element maxOccurs="unbounded" name="Book">
                         <xs:complexType>
                              <xs:sequence>
                                   <xs:choice maxOccurs="unbounded">
                                        <xs:element name="Title" type="xs:string"/>
                                        <xs:element maxOccurs="unbounded" name="Author">
                                             <xs:complexType>
                                                  <xs:sequence>
                                                       <xs:element name="Last" type="xs:string"/>
                                                       <xs:element name="First" type="xs:string"/>
                                                  </xs:sequence>
                                             </xs:complexType>
                                        </xs:element>
                                        <xs:element name="Editor">
                                             <xs:complexType>
                                                  <xs:sequence>
                                                       <xs:element name="Last" type="xs:string"/>
                                                       <xs:element name="First" type="xs:string"/>
                                                       <xs:element name="Affiliation">
                                                            <xs:complexType>
                                                                 <xs:simpleContent>
                                                                      <xs:extension base="xs:string">
                                                                           <xs:attribute name="Type" type="xs:string" use="required"/>
                                                                      </xs:extension>
                                                                 </xs:simpleContent>
                                                            </xs:complexType>
                                                       </xs:element>
                                                  </xs:sequence>
                                             </xs:complexType>
                                        </xs:element>
                                        <xs:element name="Publisher" type="xs:string"/>
                                        <xs:element name="Price" type="xs:decimal"/>
                                   </xs:choice>
                              </xs:sequence>
                              <xs:attribute name="Year" type="xs:unsignedShort" use="required"/>
                              <xs:attribute name="Anson" type="xs:boolean" use="optional"/>
                              <xs:attribute name="Joe" type="xs:string" use="optional"/>
                         </xs:complexType>
                    </xs:element>
               </xs:sequence>
          </xs:complexType>
     </xs:element>
</xs:schema>

By default, such nesting of elements and types is mapped to nesting of OO classes.

OO API (Default mapping)

class Bib : XTypedElement
{
   	constructor	  	Bib();
   	explicit cast	  	Bib(System.Xml.Linq.XElement xe);
   	
class BookLocalType : XTypedElement
{
   	constructor	  	BookLocalType();
   	explicit cast	  	BookLocalType(System.Xml.Linq.XElement xe);
   	
class AuthorLocalType : XTypedElement
{
   	constructor	  	AuthorLocalType();
   	explicit cast	  	AuthorLocalType(System.Xml.Linq.XElement xe);
   	property	  	string	  	Last	  	{ get; set; }
   	property	  	string	  	First	  	{ get; set; }
}
   	
class EditorLocalType : XTypedElement
{
   	constructor	  	EditorLocalType();
   	explicit cast	  	EditorLocalType(System.Xml.Linq.XElement xe);
   	
class AffiliationLocalType : XTypedElement
{
   	constructor	  	AffiliationLocalType();
   	explicit cast	  	AffiliationLocalType(System.Xml.Linq.XElement xe);
   	property	  	string	  	TypedValue	  	{ get; set; }
   	property	  	string	  	Type	  	{ get; set; }
}
   	property	  	string	  	Last	  	{ get; set; }
   	property	  	string	  	First	  	{ get; set; }
   	property	  	AffiliationLocalType	  	Affiliation	  	{ get; set; }
}
   	property	  	IList<string>	  	Title	  	{ get; set; }
   	property	  	IList<AuthorLocalType>	  	Author	  	{ get; set; }
   	property	  	IList<EditorLocalType>	  	Editor	  	{ get; set; }
   	property	  	IList<string>	  	Publisher	  	{ get; set; }
   	property	  	IList<decimal>	  	Price	  	{ get; set; }
   	property	  	ushort	  	Year	  	{ get; set; }
   	property	  	bool?	  	Anson	  	{ get; set; }
   	property	  	string	  	Joe	  	{ get; set; }
}
   	property	  	IList<BookLocalType>	  	Book	  	{ get; set; }
}

However, one can also request a transformation for de-anonymization. The topic of schema normalization is discussed in the LINQ to XSD manual. De-anonymization modifies the mapping such that anonymous complex types are defined globally, while the local element names are leveraged as type names, when this is possible without introducing type clashes. Accordingly, the resulting object model does not comprise any nested classes.

OO API (Mapping with de-anonymization)

class Bib : XTypedElement
{
   	constructor	  	Bib();
   	explicit cast	  	Bib(System.Xml.Linq.XElement xe);
   	property	  	IList<Book>	  	Book	  	{ get; set; }
}
class Book : XTypedElement
{
   	constructor	  	Book();
   	explicit cast	  	Book(System.Xml.Linq.XElement xe);
   	property	  	IList<string>	  	Title	  	{ get; set; }
   	property	  	IList<author>	  	author	  	{ get; set; }
   	property	  	IList<Editor>	  	Editor	  	{ get; set; }
   	property	  	IList<string>	  	Publisher	  	{ get; set; }
   	property	  	IList<decimal>	  	Price	  	{ get; set; }
   	property	  	ushort	  	Year	  	{ get; set; }
   	property	  	bool?	  	Anson	  	{ get; set; }
   	property	  	string	  	Joe	  	{ get; set; }
}
class author : XTypedElement
{
   	constructor	  	author();
   	explicit cast	  	author(System.Xml.Linq.XElement xe);
   	property	  	string	  	Last	  	{ get; set; }
   	property	  	string	  	First	  	{ get; set; }
}
class Editor : XTypedElement
{
   	constructor	  	Editor();
   	explicit cast	  	Editor(System.Xml.Linq.XElement xe);
   	property	  	string	  	Last	  	{ get; set; }
   	property	  	string	  	First	  	{ get; set; }
   	property	  	Affiliation	  	Affiliation	  	{ get; set; }
}
class Affiliation : XTypedElement
{
   	constructor	  	Affiliation();
   	explicit cast	  	Affiliation(System.Xml.Linq.XElement xe);
   	property	  	string	  	TypedValue	  	{ get; set; }
   	property	  	string	  	Type	  	{ get; set; }
}

Fixed and default values

LinqToXsd only provides support for default values. Fixed values are not yet currently supported.

Wildcards

Schema sample (A schema with wildcards)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:complexType name="ProductType">
          <xs:sequence>
               <xs:element name="Number" type="xs:string"/>
               <xs:element name="Name" type="xs:string"/>
               <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other" processContents="lax"/>
          </xs:sequence>
          <xs:anyAttribute namespace="##other" processContents="skip"/>
     </xs:complexType>
</xs:schema>

Element wildcards are mapped to a designated API member.

Note: Attribute wildcards are ignored by the mapping in the current release of LINQ to XSD and hence require the untyped API.

OO API (Designated members for wildcards)

class ProductType : XTypedElement
{
   	constructor	  	ProductType();
   	explicit cast	  	ProductType(System.Xml.Linq.XElement xe);
   	property	  	string	  	Number	  	{ get; set; }
   	property	  	string	  	Name	  	{ get; set; }
   	property	  	IEnumerable<System.Xml.Linq.XElement>	  	Any	  	{ get;  }
}

The Any property is always of type XElement or a list type thereof. Occurrence constraints and recurrence of Any are subject to the same rules as for normal element particles. That is, if the Any is neither repeating nor recurring, then the generated property is of type XElement. Otherwise, the property is of the list type.

Clone this wiki locally