delphi read xml element
Asked Answered
G

4

3

I'm new to XML and we need to do GeoCoding with the new Bing Spatial Data API. I've managed to get a result back from them in xml format. How would I read specific elements in the response, ie. the Link, Status and ErrorMessages?

<?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'm using Delphi XE.

Regards, Pieter

Gerhardine answered 30/3, 2011 at 7:8 Comment(1)
Please add a tag about Bing Spatial Data API.Refractory
P
7

How about using some simple XPATH to get the requested values?

//Link[1]/node() - selects the first "Link" node from the whole document, and then selects the first child node of any kind. It just happens that the first child node is the unnamed node containing the actual https link.

Assuming the XML document is loaded into Doc: TXMLDocument, you can extract the Link with this code:

(Doc.DOMDocument as IDomNodeSelect).selectNode('//Link[1]/node()').nodeValue

You can find some documentation about XPath reading those XPath Examples on MSDN. You might find better documentation at w3schools. And to top it all up, here's a simple (but complete) console application that uses XPath to extract and display the 3 requested values:

program Project14;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Xmldoc,
  xmldom,
  ActiveX;

var X: TXMLDocument;
    Node: IDOMNode;
    Sel: IDomNodeSelect;

begin
  try
    CoInitialize(nil);

    X := TXMLDocument.Create(nil);
    try

      // Load XML from a string constant so I can include the exact XML sample from this
      // question into the code. Note the "SomeNode" node, it's required to make that XML
      // valid.

      X.LoadFromXML(
        '<SomeNode>'+
        '  <Link role="self">' +
        '    https://spatial.virtualearth.net/REST/v1/dataflows/Geocode/jobid' +
        '  </Link>' +
        '  <Status>Aborted</Status>' +
        '  <ErrorMessage>The data uploaded in this request was not valid.</ErrorMessage>' +
        '</SomeNode>'
      );

      // Shortcut: Keep a reference to the IDomNodeSelect interface

      Sel := X.DOMDocument as IDomNodeSelect;

      // Extract and WriteLn() the values. Painfully simple!

      WriteLn(Sel.selectNode('//Link[1]/node()').nodeValue);
      WriteLn(Sel.selectNode('//Status[1]/node()').nodeValue);
      WriteLn(Sel.selectNode('//ErrorMessage[1]/node()').nodeValue);

      ReadLn;
    finally X.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Perineuritis answered 30/3, 2011 at 9:2 Comment(2)
Now I don't have to worry to recursively search for elements in a xml file. I can use a query to get what I want! Thanks.Gerhardine
With XPath you need to be extremely careful which Microsoft DOM is installed. Old versions only partially support the XPath languages, and use a different offset for the first element in an array. And when you use the generic GUID to get the underlying DOM objects (which most code does), you don't know which DOM version you get. I have been bitten by this at a few clients where they promised to get a fully up-to-date testing system, which was only a plain vanilla Windows 2003 box without updates.Astred
R
3

If the XML-structure is fairly stable, you could use the XML binding tool to generate ordinary Delphi classes to access the xml-document.

Take a look at this page.

Rhachis answered 30/3, 2011 at 7:59 Comment(0)
A
3

Since there is an XML Schema for these Bing Spatial Data Services, the easiest way to go is to import that schema using the Delphi XML Data Binding Wizard, then use the generated Delphi classes and interfaces to get your data from the XML, or put data in the XML.

This is similar to what Jørn E. Angeltveit suggested, but his suggestion uses the plain XML to generate the classes from.
That is OK if you don't have a schema, but when you have a schema, it is always better to import the schema.

Thare are many examples on using the Delphi XML Data Binding Wizard, so start there first.

If you need help on the binding: please ask a new specific question here.

Astred answered 30/3, 2011 at 9:3 Comment(2)
For the OP, it's worth giving the binding wizard a shot, if it works it's very nice (should work since the XML looks pretty simple). Unfortunately the only time I needed it didn't work, I'd get Access Violations! (ie: bugs in the wizard itself). I ended up rolling my own wizard... +1.Perineuritis
@Cosmin: which version of Delphi did you use it in? What XSD was it? You can't map all XSD/XML to all languages, but most wizards get quite far. Namespaces and include/import usually make it a lot more difficult, so many importers barf on that.Astred
C
2

Now you should parse XML file. In the most simple case (you know XML tags) it could look like this:

var
  XMLDoc: IXMLDocument;
  Node: IXMLNode;
  I: Integer;
  role, link: string;

begin
  XMLDoc:= TXMLDocument.Create(nil);
  XMLDoc.LoadFromFile(AFileName);

  for I:= 0 to XMLDoc.DocumentElement.ChildNodes.Count - 1 do begin
    Node:= XMLDoc.DocumentElement.ChildNodes[I];
    if Node.NodeType = ntElement then begin
      if Node.NodeName = 'Link' then begin
        if Node.HasAttribute('role') then
          role:= Node.Attributes['role'];
        if not VarIsNull(Node.NodeValue) then
          link:= Node.NodeValue;
[..]
      end;
    end;
  end;
end;
Countersign answered 30/3, 2011 at 7:29 Comment(1)
This solution works if the Node is at the top-most level. The node I need is 3 or 4 levels down. I did not realize that at the outset this morning, so I've now updated my question.Gerhardine

© 2022 - 2024 — McMap. All rights reserved.