Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi Xpath XML query

Tags:

xml

xpath

delphi

I'm trying to locate the value for <Link role="self"> in the following XML file using a XPath query:

<?xml version="1.0" encoding="utf-8"?>
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns="http://schemas.microsoft.com/search/local/ws/rest/v1">
    <Copyright>Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.</Copyright>
    <BrandLogoUri>http://spatial.virtualearth.net/Branding/logo_powered_by.png</BrandLogoUri>
    <StatusCode>201</StatusCode>
    <StatusDescription>Created</StatusDescription>
    <AuthenticationResultCode>ValidCredentials</AuthenticationResultCode>
    <TraceId>ID|02.00.82.2300|</TraceId>
    <ResourceSets>
        <ResourceSet>
            <EstimatedTotal>1</EstimatedTotal>
            <Resources>
                <DataflowJob>
                    <Id>ID</Id>
                    <Link role="self">https://spatial.virtualearth.net/REST/v1/dataflows/Geocode/ID</Link>
                    <Status>Pending</Status>
                    <CreatedDate>2011-03-30T08:03:09.3551157-07:00</CreatedDate>
                    <CompletedDate xsi:nil="true" />
                    <TotalEntityCount>0</TotalEntityCount>
                    <ProcessedEntityCount>0</ProcessedEntityCount>
                    <FailedEntityCount>0</FailedEntityCount>
                </DataflowJob>
            </Resources>
        </ResourceSet>
    </ResourceSets>
</Response>

I was shown an XPath query in a previous post, but I keep getting a unassigned iNode in the following code.

function TForm1.QueryXMLData(XMLFilename, XMLQuery: string): string;
var
  iNode : IDOMNode;
  Sel: IDOMNodeSelect;
begin
  try
    XMLDoc.Active := False;
    XMLDoc.FileName := XMLFilename;
    XMLDoc.Active := True;

    Sel := XMLDoc.DOMDocument as IDomNodeSelect;

    Result := '';
    iNode := Sel.selectNode('Link[@role = "self"]');
    if Assigned(iNode) then
      if (not VarisNull(iNode.NodeValue)) then
        Result := iNode.NodeValue;

    XMLDoc.Active := False;

  Except on E: Exception do
    begin
      MessageDlg(E.ClassName + ': ' + E.Message, mtError, [mbOK], 0);
      LogEvent(E.Message);
    end;
  end;
end;

What can I try to resolve this?

like image 750
Pieter van Wyk Avatar asked Apr 01 '11 10:04

Pieter van Wyk


2 Answers

If you want to locate Link anywhere in the document, you’ll have to prefix it with //; like this:

iNode := Sel.selectNode('//Link[@role = "self"][3]');

This will start searching at the root of the document, and traverse the entire document, looking for a node called Link matching the specified criteria.

See here for more operators: http://msdn.microsoft.com/en-us/library/ms256122.aspx

Note that, as Runner suggests, you can also query the full XML path. This will be faster than the // operator, since it won’t have to blindly search every node.


Edit: Why are you requesting the third matching node (the [3] bit)? AFAICS, there’s only one; if your real document does have more, and you’re certain you want the third, then it’s OK. Otherwise, remove the [3] query.


Also, depending on the XML implementation you’re using (vendor and version), you also might have to specify the XML namespace. In MSXML 4 thru 6 (IIRC), you’d have to use

XMLDoc.setProperty('SelectionNamespaces', 'xmlns:ns="http://schemas.microsoft.com/search/local/ws/rest/v1"');

This would mean using that prefix in your queries as well:

iNode := Sel.selectNode('//ns:Link[@role = "self"][3]');
like image 62
Martijn Avatar answered Sep 25 '22 14:09

Martijn


You should write it like this:

iNode := Sel.selectNode('//Link[@role = "self"]');

which will get you the first Link node in the document with attribute role="self" (even if there is more than one).

Or you can go the absolute path:

iNode := Sel.selectNode('/Response/ResourceSets/ResourceSet/Resources/DataflowJob/Link[@role = "self"]');

or even something in between

iNode := Sel.selectNode('//Resources/DataflowJob/Link[@role = "self"]');
like image 44
Runner Avatar answered Sep 26 '22 14:09

Runner