You know the documentation is really bad when…

… even a prominent ‘Product Line Manager and Technical Lead Evangelist’ for Delphi gets something pretty simple wrong: pace a recent post by Andreano Lanusse, TXMLDocument is in fact available for OS X development. The issue he had (I assume) was with the DOMVendor property: drop a TXMLDocument onto a form at design time, and this property is filled with the platform default, which on Windows is MSXML. This then causes the unit for the ‘vendor’ DOM to be added to the uses clause (unnecessarily in my view, but hey, that’s what it has always done IIRC), which for MSXML will of course be a Windows-only unit. To fix, either don’t create a TXMLDocument at design time, or if you do, set its DOMVendor property to the cross-platform ‘ADOM XML v4’ in the Object Inspector before saving. If Xml.Win.msxmldom has already been added, just remove it and you’ll be away.

So, here’s a cross-compiling version of Andreano Lanusse’s demo code:

unit MainForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, XML.XMLDoc, FMX.Dialogs,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Layouts,
  FMX.Memo, FMX.Edit, FMX.Effects, FMX.Objects, Xml.xmldom, Xml.XMLIntf;

type
  TFrmXml = class(TForm)
    CreateXMLButton: TButton;
    XmlContent: TMemo;
    XMLLocation: TEdit;
    Label1: TLabel;
    Image1: TImage;
    procedure CreateXMLButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  end;

var
  FrmXml: TFrmXml;

implementation

{$R *.fmx}

procedure TFrmXml.CreateXMLButtonClick(Sender: TObject);
var
  XmlDoc: IXMLDocument;
  Root, Book, Author, Publisher: IXMLNode;
begin
  // Create Xml Document
  XmlDoc := TXMLDocument.Create(nil);
  XmlDoc.Active := True;
  XmlDoc.Options := XmlDoc.Options + [doNodeAutoIndent];
  XmlDoc.Version := '1.0';

  // Create the root doc element with one attributes
  Root := XmlDoc.CreateNode('BookStore');
  Root.Attributes['url'] := 'http://www.amazon.com';
  XmlDoc.DocumentElement := Root;

  // Create the first Book node
  Book := XmlDoc.CreateNode('Book');
  Book.Attributes['Name'] := 'Steve Jobs';

  // Create the Author and Publisher elements
  Author := XmlDoc.CreateNode('Author');
  Author.Text := 'Walter Isaacson';
  Publisher := XmlDoc.CreateNode('Publisher');
  Publisher.Text := 'Simon Schuster (October 24, 2011)';

  // Add the elements to the XML
  Root.ChildNodes.Add(Book);
  Book.ChildNodes.Add(Author);
  Book.ChildNodes.Add(Publisher);

  // Create the second Book node
  Book := XmlDoc.CreateNode('Book');
  Book.Attributes['Name'] := 'Clean Code: A Handbook of Agile Software Craftsmanship';
  Author := XmlDoc.CreateNode('Author');
  Author.Text := 'Robert C. Martin';
  Publisher := XmlDoc.CreateNode('Publisher');
  Publisher.Text := 'Prentice Hall; 1 edition (August 11, 2008)';

  // Add the elements from the second Book node to the XML
  Root.ChildNodes.Add(Book);
  Book.ChildNodes.Add(Author);
  Book.ChildNodes.Add(Publisher);

  XmlDoc.SaveToFile(XMLLocation.Text);

  XmlContent.Lines.LoadFromFile(XMLLocation.Text);

end;

procedure TFrmXml.FormCreate(Sender: TObject);
begin
  XMLLocation.Text := IncludeTrailingPathDelimiter(GetHomePath) + 'create.xml';
end;

end.

Proof of the pudding, here’s the revised demo running on my iMac:

11 thoughts on “You know the documentation is really bad when…

  1. Thank you for pointing out it actually works – it would have been a huge debacle.

    Anyway I wonder why the IDE doesn’t automatically wrap OS-specific units with an $IFDEF when adding it. And probably they should implement OS-specific properties too.

    • Well… personally I lay the blame squarely on the TXMLDocument property, not the IDE (which shouldn’t try and be overly clever IMO). The silly thing is, it’s not like being able to select the DOM ‘vendor’ at design time is essential functionality in the first place – the default is perfectly fine in the vast majority of cases, and if it isn’t in a particular one, a single line of code will fix it.

      • Well, I guess a lot of xplat code will have $IFDEFs here and there, automatically wrapping OS-specific units IMHO is not being “overly clever”, just not blindly stupid. Maybe adding a “DefaultPlatformDOM” property value mapped to the actual platform standard DOM could help in this case.

      • “I guess a lot of xplat code will have $IFDEFs here and there”

        Oh, absolutely, but the question is whether a FireMonkey form unit should reflect the fact – IMO, platform-specific properties at design-time is a recipe for messiness. One of the better things about FMX as it currently stands is how things are designed in a manner that minimises IFDEFs even in the FMX sources themselves. By using abstract classes, most platform specific things are relegated to their own units, an approach I much prefer compared to (say) Lazarus’ use of include files.

        • FM can take a xplat approach from ground app, the problem IMHO is in non-FM code which has to rely on already existing classes that were designed before xplat support, like TXMLDocument. Platform specific properties could be a little messy, but sometimes useful, I think something that can be expanded in the IDE like:

          DOMVendor
          Windows32 | MSXML
          Windows64 | MSXML
          MacOS32| ADOMXML
          ….

          Maybe showing the by default the value for the active target – after all while compiling it just mean to put in the .dfm or whatever the proper value.

        • “the problem IMHO is in non-FM code which has to rely on already existing classes that were designed before xplat support, like TXMLDocument”

          Au contraire! TXMLDocument was introduced during the Kylix times (either in D6 or even K1 – it was definitely in K2). That’s part of why there’s a DOMVendor property in the first place – it was just implemented in a less-than-ideal fashion.

  2. I got a problem with TXMLDocument, thread and cross platform.
    For example, I use IXMLDocument in thread to parse XML but I requires DOMVendor. By using IXMLDocument, I don’t have any chance to set DOMVendor, so, if I continue using TXMLDocument, I have to call CoInitizlize/Ex but this is Windows related and it can not be use on other OSes

    • You can force ADOM by doing something like this:

      implementation
      
      uses
        Xml.Xmldom, Xml.adomxmldom;
      
      //...
      
      initialization
        DefaultDOMVendor := SAdom4XmlVendor;
      end.
      

      Alternatively, wrap the COM calls with IFDEFs (test for MSWINDOWS).

  3. Hi Chris,
    Doing anything with COM is Windows related but my problem is writing a program in OSX and iOS also.
    Luckily, I’m finally finding out the OmniXML improved for FreePascal: http://www.kluug.net/omnixml.php
    It’s platform independence and thread safe also.
    Moreover, it uses similar class types as XMLDocument such as IXMLNode or IXMLDocument, so, I didn’t change too much in my source code.
    Thanks

    • I know ‘doing anything with COM is Windows related’ – that’s why I suggested either forcing ADOM on Windows, or just IFDEF’ing the COM calls if you want to still use MSXML as the Windows backend.

      That said, I take it you realise IXMLDocument/TXMLDocument is just a layer on top of a DOM implementation that does the actual work? When targeting Windows the default backend is MSXML, otherwise it is ADOM (an open source Object Pascal implementation of the DOM object model). However, you can use ADOM on Windows too.

Leave a reply to Chris Rolliston Cancel reply