Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Delphi Cookbook

You're reading from   Delphi Cookbook Over 60 hands-on recipes to help you master the power of Delphi for cross-platform and mobile development on multiple platforms

Arrow left icon
Product type Paperback
Published in Jun 2016
Publisher
ISBN-13 9781785287428
Length 470 pages
Edition 2nd Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Daniele Teti Daniele Teti
Author Profile Icon Daniele Teti
Daniele Teti
Arrow right icon
View More author details
Toc

Table of Contents (10) Chapters Close

Preface 1. Delphi Basics FREE CHAPTER 2. Becoming a Delphi Language Ninja 3. Knowing Your Friends – the Delphi RTL 4. Going Cross-Platform with FireMonkey 5. The Thousand Faces of Multithreading 6. Putting Delphi on the Server 7. Riding the Mobile Revolution with FireMonkey 8. Using Specific Platform Features Index

Manipulating and transforming XML documents

XML stands for eXtensible Markup Language (http://en.wikipedia.org/wiki/XML) and is designed to represent, transport, and store hierarchical data in the trees of nodes. You can use XML to communicate with different systems, and store configuration files, complex entities, and so on. They all use a standard and powerful format. Delphi has had good support for XML for more than a decade now.

Getting ready

All the basic XML-related activities can be summarized with the following points:

  • Generate XML data
  • Parse XML data
  • Parse XML data and modify it

In this recipe, you will see how to carry out all these activities.

How to do it…

  1. Create a new VCL application and drop three TButton and a TMemo. Align all the buttons as a toolbar at the top of the form and the memo to the remaining form client area.
  2. From left to right, name the buttons btnGenerateXML, btnModifyXML, btnParseXML, and btnTransformXML.
  3. The real work on the XML will be done by the TXMLDocument component. So, drop one instance of the form and set its DOMVendor property to Omni XML.
  4. We will use static data as our data source. A simple matrix is enough for this recipe. Just after the implementation section of the unit, write the code that follows:
    type
      TCarInfo = (
      Manufacturer = 1,
      Name = 2,
      Currency = 3,
      Price = 4);
    
    var
      Cars: array [1 .. 4] of array [Manufacturer .. Price] of string = (
          (
            'Ferrari','360 Modena','EUR', '250,000'
          ),
          (
            'Ford', 'Mustang', 'USD', '80,000'
          ),
          (
            'Lamborghini', 'Countach', 'EUR','300,000'
          ),
          (
            'Chevrolet', 'Corvette', 'USD', '100,000'
          )
        );
  5. We will use a TMemo to display the XML and the data. To keep things clear, create a public property called Xml on the form and map its setter and getter to the Memo1.Lines.Text property. Use the following code:
    //…other form methods declaration 
    private
      procedure SetXML(const Value: String);
      function GetXML: String;
    public
      property Xml: String read GetXML write SetXML;
    end;
    
    //…then in the implementation section
    function TMainForm.GetXML: String;
    begin
      Result := Memo1.Lines.Text;
    end;
    
    procedure TMainForm.SetXML(const Value: String);
    begin
      Memo1.Lines.Text := Value;
    end;
  6. Now, create event handlers for each button. For btnGenerateXML, write the following code:
    procedure TMainForm.btnGenerateXMLClick(Sender: TObject);
    var
      RootNode, Car, CarPrice: IXMLNode;
      i: Integer;
      s: String;
    begin
      XMLDocument1.Active := True;
      try
        XMLDocument1.Version := '1.0';
        RootNode := XMLDocument1.AddChild('cars');
        for i := Low(Cars) to High(Cars) do
        begin
          Car := XMLDocument1.CreateNode('car');
          Car.AddChild('manufacturer').Text := Cars[i][TCarInfo.Manufacturer];
          Car.AddChild('name').Text := Cars[i][TCarInfo.Name];
          CarPrice := Car.AddChild('price');
          CarPrice.Attributes['currency'] := Cars[i][TCarInfo.Currency];
          CarPrice.Text := Cars[i][TCarInfo.Price];
          RootNode.ChildNodes.Add(Car);
        end;
        XMLDocument1.SaveToXML(s);
        Xml := s;
      finally
        XMLDocument1.Active := False;
      end;
    end;
  7. Now, we have to write the code to change the XML. In the btnModifyXML click event handler, write the following code:
    procedure TMainForm.btnModifyXMLClick(Sender: TObject);
    var
      Car, CarPrice: IXMLNode;
      s: string;
    begin
      XMLDocument1.LoadFromXML(Xml);
      try
        Xml := '';
        Car := XMLDocument1.CreateNode('car');
        Car.AddChild('manufacturer').Text := 'Hennessey';
        Car.AddChild('name').Text := 'Venom GT';
        CarPrice := Car.AddChild('price');
        CarPrice.Attributes['currency'] := 'USD';
        CarPrice.Text := '600,000';
        XMLDocument1.DocumentElement.ChildNodes.Add(Car);
        XMLDocument1.SaveToXML(s);
        Xml := s;
      finally
        XMLDocument1.Active := False;
      end;
    end;
  8. Write the following code under the btnParseXML click event handler:
    procedure TMainForm.btnParseXMLClick(Sender: TObject);
    var
      CarsList: IDOMNodeList;
      CurrNode: IDOMNode;
      childidx, i: Integer;
      CarName, CarManufacturer, CarPrice, CarCurrencyType: string;
    begin
      XMLDocument1.LoadFromXML(Xml);
      try
        Xml := '';
        CarsList := XMLDocument1.DOMDocument.getElementsByTagName('car');
        for i := 0 to CarsList.length - 1 do
        begin
          CarName := '';  CarManufacturer := '';
          CarPrice := '';  CarCurrencyType := '';
          for childidx := 0 to CarsList[i].ChildNodes.length - 1 do
          begin
            CurrNode := CarsList[i].ChildNodes[childidx];
            if CurrNode.nodeName.Equals('name') then
              CarName := CurrNode.firstChild.nodeValue;
            if CurrNode.nodeName.Equals('manufacturer') then
              CarManufacturer := CurrNode.firstChild.nodeValue;
            if CurrNode.nodeName.Equals('price') then
            begin
              CarPrice := CurrNode.firstChild.nodeValue;
              CarCurrencyType := CurrNode.Attributes.getNamedItem('currency').nodeValue;
            end;
          end;
          Xml := Xml + 'Name = ' + CarName + sLineBreak + 'Manufacturer = ' + CarManufacturer + sLineBreak + 'Price = ' + CarPrice + CarCurrencyType + sLineBreak + '-----' + sLineBreak;
        end;
      finally
        XMLDocument1.Active := False;
      end;
    end;
  9. Finally, write the following code under the btnTransformXML click event handler:
    procedure TMainForm.btnTransformClick(Sender: TObject);
    var
      LXML, LXSL: string;
      LOutput: string;
    begin
      LXML := TFile.ReadAllText('..\..\..\cars.xml');
      LXSL := TFile.ReadAllText('..\..\..\cars.xslt');
      LOutput := Transform(LXML, LXSL);
      TFile.WriteAllText('..\..\..\cars.html', LOutput);
      ShellExecute(0, PChar('open'), PChar('file:///' + TPath.GetFullPath('..\..\..\cars.html')), nil, nil, SW_SHOW);
    end;
  10. Now, add the following function in your form implementation section:
    function Transform(XMLData: string; XSLT: string): String;
    var
      LXML, LXSL: IXMLDocument;
      LOutput: WideString;
    begin
      LXML := LoadXMLData(XMLData);
      LXSL := LoadXMLData(XSLT);
      LXML.DocumentElement.TransformNode(LXSL.DocumentElement, LOutput);
      Result := String(LOutput);
    end;
  11. Run the application by hitting F9 (or by going to Run | Run).
  12. Click on the btnGenerateXML button, and you should see some XML data in the memo.
  13. Click on the btnModifyXML button, and you should see some more XML in the memo.
  14. Click on btnParseXML, and you should see the same data as before, but in normal text representation.
  15. After the third click, you should see something similar to the following screenshot:
    How to do it…

    Figure 7.1: Text representation of the XML data generated and modified

  16. Now, copy the cars.xml and cars.xslt files from the respective recipe folder to the parent folder of your project folder and click on the btnTransformXML button.
  17. The system default browser should appear showing, something like the following screenshot:
    How to do it…

    Fig. 7.2 XML data transformed to HTML using a XSLT transformation

How it works…

  1. The first button generates the XML representation of the data in our matrix. We've used some car information as sample data.

    Note

    Note that the prices of the cars are not real!!

  2. To create an XML attribute, there are three fundamental TXMLDocument methods:
    • XMLNode := XMLDocument1.CreateNode('node');
    • XMLNode.AddChild('childnode');
    • XMLNode.Attributes['attrname'] := 'attrvalue';

    There are other very useful methods, but these are the basics of XML generation.

  3. The btnModifyXML button loads the XML into the memo and appends some other data (another car) to the list. Then, it updates the memo with the new updated XML. These are the most important lines to note:
    //Create a node without adding it to the DOM
    Car := XMLDocument1.CreateNode('car');
    
    //fill Car XMLNode… and finally add it to the DOM
    //as child of the root node
    XMLDocument1.DocumentElement.ChildNodes.Add(Car);
  4. The code under the btnParseXMLClick event handler allows us to read the display as normal text the XML data navigating through XML tree.
  5. The code under the btnTransformXMLClick event handler uses the XSLT transformation in cars.xslt and the data in cars.xml to generate a brand new HTML page. The XSLT code is as follows:
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"  
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="html" version="5.0" encoding="UTF-8" indent="yes"/>
      <xsl:template match="cars">
        <html>
          <head>
            <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"/>
            <title>
              Sport Cars
            </title>
          </head>
          <body>
            <div class="container">
            <div class="row">
            <h1>Sport Cars</h1>
              <table class="table table-striped table-hover">
                <thead>
                  <tr>
                    <th>Model</th>
                    <th>Manufacturer</th>
                    <th class="text-right">Price</th>
                  </tr>
                </thead>
                <tbody>
                  <xsl:for-each select="car">
                  <tr>
                    <td>
                      <xsl:value-of select="name"/>
                    </td>
                    <td>
                      <xsl:value-of select="manufacturer"/>
                    </td>
                    <td class="text-right">
                      <span class="glyphicon glyphicon-euro">
                      </span>
                      <xsl:value-of select="price"/>
                    </td>
                  </tr>
                </xsl:for-each>
              </tbody>
            </table>
          </div>
          </div>
        </body>
      </html>
    </xsl:template>
    </xsl:stylesheet>

There's more…

There are many things to say about XML ecospace. There are XML engines that provide facilities to search data in an XML tree (XPath), to validate an XML using another XML (XML Schema or DTD), to transform an XML into another kind of format using another XML (XSLT), and many others use http://en.wikipedia.org/wiki/List_of_XML_markup_languages. The good thing is that just like XML, the DOM object is also standardized. So, every library that is compliant to the standard has the same methods, from Delphi to JavaScript and from Python to C#.

TXMLDocument allows you to select the DOMVendor implementation. By default, there are three implementations available:

  • MSXML:
    • Is from Microsoft, implemented as COM objects
    • Supports XML transformations
    • Is available only on Windows (so no Android, iOS, or MacOSX)
  • Omni XML:
    • Much faster than ADOM and based on the Open Source project.
    • It is cross-platform, so is available on all the supported Delphi platforms. If you plan to write XML handling code on mobile or Mac, this is the way to go.
  • ADOM XML:
    • Is a (quite old) open source Delphi implementation
    • Does not support transformations
    • Is available on all the supported Delphi platforms
    • Is still in Delphi for backward compatibility, consider the Omni XML instead

TXMLDocument uses a Windows-only vendor by default. If you are designing a FireMonkey application that is intended to run on other platforms than Windows, select a cross-platform DOM vendor.

XSLT allows you to transform an XML to something else, using another XML as a "stylesheet." As we saw in this recipe, you can use an XML file and an XSLT file to generate an HTML page that shows the data contained in the XML using XSLT to format the data.

The following function loads the XML and an XSLT documents from two string variables. Then, we use the XSLT document to transform the XML document. The code that follows shows this in detail:

function Transform(XMLData: string; XSLT: string): String;
var
  LXML, LXSL: IXMLDocument;
  LOutput: WideString;
begin
  LXML := LoadXMLData(XMLData);
  LXSL := LoadXMLData(XSLT);
  LXML.DocumentElement.TransformNode(
    LXSL.DocumentElement, LOutput);
  Result := String(LOutput);
end;

This function doesn't know about the output format because it is defined by the XSLT document. The result could be an XML, an HTML, a CSV, or a plain text, or whatever the XSLT defines, but the code does not change.

XSLT can be really useful. I recommend that you go and visit http://www.w3schools.com/xsl/xsl_languages.asp for further details on the language.

You have been reading a chapter from
Delphi Cookbook - Second Edition
Published in: Jun 2016
Publisher:
ISBN-13: 9781785287428
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime