Mapping documentation - mamift/LinqToXsdCore GitHub Wiki
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.
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; }
.
class Foo : XTypedElement
{
constructor Foo();
explicit cast Foo(System.Xml.Linq.XElement xe);
property string Bar { get; set; }
}
- Global types and elements
- Content models
- Substitution forms
- Trivially mapped XSD constructs
- Appendix: Mapping of built-in simple types
- Appendix: Name mapping
- Appendix: Namespace mapping
- Appendix: More samples
- Complex-type definitions
- Roots with anonymous, complex types
- Roots with type references to complex types
- Simple-type definitions
- Roots with anonymous, simple types
- Roots with type references to simple types
- 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 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.
<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>
class Address : XTypedElement
{
constructor Address();
explicit cast Address(System.Xml.Linq.XElement xe);
}
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.
<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>
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.
This pattern makes combined use of a complex-type definition and a global element declaration.
<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.
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.
<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.
Root element declarations (with anonymous types or not) are mapped to classes.
<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>
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.
<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>
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.
- Flat sequences
- Type and element references
- Optional elements
- Repeating elements
- Attributes
- Flat choices
- Recurrent element names
- Nested content models
- Anonymous types
- Fixed and default values
- Wildcards
- 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.
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.)
<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.
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; }
}
- 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.
<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.
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.
<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.
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; }
}
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.
<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>
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.
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 tot
. - Hence, the observable CLR type does not resemble the precise occurrence constraints.
<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>
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.
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.)
<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>
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; }
}
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>
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.
<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.
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.
<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.
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.
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.
<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>
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; }
}
<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.
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; }
}
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.
<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.
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.
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; }
}
LinqToXsd only provides support for default values. Fixed values are not yet currently supported.
<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.
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.
- Complex-type extension
- Extension of simple content
- Simple-type restriction
- Complex-type restriction
- Susbstitution groups
- Control of substitutability
For simplicity, the following assumptions are made:
- A complex base type is extended by complex content.
- Substitutability is unrestricted, i.e., block and final are not used.
- The derived content model is a sequence on element particles.
- The derived type is non-anonymous.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- A base type of products -->
<xs:complexType name="Product">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="Name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<!-- shirt extends product -->
<xs:complexType name="Shirt">
<xs:complexContent>
<xs:extension base="Product">
<xs:sequence>
<xs:element name="Size" type="xs:string"/>
<xs:element name="Color" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- hat extends product -->
<xs:complexType name="Hat">
<xs:complexContent>
<xs:extension base="Product">
<xs:sequence>
<xs:element name="Size" type="xs:int"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
Such type derivation by extension is mapped as follows:
- Both base type and derived type are mapped to classes.
- The derivation relationship is mapped to OO class inheritance.
- That is, the "derived" class inherits from the "base" class.
- Roots in the derivation hierarchy inherit from "XTypedElement" as usual.
- The properties for the added particles appear in the derived class.
class Product : XTypedElement
{
constructor Product();
explicit cast Product(System.Xml.Linq.XElement xe);
property decimal Number { get; set; }
property string Name { get; set; }
}
class Shirt : Product
{
constructor Shirt();
explicit cast Shirt(System.Xml.Linq.XElement xe);
property string Size { get; set; }
property string Color { get; set; }
}
class Hat : Product
{
constructor Hat();
explicit cast Hat(System.Xml.Linq.XElement xe);
property int Size { get; set; }
}
Note: This topic is not documented in the current release of LINQ to XSD.
Note: This topic is not documented in the current release of LINQ to XSD.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- A base type of products -->
<xs:complexType name="Product">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Size" type="xs:int" minOccurs="0"/>
<xs:element name="Color" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<!-- A restriction of product -->
<xs:complexType name="RigidProduct">
<xs:complexContent>
<xs:restriction base="Product">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Size" type="xs:int"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</xs:schema>
The derived type is mapped to a subclass of the class for the base type.
Note: In the current release of LINQ to XSD, the restrictions are not checked in any way.
class Product : XTypedElement
{
constructor Product();
explicit cast Product(System.Xml.Linq.XElement xe);
property decimal Number { get; set; }
property string Name { get; set; }
property int? Size { get; set; }
property string Color { get; set; }
}
class RigidProduct : Product
{
constructor RigidProduct();
explicit cast RigidProduct(System.Xml.Linq.XElement xe);
}
Here is an example for using substitution groups. (We continue the example on type derivation by extension for complex types.)
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Product" type="ProductType"/>
<xs:element name="Shirt" type="ShirtType"/>
<xs:element name="Hat" type="HatType"/>
<xs:complexType name="ProductType">
<xs:sequence>
<!-- product details omitted -->
</xs:sequence>
</xs:complexType>
<xs:complexType name="ShirtType">
<xs:complexContent>
<xs:extension base="ProductType">
<xs:sequence>
<!-- shirt details omitted -->
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="HatType">
<xs:complexContent>
<xs:extension base="ProductType">
<xs:sequence>
<!-- hat details omitted -->
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
Substitution groups are mapped in accordance to the combined use of element declarations and complex-type definitions. In addition, the following mapping rules apply:
- The type derivation hierarchy is mapped as in the case of plain type derivation.
- The global element declarations are mapped as usual except for the superclass constraint.
- Classes for substitution group members inherit from the class for the head.
class ProductType : XTypedElement
{
constructor ProductType();
explicit cast ProductType(System.Xml.Linq.XElement xe);
}
class ShirtType : ProductType
{
constructor ShirtType();
explicit cast ShirtType(System.Xml.Linq.XElement xe);
}
class HatType : ProductType
{
constructor HatType();
explicit cast HatType(System.Xml.Linq.XElement xe);
}
class Product : XTypedElement
{
constructor Product();
constructor Product(ProductType content);
explicit cast Product(System.Xml.Linq.XElement xe);
property ProductType Content { get; }
}
class Shirt : XTypedElement
{
constructor Shirt();
constructor Shirt(ShirtType content);
explicit cast Shirt(System.Xml.Linq.XElement xe);
property ShirtType Content { get; }
}
class Hat : XTypedElement
{
constructor Hat();
constructor Hat(HatType content);
explicit cast Hat(System.Xml.Linq.XElement xe);
property HatType Content { get; }
}
Note: This topic is not documented in the current release of LINQ to XSD.
- Group definitions
- Global attribute declarations
- Global attribute-group definitions
- Redefinitions
- Identity constraints
- Mixed content
These definitions are not mapped. References to these definitions are mapped by inlining them in the place of the reference. Note: This approach may be refined in a future version of LINQ to XSD so that groups are mapped for better code factorization.
These declarations are not mapped. References to these declarations are mapped by inlining them in the place of the reference.
These definitions are not mapped. References to these definitions are mapped by inlining them in the place of the reference.
The System.Xml.Schema
semantics of redefinitions applies.
Essentially, this implies that redefinitions override the original definitions.
Identity constraints are not taken into account, in any way. Normal validation is supposed to check these constraints.
There are no designated API members for mixed content.
Interim text is discoverable and definable through the XElement
interface.
Tool tips may document the mixed-content status of a class.
By default, each and every XSD built-in simple type is mapped to a CLR type, mostly to a CLR value type or to string. This default mapping is exercised below by means of a content model with an element particle for each and every built-in type.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- An illustrative type that exercises all built-in simple types. -->
<!-- Indeed, all these types are listed in alphabetic order. -->
<xs:complexType name="simpleType">
<xs:sequence>
<xs:element name="xsAnySimpleType" type="xs:anySimpleType"/>
<xs:element name="xsAnyURI" type="xs:anyURI"/>
<xs:element name="xsBase64Binary" type="xs:base64Binary"/>
<xs:element name="xsBoolean" type="xs:boolean"/>
<xs:element name="xsByte" type="xs:byte"/>
<xs:element name="xsDate" type="xs:date"/>
<xs:element name="xsDateTime" type="xs:dateTime"/>
<xs:element name="xsENTITIES" type="xs:ENTITIES"/>
<xs:element name="xsENTITY" type="xs:ENTITY"/>
<xs:element name="xsFloat" type="xs:float"/>
<xs:element name="xsDecimal" type="xs:decimal"/>
<xs:element name="xsDouble" type="xs:double"/>
<xs:element name="xsDuration" type="xs:duration"/>
<xs:element name="xsGDay" type="xs:gDay"/>
<xs:element name="xsGMonth" type="xs:gMonth"/>
<xs:element name="xsGMonthDay" type="xs:gMonthDay"/>
<xs:element name="xsGYear" type="xs:gYear"/>
<xs:element name="xsGYearMonth" type="xs:gYearMonth"/>
<xs:element name="xsHexBinary" type="xs:hexBinary"/>
<xs:element name="xsID" type="xs:ID"/>
<xs:element name="xsIDREF" type="xs:IDREF"/>
<xs:element name="xsIDREFS" type="xs:IDREFS"/>
<xs:element name="xsInt" type="xs:int"/>
<xs:element name="xsInteger" type="xs:integer"/>
<xs:element name="xsLanguage" type="xs:language"/>
<xs:element name="xsLong" type="xs:long"/>
<xs:element name="xsName" type="xs:Name"/>
<xs:element name="xsNCName" type="xs:NCName"/>
<xs:element name="xsNegativeInteger" type="xs:negativeInteger"/>
<xs:element name="xsNMTOKEN" type="xs:NMTOKEN"/>
<xs:element name="xsNMTOKENS" type="xs:NMTOKENS"/>
<xs:element name="xsNonNegativeInteger" type="xs:nonNegativeInteger"/>
<xs:element name="xsNonPositiveInteger" type="xs:nonPositiveInteger"/>
<xs:element name="xsNormalizedString" type="xs:normalizedString"/>
<!-- xs:NOTATION was omitted since it is only used in derivations. -->
<xs:element name="xsPositiveInteger" type="xs:positiveInteger"/>
<xs:element name="xsQName" type="xs:QName"/>
<xs:element name="xsUnsignedByte" type="xs:unsignedByte"/>
<xs:element name="xsUnsignedInt" type="xs:unsignedInt"/>
<xs:element name="xsUnsignedLong" type="xs:unsignedLong"/>
<xs:element name="xsUnsignedShort" type="xs:unsignedShort"/>
<xs:element name="xsShort" type="xs:short"/>
<xs:element name="xsString" type="xs:string"/>
<xs:element name="xsTime" type="xs:time"/>
<xs:element name="xsToken" type="xs:token"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
class simpleType : XTypedElement
{
constructor simpleType();
explicit cast simpleType(System.Xml.Linq.XElement xe);
property string xsAnySimpleType { get; set; }
property System.Uri xsAnyURI { get; set; }
property System.Byte[] xsBase64Binary { get; set; }
property bool xsBoolean { get; set; }
property sbyte xsByte { get; set; }
property System.DateTime xsDate { get; set; }
property System.DateTime xsDateTime { get; set; }
property IList<string> xsENTITIES { get; set; }
property string xsENTITY { get; set; }
property float xsFloat { get; set; }
property decimal xsDecimal { get; set; }
property double xsDouble { get; set; }
property System.TimeSpan xsDuration { get; set; }
property System.DateTime xsGDay { get; set; }
property System.DateTime xsGMonth { get; set; }
property System.DateTime xsGMonthDay { get; set; }
property System.DateTime xsGYear { get; set; }
property System.DateTime xsGYearMonth { get; set; }
property System.Byte[] xsHexBinary { get; set; }
property string xsID { get; set; }
property string xsIDREF { get; set; }
property IList<string> xsIDREFS { get; set; }
property int xsInt { get; set; }
property decimal xsInteger { get; set; }
property string xsLanguage { get; set; }
property long xsLong { get; set; }
property string xsName { get; set; }
property string xsNCName { get; set; }
property decimal xsNegativeInteger { get; set; }
property string xsNMTOKEN { get; set; }
property IList<string> xsNMTOKENS { get; set; }
property decimal xsNonNegativeInteger { get; set; }
property decimal xsNonPositiveInteger { get; set; }
property string xsNormalizedString { get; set; }
property decimal xsPositiveInteger { get; set; }
property System.Xml.XmlQualifiedName xsQName { get; set; }
property byte xsUnsignedByte { get; set; }
property uint xsUnsignedInt { get; set; }
property ulong xsUnsignedLong { get; set; }
property ushort xsUnsignedShort { get; set; }
property short xsShort { get; set; }
property string xsString { get; set; }
property System.DateTime xsTime { get; set; }
property string xsToken { get; set; }
}
Note: This topic is not documented in the current release of LINQ to XSD.
Note: This topic is not documented in the current release of LINQ to XSD.
Subsections
The following schema is used in the LINQ to XSD overview.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Batch">
<xs:complexType>
<xs:sequence>
<xs:element ref="PurchaseOrder" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="PurchaseOrder">
<xs:complexType>
<xs:sequence>
<xs:element name="CustId" type="xs:string"/>
<xs:element ref="Item" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Item">
<xs:complexType>
<xs:sequence>
<xs:element name="ProdId" type="xs:string"/>
<xs:element name="Price" type="xs:double"/>
<xs:element name="Quantity" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
class Batch : XTypedElement
{
constructor Batch();
explicit cast Batch(System.Xml.Linq.XElement xe);
property IList<PurchaseOrder> PurchaseOrder { get; set; }
}
class PurchaseOrder : XTypedElement
{
constructor PurchaseOrder();
explicit cast PurchaseOrder(System.Xml.Linq.XElement xe);
property string CustId { get; set; }
property IList<Item> Item { get; set; }
}
class Item : XTypedElement
{
constructor Item();
explicit cast Item(System.Xml.Linq.XElement xe);
property string ProdId { get; set; }
property double Price { get; set; }
property int Quantity { get; set; }
}
The following schema is used in the XML 2006 paper.
<xs:schema xmlns:xmlns="http://www.w3.org/2001/XMLSchema">
<xs:element name="Invoice">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="string"/>
<xs:element name="Street" type="string"/>
<xs:element name="City" type="string"/>
<xs:element name="Zip" type="string"/>
<xs:element name="State" type="string"/>
<xs:element name="Position" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="ProdId" type="string"/>
<xs:element name="Price" type="double"/>
<xs:element name="Quantity" type="int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Total" type="double"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
class Invoice : XTypedElement
{
constructor Invoice();
explicit cast Invoice(System.Xml.Linq.XElement xe);
property string Name { get; set; }
property string Street { get; set; }
property string City { get; set; }
property string Zip { get; set; }
property string State { get; set; }
property IList<Position> Position { get; set; }
property double Total { get; set; }
}
class Position : XTypedElement
{
constructor Position();
explicit cast Position(System.Xml.Linq.XElement xe);
property string ProdId { get; set; }
property double Price { get; set; }
property int Quantity { get; set; }
}
The following schema is used in the LINQ to XSD demo.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Company">
<xs:complexType>
<xs:sequence>
<xs:element ref="Department" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Department">
<xs:complexType>
<xs:sequence>
<xs:element name="Label" type="xs:string"/>
<xs:element name="Manager" type="EmployeeType" minOccurs="0"/>
<xs:element ref="Member" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="Deptno" type="xs:int"/>
</xs:complexType>
</xs:element>
<xs:element name="Member">
<xs:complexType>
<xs:choice>
<xs:element name="Employee" type="EmployeeType"/>
<xs:element ref="Department"/>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Salary" type="xs:double"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
class Company : XTypedElement
{
constructor Company();
explicit cast Company(System.Xml.Linq.XElement xe);
property IList<Department> Department { get; set; }
}
class Department : XTypedElement
{
constructor Department();
explicit cast Department(System.Xml.Linq.XElement xe);
property string Label { get; set; }
property EmployeeType Manager { get; set; }
property IList<Member> Member { get; set; }
property int? Deptno { get; set; }
}
class Member : XTypedElement
{
constructor Member();
constructor Member(EmployeeType Employee);
constructor Member(Department Department);
explicit cast Member(System.Xml.Linq.XElement xe);
property EmployeeType Employee { get; set; }
property Department Department { get; set; }
}
class EmployeeType : XTypedElement
{
constructor EmployeeType();
explicit cast EmployeeType(System.Xml.Linq.XElement xe);
property string Name { get; set; }
property double Salary { get; set; }
}
The following schema is used in the LINQ to XSD demo.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Program" type="Block"/>
<xs:complexType name="Expr" abstract="true"/>
<xs:complexType name="Constant">
<xs:complexContent>
<xs:extension base="Expr">
<xs:choice>
<xs:element name="Integer" type="xs:int"/>
<xs:element name="Boolean" type="xs:boolean"/>
</xs:choice>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Variable">
<xs:complexContent>
<xs:extension base="Expr">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Block">
<xs:complexContent>
<xs:extension base="Expr">
<xs:sequence>
<xs:element name="Decl" type="Decl" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Expr" type="Expr" maxOccurs="unbounded"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Decl">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
<xs:element name="Type" type="Type"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Type" abstract="true"/>
<xs:complexType name="Integer">
<xs:complexContent>
<xs:extension base="Type"/>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Boolean">
<xs:complexContent>
<xs:extension base="Type"/>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Assign">
<xs:complexContent>
<xs:extension base="Expr">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
<xs:element name="Rhs" type="Expr"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
class Expr : XTypedElement
{
constructor Expr();
explicit cast Expr(System.Xml.Linq.XElement xe);
}
class Constant : Expr
{
constructor Constant();
constructor Constant(int? Integer);
constructor Constant(bool? Boolean);
explicit cast Constant(System.Xml.Linq.XElement xe);
property int? Integer { get; set; }
property bool? Boolean { get; set; }
}
class Variable : Expr
{
constructor Variable();
explicit cast Variable(System.Xml.Linq.XElement xe);
property string Id { get; set; }
}
class Block : Expr
{
constructor Block();
explicit cast Block(System.Xml.Linq.XElement xe);
property IList<Decl> Decl { get; set; }
property IList<Expr> Expr { get; set; }
}
class Decl : XTypedElement
{
constructor Decl();
explicit cast Decl(System.Xml.Linq.XElement xe);
property string Id { get; set; }
property Type Type { get; set; }
}
class Type : XTypedElement
{
constructor Type();
explicit cast Type(System.Xml.Linq.XElement xe);
}
class Integer : Type
{
constructor Integer();
explicit cast Integer(System.Xml.Linq.XElement xe);
}
class Boolean : Type
{
constructor Boolean();
explicit cast Boolean(System.Xml.Linq.XElement xe);
}
class Assign : Expr
{
constructor Assign();
explicit cast Assign(System.Xml.Linq.XElement xe);
property string Id { get; set; }
property Expr Rhs { get; set; }
}
class Program : XTypedElement
{
constructor Program();
constructor Program(Block content);
explicit cast Program(System.Xml.Linq.XElement xe);
property Block Content { get; }
property IList<Decl> Decl { get; set; }
property IList<Expr> Expr { get; set; }
}