уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Работа над клиентов для DelphiFeeds.ru идёт полным ходом — получил доступ к XML-RPC и теперь познаю всё тонкости работы с XML-RPC в Joomla!. Соответственно, теперь вся работа клиента будет строится на чтении XML-документов, содержащих информацию по публикациям и это обстоятельство (использование только XML) подтолкнуло меня на то, чтобы более детально разобраться с парсингом XML в Mac OS X. Как знать, вполне возможно, что с выходом Mobile Studio появится и Mac- или iOS-версия клиента (при условии, что он вообще будет востребован на этих платформах).

Как я уже говорил в предыдущей статье, посвященной работе с Mac OS X в Delphi XE3, в примерах к RAD Studio есть замечательный примерчик, демонстрирующий создание XML-документов с использованием интерфейсов из Foundation Framework. Вот его-то я и решил использовать как основу для своих будущих исследований возможностей Mac OS X.

Работая в ОС Windows мы привыкли к разнообразию библиотек и компонентов для решения самых разнообразных задач, в том числе и для работы с XML. Хочешь — бери MSXML, не нравится MSXML — бери NativeXML, OmniXML. Вариантов масса. Однако, когда мы пробуем создать что-либо, работающее в Mac OS X, возникает закономерный вопрос: что и, главное, как использовать? Можно, конечно, забраться на форум cyberforum.ru, окопаться в темах про XML и долго и упорно перенимать чужой опыт, а можно разобраться самим. Далеко не каждая библиотека Delphi, заявленная как кроссплатформенная успешно заработает под ОС от Apple, а изобретать очередной велосипед особенного желания как-то не возникает. Да и зачем? В OS X уже есть необходимые интерфейсы для работы с XML — так почему бы не начать именно с них?

Содержание

Получить основные сведения про устройство и архитектуру Mac OS Вы можете из статьи «Delphi XE3: работа с Mac OS X.«

NSXMLDocument — создание XML-документа, чтение/запись его свойств

Представляет собой XML-документ, который может содержать сколько угодно дочерних узлов и только один корневой узел. Здесь реализованы методы чтения XML из локальных файлов, строк, URL, представления данных в виде plain text, html и т.д. Здесь же, соответственно, реализованы и основные методы для работы с узлами XML-документа. Рассмотрим несколько примеров работы с NSXMLDocument.

Пример №1. Загружаем XML-данные из файла.

function LoadXMLFromFile(const FileName: string): NSXMLDocument;
var Data: NSData;
begin
  //получаем NSData, загрузив данные из файла
  Data:=TNSData.Wrap(TNSData.OCClass.dataWithContentsOfFile(NSSTR(FileName)));
  //создаем пустой NSXMLDocument
  Result:=TNSXMLDocument.Create;
  //грузим XML-данные из NSData
  Result.initWithData(Data, NSXMLDocumentTidyXML,nil);
end;

Здесь для загрузки данных в пустой NSXMLDocument мы воспользовались интерфейсом NSData, у которого использовали метод dataWithContentsOfFile для взятия данных из файла. Также просто мы можем получить NSXMLDocument и из простой строки.

NSData представляет объекты данных и объектно-ориентированные обёртки для байтовых буферов. Объекты NSData позволяют к простым буферам данных обращаться как к объектам. NSData создаёт статические объекты данных. Для работы с динамическими объектами данных используется класс NSMutableData

Пример №2. Загрузка XML-данных из строки

function LoadXMLFromString(const XMLData: string): NSXMLDocument;
begin
 //создали пустой NSXMLDocument
 Result:=TNSXMLDocument.Create;
 //создали NSString и передали его NSXMLDocument
 Result.initWithXMLString(NSSTR(XMLData), NSXMLDocumentTidyXML,nil);
end;

Теперь посмотрим какую информацию об XML мы можем получить, используя только NSXMLDocument.

Пример №3. Чтение свойств XML-документа

У NSXMLDocument имеются следующие методы для получения и установки свойств документа:

Метод чтение свойства Метод записи свойства Описание Тид данных
characterEncoding setCharacterEncoding Кодировка документа NSString
documentContentKind setDocumentContentKind Тип содержимого документа. Может принимать одно из следующих значений:
  • NSXMLDocumentXMLKind — документ представлен как XML,
  • NSXMLDocumentXHTMLKind — документ представлен как XHTML,
  • NSXMLDocumentHTMLKind — документ представлен как HTML,
  • NSXMLDocumentTextKind — документ представлен как Plain Text.
LongWord
DTD setDTD Описание схемы документа NSXMLDTD
isStandalone setStandalone Возвращает False, если документ не содержит внешних DTD boolean
MIMEType setMIMEType MIME-тип документ, например «text/xml» NSString
version setVersion Версия документа NSString

