1

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.

Random Human
  • 946
  • 1
  • 14
  • 31
Jeremy Holovacs
  • 22,480
  • 33
  • 117
  • 254
  • Do you *have* to use XmlDocument? LINQ to XML makes all of this really easy... and presumably you *are* using .NET 3.5 if you're using LINQ... – Jon Skeet Apr 17 '12 at 18:18
  • Well... *have to* is a strong term. I would prefer to; I'm sure there's a way to do this, and I'd like to know how it would be done. Everywhere else in my code I am using `XmlNode`s and `XmlDocument`s. – Jeremy Holovacs Apr 17 '12 at 18:22
  • The rest of your code would likely become a lot cleaner if you changed it *all* to LINQ to XML. You *can* use an `XmlNamespaceManager` with XPath, but it's horrible compared with the cleanliness of LINQ to XML. I'd strongly suggest making a start on the transition ASAP :) – Jon Skeet Apr 17 '12 at 18:23
  • 1
    possible duplicate of [Using Xpath With Default Namespace in C#](http://stackoverflow.com/questions/585812/using-xpath-with-default-namespace-in-c-sharp). Note that 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. – Alexei Levenkov Apr 17 '12 at 18:24
  • It does seem like that is the short answer, @AlexeiLevenkov. Jon, I will have to look into Linq To XML. – Jeremy Holovacs Apr 17 '12 at 18:44

1 Answers1

1

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']".

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179