I wrote an XSD for an XML configuration file like so:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:t="http://www.foo.com/schemas/datatransfer"
targetNamespace="http://www.foo.com/schemas/datatransfer"
attributeFormDefault="unqualified" elementFormDefault="qualified">
<xs:element name="transferGroups">
<xs:complexType>
<xs:sequence>
<xs:element name="transferGroup" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="connectionString" type="xs:string" />
<xs:choice minOccurs="1" maxOccurs="1">
<xs:element name="script" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="path" type="xs:string" use="required" />
<xs:attribute name="fileName" type="xs:string" use="required" />
<xs:attribute name="useCompression" type="xs:boolean" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="table" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="fileName" type="xs:string" use="required" />
<xs:attribute name="useCompression" type="xs:boolean" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="tables" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="table" maxOccurs="unbounded" minOccurs="1">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="fileName" type="xs:string" use="required" />
<xs:attribute name="useCompression" type="xs:boolean" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
<xs:element name="format">
<xs:complexType>
<xs:sequence>
<xs:element name="addHeaderRow">
<xs:complexType>
<xs:attribute name="value" type="xs:boolean" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="columnsDelimitedBy">
<xs:complexType>
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="rowsDelimitedBy">
<xs:complexType>
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="transferSite">
<xs:complexType>
<xs:sequence>
<xs:element name="protocol">
<xs:complexType>
<xs:attribute name="value" type="xs:string" use="required" />
<xs:attribute name="address" type="xs:string" use="required" />
<xs:attribute name="port" type="xs:unsignedByte" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="credentials">
<xs:complexType>
<xs:attribute name="userName" type="xs:string" use="required" />
<xs:attribute name="password" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="destinationFolder">
<xs:complexType>
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="useCompression" type="xs:boolean" use="required" />
<xs:attribute name="fileName" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
I generated (then modified) the schema from an XML file I designed in Visual Studio like so:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<transferGroups>
<transferGroup useCompression="false" >
<connectionString>DSN=MyDSN;uid=foo;password=bar;</connectionString>
<tables>
<table name="dbo.Foo1" fileName="E:\Files\Foo1_{0:yyyyMMdd}.gz" useCompression="true" />
<table name="pub.Foo2" fileName="E:\Files\Foo2_{0:yyyyMMdd}.gz" useCompression="true" />
</tables>
<format>
<addHeaderRow value="true"/>
<columnsDelimitedBy value="|" />
<rowsDelimitedBy value="\r\n" />
</format>
<transferSite>
<protocol value="FTP" address="localhost" port="21" />
<credentials userName="anonymous" password="test@myserver.com" />
<destinationFolder value="/" />
</transferSite>
</transferGroup>
</transferGroups>
In practice, I made a configuration file like so:
<transferGroups xmlns="http://www.foo.com/schemas/datatransfer">
<transferGroup useCompression="false" >
<connectionString>some connection string</connectionString>
<tables>
<table name="tableName1" fileName="tableName1_{0:yyyyMMdd}.gz" useCompression="true" />
<table name="tableName2" fileName="tableName2_{0:yyyyMMdd}.gz" useCompression="true" />
</tables>
<format>
<addHeaderRow value="true"/>
<columnsDelimitedBy value="|" />
<rowsDelimitedBy value="\r\n" />
</format>
<transferSite>
<protocol value="FTP" address="ftp.someplace.com" port="21" />
<credentials userName="foo" password="bar" />
<destinationFolder value="subFolder1" />
</transferSite>
</transferGroup>
</transferGroups>
I did not realize, however, that specifying a namespace/ schema changed the way to parse the file in an XmlDocument. For example, this segment of code would capture an XmlNodeList of one XmlNode if I didn’t specify the schema, but once the schema is specified, no nodes match:
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myXml);
var transferGroupNodes = xmlDoc.SelectNodes("//transferGroups/transferGroup")
.OfType<XmlNode>();
How would I collect the transferGroup node for parsing in this situation? I cannot find a decent example online to show me how to do this.
Schema has nothing to do with your problem – you have nodes with namespaces, so need to change XPath’s accordingly. As Jon Skeet points out LINQ to XML will make it easier to use, but still you need to select nodes with correct namespaces.
The question of using namespaces in XPath is covered multiple times – start with the FAQ answer – Using Xpath With Default Namespace in C#.
Cheating way of ignoring namespaces with XPath:
"//*[ local-name() = 'justName']".