Так как большинство методов чтения свойств документа возвращают ссылки на интерфейсы, то не стоит читать свойства так:

XmlContent.Lines.Add('Character Encoding: '+XmlDoc.characterEncoding.UTF8String);

Так как вполне возможно, что метод вернет и nil. Организовать чтение свойств документа можно, например, так:

procedure TFrmXml.GetDocumentProperties(Document: NSXMLDocument;
  PropList: TStrings);
var
  Version, Encoding, MimeType: NSString;
begin
  Version := Document.Version;
  if Assigned(Version) then
    PropList.Add('Version: ' + Version.UTF8String)
  else
    PropList.Add('Version: Unknown');
 
  Encoding := Document.characterEncoding;
  if Assigned(Encoding) then
    PropList.Add('Character Encoding: ' + Encoding.UTF8String)
  else
    PropList.Add('Encoding: Unknown');
 
  PropList.Add('Standalone: ' + BoolToStr(Document.isStandalone, true));
  case Document.documentContentKind of
    NSXMLDocumentXMLKind:  PropList.Add('Content Kind: XML');
    NSXMLDocumentHTMLKind: PropList.Add('Content Kind: HTML');
    NSXMLDocumentXHTMLKind:PropList.Add('Content Kind: XHTML');
    NSXMLDocumentTextKind: PropList.Add('Content Kind: Plain Text');
  end;
 
 MimeType:=Document.MIMEType;
  if Assigned(MimeType) then
    PropList.Add('MimeType: '+MimeType.UTF8String)
  else
    PropList.Add('MimeType: Unknown')

Следующий момент работы с NSXMLDocument — чтение данных документа.

Пример №4. Чтение данных из XML-документа

Для получения данных их XML-документа у NSXMLDocument предусмотрены следующие методы:

Метод Описание Тип результата
XMLData Возвращает контент документа как NSData NSData
XMLDataWithOptions Возвращает контент документа как NSData. Входной параметр options может принимать одно из следующих значений:
  • NSXMLDocumentTidyHTML,
  • NSXMLDocumentTidyXML,
  • NSXMLDocumentValidate,
  • NSXMLDocumentXInclude,
  • NSXMLDocumentIncludeContentTypeDeclaration
 NSData
rootElement Возвращает корневой элемент документа  NSXMLElement

Описание констант для XMLDataWithOptions можно посмотреть в официальной документации.

Для примера попробуем загрузить XML-документ из файла и прочитать данные о корневом элементе (с использованием XMLData и XMLDataWithOptions можно ознакомиться в официальном примере Embarcadero — XMLOnMac):

procedure GetRootInfo(Document: NSXMLDocument; PropList: TStrings);
var Root: NSXMLElement;
    Name, URI, XMLString: NSString;
begin
  Root:=Document.rootElement;
  if Assigned(Root) then
    begin
      Name:=Root.name;
      if Assigned(Name) then
        PropList.Add('Root Name: '+Name.UTF8String);
      URI:=Root.URI;
      if Assigned(URI) then
        PropList.Add('URI: '+URI.UTF8String);
      XMLString:=Root.XMLString;
      if Assigned(XMLString) then
        PropList.Add('XMLString: '+XMLString.UTF8String);
    end
  else
    PropList.Add('RRoot Element not assigned')
end;

На этом примере мы вплотную подошли к работе со следующими интерфейсами — NSXMLNode и NSXMLElement. Посмотрим, что из себя представляют эти интерфейсы и как их можно использовать для работы с XML-данными.

NSXMLNode и NSXMLElement — работа с узлами XML-документа

NSXMLNode представляет собой узел XML-документа и содержит необходимые методы для работы с дочерними и родительскими элементами, атрибутами узла и т.д. NSXMLNode  является родителем для NSXMLElement. Так, если NSXMLNode предоставляет доступ к таким свойствам как количество дочерних элементов, массив дочерних элементов, имя узла, уровень и т.д., то NSXMLElement расширяет возможности NSXMLNode, предоставляя доступ, например, к атрибутам узла.

Пример №1. Чтение имен дочерних узлов

procedure GetChilNodesNames(RootElement: NSXMLElement;
  NamesList: TStrings);
var i,count:integer;
    Node: NSXMLElement;
    childs: NSArray;
begin
 childs:=RootElement.children;
 count:=childs.count;
 for i:=0 to Count-1 do
   begin
     Node:=TNSXMLElement.Wrap(childs.objectAtIndex(i));
     NamesList.Add(Node.name.UTF8String);
     GetNodeAttributes(Node,NamesList);
   end;
end

