Introduction
This article shows a class to contain information about a node from an XmlDocument
combined with information from the corresponding XmlSchema
.
These classes are the objects to be implemented:
Implementation
How do we simultaneously read XML and XSD documents, and build a document which will contain nodes combining information from both documents.
When we need to read information from a XmlDocument
file, we use the class System.Xml.XmlDocument
. This class not only reads an isolated XML file but can simultaneously read a corresponding XSD schema file and apply the necessary validation during this process.
When we want to intercept this process, we need to implement a class derived from System.Xml.XmlValidatingReader
and override the Read
method. This method is invoked every time the base class starts or stops reading a node from the XML file. At this point, the base class already contains information about the currently read node, such as: Name, Value, and information which had been extracted from XSD file. Unfortunately, it does not contain crucial information for our objective; it does not have the property that returns the System.Xml.Schema.XmlSchemaElement
described by the currently read node.
How can we overcome this obstacle? See these project files: Puma.Xml.XmlValidatingReader.cs, Puma.Xml.XmlXsdNodeBuilder.cs.
For testing, just turn out project properties to \output type\Console application.
Full project static schema:
Using the code
Puma.Xml.XmlXsdDocument class
This class creates a tree representation of the XML. This tree contains not only the information from the XML but also associated information from the corresponding XSD schema. The tree representation of the XML and associated XSD document enables the navigation and editing of the XML document. Also, this class provides a method for the creation of an XML file from the corresponding XSD.
Restrictions:
This class may process only documents with restricted contents: For example, an element of complexType
may have only a sequence
child.
Constructors:
public XmlXsdDocument(string XmlFileName, string XsdFileName)
Loads the XML and the XSD documents from the specified files
public XmlXsdDocument(string XsdFileName)
Create an XML file that conforms to the XSD file.
public XmlXsdDocument(string XmlFileName, string XsdFileName, NodeExtensible NodeExtensible)
Loads the XML and the XSD documents from the specified files. When the load occurs, the overridden methods of the NodeExtensible
class are invoked.
public XmlXsdDocument(string XsdFileName, NodeExtensible NodeExtensible)
Creates an XML file that conforms to the XSD file. When created, the overridden methods of the NodeExtensible
class are invoked.
public XmlXsdDocument(string XsdFileName, ConstructParams ConstructParams, NodeExtensible NodeExtensible)
Create an XML file that conforms to the XSD file and the ConstructParams
parameters. When created, the overridden methods of the NodeExtensible
class are invoked.
Properties:
public Puma.Xml.XmlDocument XmlDocument
public Puma.Xml.XsdDocument XsdDocument
Methods:
public void Save(string FileName)
Saves the XML document to the specified file.
public void Save()
Saves the XML document to the file from where it was loaded.
public void SaveBin(string FileName)
Saves the XML document to the specified binary file.
Restrictions:
The maxOccurs
attribute of each element cannot be unbounded. Each element with a string
type must have the maxLength
restriction. The type of any element from the document should be recognized by the SaveBin
method.
Current version supports: String, Byte, SByte, Int16, Int32, Int64, UInt16, UInt32, UInt64
.
Element with an enumeration restriction should have a non-string base type.
Puma.Xml.ConstructParams class
Puma.Xml.ConstructParams.ConstructElementParams class
Properties:
public bool UseMinOccurs = true
When an element is created, the quantity in the document will be equal to the minOccurs
attribute. Otherwise, it will be equal to maxOccurs
(in this case, maxOccurs
should not have the unbounded
value).
Puma.Xml.ConstructParams.ConstructDefaultValueParams class
Properties:
public char StringFillChar = ' '
When an element of string
type is created that has a minLength
restriction, the value is initialized by a string with a length equal to minLength
and filled by the StringFillChar
character.
System.IO.BinaryReader ValueProvider
When an XML document is created from an XSD schema file, and this parameter is not null
, all values of the created XML will be initialized by reading data from this reader.
Restrictions:
See the Puma.Xml.XmlXsdDocument.SaveBeen()
restrictions.
Puma.Xml.NodeExtensible class
This class helps to extend the functionality of a document’s node or the document itself. When XmlXsdDocument
creates a node in the nodes tree, we can invoke one of methods of this class and initialize the node using the returned object TagEx
property. Further, we get easy access to this returned object through the TagEx
property of the appropriate node.
Methods:
virtual object OnXmlNodeCreated(Puma.Xml.XmlNode XmlNode)
Invoked when the document has finished creating XmlNode
.
virtual object OnXsdNodeCreated(Puma.Xml.XsdNode XsdNode)
Invoked when the document has finished creating XsdNode
.
virtual object OnXmlXsdNodeCreated(Puma.Xml.XmlXsdNode XmlXsdNode)
Invoked when the document has finished creating XmlXsdNode
.
virtual object OnXmlDocumentCreated(Puma.Xml.XmlDocument XmlDocument)
Invoked when the document has finished creating XmlDocument
.
virtual object OnXsdDocumentCreated(Puma.Xml.XsdDocument XsdDocument)
Invoked when the document has finished creating XsdDocument
.
virtual object OnXmlXsdDocumentCreated(Puma.Xml.XmlXsdDocument XmlXsdDocument)
Invoked when the document has finished creating itself.
Puma.Xml.XmlXsdNode class
The class contains information about a document node. Since what are part of the document tree have properties, which get access to one child and parent, combined information is extracted from the XML and XSD documents.
Properties:
Puma.Xml.XmlXsdDocument XmlXsdDocument
Document containing this node.
public Puma.Xml.XmlNode XmlNode
Describes the node from the XML document.
public Puma.Xml.XsdNode XsdNode
Describes the node from the XSD document.
object Parent
Retrieve parent (either Puma.Xml.XmlXsdDocument
or Puma.Xml.XmlXsdNode
type).
public string Name
Retrieve node name.
public bool IsComplexType
Define if the node may have child nodes.
public string Value
Retrieve node value.
public object TypedValue
Retrieve node typed value.
public XmlXsdNodeCollection Childs
Retrieve collection of child nodes.
Puma.Xml.XmlXsdNodeCollection class
Represents a collection of child nodes of a node. Each collection is subdivided into a number of fragments [see XmlXsdNodeCollectionFragment
class].
Methods:
public XmlXsdNodeCollectionFragment GetXmlXsdNodeFragment(string FragName)
Get the associated fragment of a collection.
Properties:
public XmlXsdNodeCollectionFragment this[string FragName] { get }
Get the associated fragment of a collection.
Puma.Xml.XmlXsdNodeCollectionFragment class
The class represents a collection of child nodes with the same name. This enables the ability to add nodes to remove nodes from this collection.
Note: A node may contain a collection of child nodes with the same name. This may result when this node is described in the XSD document with attribute maxOccurs
> 1, and is encountered in the XML document more than once; all these nodes will be located in one XmlXsdNodeCollectionFragment
container. Even in the case of maxOccurs = 1
, the single node will be located in a separate XmlXsdNodeCollectionFragment
container. Also, a node may have minOccurs = 0
, and not be encountered in the XML document. In this case, there will also be a XmlXsdNodeCollectionFragment
created.
Example: an XSD document:
<xs:element name="Root">
<xs:complexType>
<xs:sequence>
<xs:element name="Node1" type="xs:unsignedByte" maxOccurs="3"/>
<xs:element name="Node2" type="xs:unsignedByte" maxOccurs="1"/>
<xs:element name="Node3" type="xs:unsignedByte" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
XML document:
<Root>
<Node1>1</Node1>
<Node1>2</Node1>
<Node2>3</Node2>
</Root>
foreach(XmlXsdNodeCollectionFragment frag in rootXmlXsdNode)
{
System.Console.WriteLine("Frag name: " + frag.Name);
Foreach(XmlXsdNode child in frag)
{
System.Console.WriteLine("Child name: " + child.Name +
", val= " + child.Value);
}
}
Console:
Frag name: Node1
Child name: Node1, Val = 1
Child name: Node1, Val = 2
Frag name: Node2
Child name: Node2, Val = 3
Frag name: Node3
Properties:
public XsdNode TemplateXsdNode
Return node which contains information, related to the XSD document, about nodes from the current fragment.
public XmlXsdNode this[int Index]
Retrieves a node at the given index.
public XmlXsdNode LastNode
Retrieves the last node. If no node exists, then return null
.
public bool IsCanInsert
Define that a node may be added to the fragment.
public bool IsCanRemove
Define that a node may be removed from the fragment.
Methods:
public XmlXsdNode InsertXmlXsdNode(int Index)
This method creates a node based on information contained in the TemplateXsdNode
member and inserts that node in the document. The node is inserted after the fragment’s node with the same index. If Index = -1
, the node is placed at the end of the fragment.
The value is initialized in this way:
- If a default value exists for this node, it is applied.
- If the value has a string type and a
minLength
restriction, it is initialized with a string of length = minLength
and filled with the character ConstructDefaultValueParams.StringFillChar
(see the Puma.Xml.ConstructParams
class)
- If the value has the enumeration restriction, it is initialized with the first enumeration value encountered.
- If the value is a data type value and implements a
MinValue
static property, it is initialized by this property.
- If none of these cases is met, the value is initialized as
null
.
Notes:
Used when creating a complex node when number of sub node elements depend on the ConstructElementParams.UseMinOccurs
parameter (see Puma.Xml.ConstructParams
class). If this value is true
then this number is equal to minOccurs
; otherwise it is equal to maxOccurs
.
Exceptions:
Puma.Xml.XmlExceptions.ChildCollectionXmlException[ChildCollectionExceptionCode = Puma.Xml.XmlExceptions.ChildCollectionExceptionCode.NodeIndexInvalid]
Occurs when the index of a fragment is out of range (index exceeds the number of elements in the fragment).
Puma.Xml.XmlExceptions.ChildCollectionXmlException[ChildCollectionExceptionCode = Puma.Xml.XmlExceptions.ChildCollectionExceptionCode. MaxOccursLimitExcited]
OOccurs when no node can be added to the fragment (the number of nodes is more than maxOccurs
).
Example: XSD document:
<xs:element name="Root">
<xs:complexType>
<xs:sequence>
<xs:element name="Node1" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Node11"
type="xs:unsignedByte" default="100" />
<xs:element name="Node12"
type="xs:unsignedByte" minOccurs="3"/>
<xs:element name="Node13" >
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="Enum1" pge:ParentValuePart="true">
<xs:simpleType>
<xs:restriction base="xs:unsignedByte">
<xs:enumeration value="2"/>
<xs:enumeration value="3"/>
<xs:enumeration value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
XML document:
<Root>
</Root>
Puma.Xml.XmlXsd.XmlXsdNodeCollectionFragment frag =
rootXmlXsdNode["Node1"];
Puma.Xml.XmlXsd.XmlXsdNode insertedNode =
frag.InsertXmlXsdNode(-1);
Foreach(XmlXsdNodeCollectionFragment insFrag in insertedNode)
{
System.Console.WriteLine("Frag name: " + insFrag.Name);
Foreach(XmlXsdNode child in insFrag)
{
System.Console.WriteLine("Child name: " +
child.Name ", val= " + child.Value);
}
}
Console:
Frag name: Node11
Child name: Node1, Val = 100
Frag name: Node12
Child name: Node12, Val = 0
Child name: Node12, Val = 0
Child name: Node12, Val = 0
Frag name: Node13
Child name: Node13, Val = xxxxx
Frag name: Enum1
Child name: Enum1, Val = 2
public XmlXsdNode RemoveXmlXsdNode(int Index)
Remove a node with Index
from fragment and document.
Exceptions:
Puma.Xml.XmlExceptions.ChildCollectionXmlException[ChildCollectionExceptionCode = Puma.Xml.XmlExceptions.ChildCollectionExceptionCode.NodeIndexInvalid]
Occurs when the index of a fragment is out of range (Index
exceeds the number of elements in a fragment).
Puma.Xml.XmlNode class
The class facilitates dealing with the System.Xml.XmlElement
class.
Members:
public readonly Puma.Xml.XmlXsdNode container
Get the container of this class.
Properties:
public System.Xml.XmlElement XmlElement
Get the contained System.Xml.XmlElement
element.
Puma.Xml.XsdNode class
The class facilitates dealing with the System.Xml.Schema.XmlSchemaElement
class.
Members:
public readonly Puma.Xml.XsdSimpleType XsdSimpleType
See the Puma.Xml.XsdSimpleType
class
public readonly XmlXsdNode container
Get the container of this class.
Properties:
public System.Xml.Schema.XmlSchemaElement XmlSchemaElement
Get the contained System.Xml.XmlSchemaElement
element.
public bool IsComplexType
Return of true
indicates that the element has a complex type (contains child nodes).
public Puma.Xml.MinMaxOccurs MinMaxOccurs
Return the minOccurs
and maxOccurs
node attributes.
public string Annotation
Return node annotation [see xs:annotation
].
public bool IsReadOnly
Return indicates that node is read only [see msadata:ReadOnly
attribute].
public string Caption
Return node caption [see msdata:Caption
attribute].
Puma.Xml.XsdSimpleType class
The class facilitates dealing with information extracted from the XSD document, which describes the type of a node.
Members:
public readonly XsdNode Container
Get the container of this class.
public readonly XmlSimpleTypeRestriction Restriction
Get the restrictions of the node.
Properties:
public System.Xml.Schema.XmlSchemaSimpleType SimpleType
Get the simple type of the node.
public System.Xml.Schema.XmlSchemaDatatype DataType
Get the data type of the node.
public System.Type ValueType
Get the system type associated with the XSD document node type. See MSDN: Mapping XML Data Types to CLR Types.
XmlSimpleTypeRestriction class
The class facilitates dealing with information that is described by the restrictions of the type of the node.
Enum:
public enum XsdSimpleTypeRestriction
{
MinLength = 0x1,
MaxLength = 0x10,
MinValue = 0x100,
MaxValue = 0x1000,
Enumeration = 0x10000,
All = 0x11111
}
This enum defines the restriction type.
Properties:
public System.Xml.Schema.XmlSchemaSimpleTypeRestriction Restriction
Get node System.Xml.Schema.XmlSchemaSimpleTypeRestriction
.
public System.Xml.Schema.XmlSchemaObjectCollection Facets
Get node System.Xml.Schema.XmlSchemaSimpleTypeRestriction
.
public bool IsEnum
Return indicates if the node type has enumeration
restriction.
public bool IsMinValue
Return indicates if the node type has minValue
restriction.
public bool IsMaxValue
Return indicates if the node type has maxValue
restriction.
public bool IsMinLength
Return indicates if the node type has minLength
restriction.
public bool IsMaxLength
Return indicates if the node type has maxLength
restriction.
public int MinLengthRestriction
Return the node minLength
restriction. If the restriction is not found, return -1.
public int MaxLengthRestriction
Return the node maxLength
restriction. If the restriction is not found. return -1.
public EnumerationRestriction EnumerationRestriction
Return node enumeration restriction
Methods:
public object GetRestriction(XsdSimpleTypeRestriction TypeRestriction)
Get restriction by type. If restriction with this type is not found, return null
. The type of the returned object may be one of the following: Puma.Xml.EnumerationRestriction
, Puma.Xml.MinMaxLengthRestriction
, Puma.Xml.MinMaxValueRestriction
public object GetFirstRestrictionValue(XsdSimpleTypeRestriction TypeRestriction)
Get the value of the node restriction by type. If there is more than one restriction with the same type (enumeration for example), return the first.
public object[] GetRestrictions()
Get all type node restrictions. If none found, return null
. See the GetRestriction
method for information about the type of returned objects.
public object[] GetRestrictions(int TypeRestrictions)
Get an array of type nodes restrictions by type. If none found, return null
. See the GetRestriction
method for information about the type of the returned objects.
Acknowledgement
Thanks to Arnie Berg for the contribution in this article writing.