Search code examples
xmldelphidelphi-xeomnixml

Delphi XE & OmniXML: Using SelectNode()?


I've got the following XML snippet as part of a larger XML file that I'm processing using the latest OmniXML snapshot:

<OrderRequestHeader>
<!-- snipped XML bits here -->
<ShipTo>                                                        
    <Address addressID="">                                      
        <Name xml:lang="en">SOME COMPANY</Name>        
        <PostalAddress name="default">                          
            <DeliverTo>John Doe</DeliverTo>                  
            <Street>123 Any St</Street>                  
            <City>Nowhere</City>                              
            <State>AK</State>                                   
            <PostalCode>99999</PostalCode>                      
            <Country isoCountryCode="US">United States</Country>
        </PostalAddress>                                        
        <Email/>                                                
        <Phone>                                                 
            <TelephoneNumber>                                   
                <CountryCode isoCountryCode=""/>                
                <AreaOrCityCode/>                               
                <Number></Number>                               
            </TelephoneNumber>                                  
        </Phone>                                                
    </Address>                                                  
</ShipTo>
<!-- more XML stuff follows -->
</OrderRequestHeader>

I've currently got a variable pointing to the <ShipTo> node, and want to select the contents of the <Name> node. I'm using the following code, but Node2 is coming up Nil...

procedure ProcessXML;
var
    Node1, Node2: IXMLNode;

begin
    Node1 := FindNode(OrderHeader, 'ShipTo');
    // the above is working.  Node points to the <ShipTo> node
    Node2 := SelectNode(Node1, 'Name');
    // the above line doesn't work.  Node2 is Nil
end;

Why is Node2 Nil? According to the help in OmniXMLUtils.pas, SelectNode will select a single node possibly more than one level below. There's definitely a <Name> node. Even trying to find the node via XPathSelect(Node1, 'Name'); returns an empty list. Am I using OmniXML wrong somehow? Is it possible to get to the <Name> node without first selecting the <Address> node?


Solution

  • SelectNode works fine, if you put double-slash characters in front:

    var
      FXMLDocument: IXMLDocument;
    
    // Somewhere along the line
      FXMLDocument := CreateXMLDocument
      XMLLoadFromFile(FXMLDocument, 'WhateverFile.xml');
      // or XMLLoadFromAnsiString(FXMLDocument, SomeStringVar);
    
    
    var
      QryNode, Node: IXMLNode;
    begin
      QryNode := FXMLDocument.DocumentElement;
      Node := SelectNode(QryNode, 'ShipTo');
      if Assigned(Node) then
      begin
        QryNode := SelectNode(Node, '//Name');
        if Assigned(QryNode) then
          ShowMessage('ShipTo Name is ' + QryNode.FirstChild.Text)
        else
          ShowMessage('Name not found');
      end;
    end;
    

    If you prefer, XPath works fine as well:

    implementation
    
    var
      FXMLDocument: IXMLDocument;
    
    // Somewhere along the line
      FXMLDocument := CreateXMLDocument
      XMLLoadFromFile(FXMLDocument, 'WhateverFile.xml');
      // or XMLLoadFromAnsiString(FXMLDocument, SomeStringVar);
    
    function GetShipTo: string;
    var
      QryNode: IXMLNode;
      Node: IXMLNode;
      NodeList: IXMLNodeList;
    begin
      Result := '';
      QryNode := FXMLDocument.DocumentElement;  
    
      // The following also work:
      // '//Address/Name'
      // '//Name'
      NodeList := XPathSelect(QryNode, '//ShipTo/Address/Name');
      if NodeList.Length > 0 then
        QryNode := NodeList.Item[0]
      else
        QryNode := nil;
      if Assigned(QryNode) then
        Result := QryNode.FirstChild.Text; // Now has 'SOME COMPANY'
    end;