Здесь для чтения имен дочерних узлов мы воспользовались методом children, который вернул нам массив NSArray, содержащий все дочерние узлы.

Класс NSArray управляет упорядоченной коллекцией элементов (массивом). Вы можете использовать NSArray для создания неизменяемых массивов. Это значит, что все элементы NSArray доступны только для чтения. Имеется возможность доступа к элементам массива по индексу. Массивы могут хранить элементы различных типов. Массивы поддерживают сортировку и поиск элементов, а также сравнение самих массивов между собой

Следующий простой пример демонстрирует чтение атрибутов узла.

Пример №2. Чтение атрибутов узла

procedure TFrmXml.GetNodeAttributes(XMLNode: NSXMLElement; AttrList: TStrings);
var Element: NSXMLElement;
    i:integer;
    AttrArray: NSArray;
    AttrNode: NSXMLNode;
begin
  AttrArray:=XMLNode.attributes;
  if Assigned(AttrArray) then
    begin
      for I := 0 to AttrArray.count-1 do
         begin
           AttrNode:=TNSXMLNode.Wrap(AttrArray.objectAtIndex(i));
           AttrList.Add('Attr Name: '+AttrNode.name.UTF8String+' Value: '+AttrNode.stringValue.UTF8String)
         end;
    end;
end;

И теперь мы уже вплотную подошли к тому, чтобы полностью прочитать и разобрать XML-документ. Для этого мы напишем свое небольшое приложение, которое назовем просто — XMLBrowser.

 Приложение XMLBrowser для Mac OS X

Наше приложение будет делать совсем не много, а именно:

  • строить дерево всех узлов XML
  • получать строковые значения узлов (stringValue)
  • получать доступ к атрибутам узла и выводить их имена/значения в таблицу
  • читать другие свойства узлов XML: уровень, индекс, имя узла, количество дочерних узлов и т.д.

Главная форма приложения у меня выглядит следующим образом:

xml parsing on mac

Главное окно приложения XMLBrowser

Для работы с XML-документом определена переменная XmlDoc и следующие методы:

type
  TMainForm = class(TForm)
    [...]
  private
    XmlDoc: NSXMLDocument;
    function LoadXmlFromFile(const Filename: string):NSXMLDocument;
    procedure GenerateTree;
    procedure ReadNodePropertys(XPath:string);
    procedure ReadAttributes(XPath:string);
  public
    { Public declarations }
  end;

Метод LoadXmlFromFile в точности повторяет первый рассмотренный пример по работе с NSXMLDocument.

Метод GenerateTree выводит в TreeView названия всех узлов в XML-документа. Выглядит он следующим образом:

procedure TMainForm.GenerateTree;
 
function AddNode(RTreeNode: TTreeViewItem; Element: NSXMLElement):TTreeViewItem;
begin
 Result:=TTreeViewItem.Create(RTreeNode);
 Result.Parent:=RTreeNode;
 Result.Text:=Element.name.UTF8String;
 Result.TagString:=Element.XPath.UTF8String;
 RTreeNode.AddObject(Result);
end;
 
procedure ProcessNode(Node: NSXMLElement; TreeNode: TTreeViewItem);
var childArr: NSArray;
    I: Integer;
    ChildNode: NSXMLElement;
    childTreeNode: TTreeViewItem;
begin
  if Node.childCount=0 then Exit;
  childArr:=Node.children;
  for I := 0 to childArr.count-1 do
    begin
      ChildNode:=TNSXMLElement.Wrap(childArr.objectAtIndex(i));
      if (ChildNode.kind=NSXMLDocumentKind)or(ChildNode.kind=NSXMLElementKind) then
        begin
          childTreeNode:=AddNode(TreeNode,ChildNode);
          if ChildNode.childCount>0 then
            ProcessNode(ChildNode,childTreeNode);
        end;
    end;
end;
 
var RootNode: NSXMLElement;
    TreeNode: TTreeViewItem;
    Arr: NSArray;
    I: Integer;
begin
  XMLTreeView.Clear;
  //добавляем корневой узел в TreeView
  RootNode:=XmlDoc.rootElement;
  TreeNode:=TTreeViewItem.Create(XMLTreeView);
  TreeNode.Parent:=XMLTreeView;
  TreeNode.Text:=RootNode.name.UTF8String;
  TreeNode.TagString:=RootNode.XPath.UTF8String;
  XMLTreeView.AddObject(TreeNode);
  //читаем дочерние узлы
  Arr:=RootNode.children;
  for I := 0 to Arr.count-1 do
    begin
      RootNode:=TNSXMLElement.Wrap(Arr.objectAtIndex(i));
      //обходим все дочерние узлы
      ProcessNode(RootNode,AddNode(TreeNode,RootNode));
    end;
end;

В принципе, эта процедура практически ничем не отличается от тех же процедур обхода XML-дерева в VCL и Windows. Различие лишь в том, что здесь для хранения информации об XML-узле мы используем свойство TagString узла TreeView и делаем мы это следующим образом:

function AddNode(RTreeNode: TTreeViewItem; Element: NSXMLElement):TTreeViewItem;
begin
 [...]
 Result.TagString:=Element.XPath.UTF8String;
 [...]
end;
XPath (XML Path Language) — язык запросов к элементам XML-документа. Разработан для организации доступа к частям документа XML в файлах трансформации XSLT и является стандартом консорциума W3C. XPath призван реализовать навигацию по DOM в XML. В XPath используется компактный синтаксис, отличный от принятого в XML. Более подробную информацию по XPath см. в Wikipedia.

Сохранив XPath XML-узла в TagString узла TreeView мы можем в любой момент получить значение NSXMLElement, что и было реализовано в обработчике OnClick TreeView:

procedure TMainForm.XMLTreeViewClick(Sender: TObject);
begin
  ReadNodePropertys(XMLTreeView.Selected.TagString);
  ReadAttributes(XMLTreeView.Selected.TagString);
end;

Здесь методам ReadNodePropertys и ReadAttributes в параметрах передается XPath узла, а сами методы реализуют заполнение двух таблиц на форме — свойств узла и его атрибутов:

procedure TMainForm.ReadNodePropertys(XPath: string);
var Arr: NSArray;
    Element:NSXMLElement;
begin
  Arr:=XmlDoc.nodesForXPath(NSSTR(XPath),nil);
  if Assigned(Arr) then
    begin
      Element:=TNSXMLElement.Wrap(Arr.objectAtIndex(0));
      NodeStrValue.Text:=Element.stringValue.UTF8String;
      CommonPropsGrid.Cells[1,0]:=Element.XPath.UTF8String;
      CommonPropsGrid.Cells[1,1]:=Element.name.UTF8String;
      CommonPropsGrid.Cells[1,2]:=Element.localName.UTF8String;
      CommonPropsGrid.Cells[1,3]:=IntToStr(Element.index);
      CommonPropsGrid.Cells[1,4]:=IntToStr(Element.childCount);
      CommonPropsGrid.Cells[1,5]:=IntToStr(Element.level);
    end;
end;
 
procedure TMainForm.ReadAttributes(XPath: string);
var Arr: NSArray;
    Element:NSXMLElement;
    Attributes: NSArray;
    I: Integer;
    AttrNode: NSXMLNode;
begin
  AttributesGrid.RowCount:=0;
  //получаем узлы по XPath
  Arr:=XmlDoc.nodesForXPath(NSSTR(XPath),nil);
  if Assigned(Arr) then
    begin
      Element:=TNSXMLElement.Wrap(Arr.objectAtIndex(0));
      Attributes:=Element.attributes;
      if Assigned(Attributes) then
        begin
          AttributesGrid.RowCount:=Attributes.count;
          for I := 0 to AttributesGrid.RowCount-1 do
            begin
              AttrNode:=TNSXMLNode.Wrap(Attributes.objectAtIndex(i));
              AttributesGrid.Cells[0,i]:=AttrNode.name.UTF8String;
              AttributesGrid.Cells[1,i]:=AttrNode.stringValue.UTF8String;
            end;
        end;
    end;
end;

В этим процедурах XPath всегда однозначно определяет конкретный узел в XML, поэтому при работе с массивом я только проверяю, что узел найден (массив определен) и не проверяю его размерность — в массиве гарантировано будет содержаться только один узел.

Теперь остается проверить работу нашего приложения. Для примера я взял два XML-файла:

  • XML, содержащий ответ XML-RPC
  • XML, описывающий скин для видео-плеера.

Первый XML-документ:

Структура ответа XML-RPC

Второй XML-документ:

Структура XML-документа описания скина виде-плеера

Вот так, используя всего пару интерфейсов из Foundation Framework можно разбирать XML-документы. Приведенный выше пример программы — основа для дальнейшего изучения вопроса, т.к. ещё достаточно много моментов по работе с XML в Mac OS X осталось «за бортом», например, как работать с namespase, CDATA или комментариями в XML.

Скачать исходник: Исходники —> XML и JSON в Delphi
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
1 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
trackback
Delphi XE3: работа с Mac OS X. | Delphi в Internet
04/02/2013 16:54

[…] Также про работу с XML в Mac OS Вы можете узнать из статьи "Delphi XE3: работа с XML в Mac OS X" […]