Разбор XML, TXMLDocument

У меня проблема с разбором XML.

Как получить значения полей se_url и phrase?
Мне нужно получить link1_1, link1_2, key1, link2_1, link2_2, key2... которые находятся в se_url и phrase.

В гугле не нашел как это сделать (мануала по работе с TXMLDocument тоже не нашел).

<doc>
  <date2>20120214</date2>
  <date1>20120214</date1>
  <data count="116">
    <row>
      <search_engines count="2">
        <search_engine>
          <se_url>link1_1</se_url>
          <se_page>1</se_page>
          <se_id>2</se_id>
        </search_engine>
        <search_engine>
          <se_url>link1_2</se_url>
          <se_page>1</se_page>
          <se_id>3</se_id>
        </search_engine>
      </search_engines>
      <denial>0.4889</denial>
      <visits>45</visits>
      <page_views>52</page_views>
      <phrase>key1</phrase>
      <visit_time>126</visit_time>
      <depth>1.1556</depth>
    </row>
    <row>
      <search_engines count="2">
        <search_engine>
          <se_url>link2_1</se_url>
          <se_page>1</se_page>
          <se_id>3</se_id>
        </search_engine>
        <search_engine>
          <se_url>link2_2</se_url>
          <se_page>1</se_page>
          <se_id>6</se_id>
        </search_engine>
      </search_engines>
      <denial>0.5714</denial>
      <visits>42</visits>
      <page_views>50</page_views>
      <phrase>key2</phrase>
      <visit_time>109</visit_time>
      <depth>1.1905</depth>
    </row>
  </data>
</doc>

person dedoki    schedule 14.02.2012    source источник
comment
Извините, я понимаю, что английский, вероятно, не ваш родной язык, поскольку документ размещен в домене .ru, но этот вопрос не имеет никакого смысла. Не могли бы вы отредактировать его и предоставить более подробную информацию о том, что именно вы ищете?   -  person Mason Wheeler    schedule 14.02.2012
comment
Публикация большей части вашего вопроса по ссылке за пределами сайта означает, что он становится бессмысленным, если этот другой сайт становится недоступным или исчезает. Пожалуйста, отредактируйте свой вопрос, предоставив здесь хотя бы часть XML-документа, чтобы ваш вопрос оставался полезным для других в будущем и был доступен для поиска. Пока вы редактируете эту часть, вы можете предоставить дополнительную информацию о том, что вы пытаетесь сделать и что вы пробовали до сих пор, и объяснить, почему это не сработало, как ожидалось. Спасибо. :0   -  person Ken White    schedule 14.02.2012
comment
dredei Я пытался показать вам, что имеет в виду Кен, надеюсь, мое редактирование поможет? Вы все равно должны расширить свой вопрос, чтобы сказать, что вы пробовали. Вы даже пробовали MSXML DOM или OmniXML, или вы просто пытались анализировать без парсера XML?   -  person Warren P    schedule 15.02.2012
comment
@ Уоррен, вы только что убрали все стимулы для автора вернуться и отредактировать вопрос. (Вы сделали то же самое, что выполнили чье-то домашнее задание, а затем посоветовали ему прочитать книгу после этого.)   -  person Ken White    schedule 15.02.2012
comment
обновил первый пост и поменял ссылку :)   -  person dedoki    schedule 15.02.2012
comment
@dredei: не меняйте ссылку. Разместите здесь фрагмент XML, как мы просили несколько раз. Опять же, ссылки вне сайта не подходят - здесь ваш вопрос должен стоять сам по себе.   -  person Ken White    schedule 15.02.2012


Ответы (3)


Попробуй это:

uses ComObj, MSXML;

procedure TForm1.Button1Click(Sender: TObject);
var
  xml: IXMLDOMDocument;
  node: IXMLDomNode;
  nodes_row, nodes_se: IXMLDomNodeList;
  i, j: Integer;
  url: string;
begin
  // put url or file name
  url := 'http://softez.pp.ua/gg.xml';

  xml := CreateOleObject('Microsoft.XMLDOM') as IXMLDOMDocument;
  xml.async := False;
  xml.load(url); // or use loadXML to load XML document using a supplied string
  if xml.parseError.errorCode <> 0 then
    raise Exception.Create('XML Load error:' + xml.parseError.reason);

  Memo1.Clear;
  nodes_row := xml.selectNodes('/doc/data/row');
  for i := 0 to nodes_row.length - 1 do
  begin
    node := nodes_row.item[i];
    Memo1.Lines.Add('phrase=' + node.selectSingleNode('phrase').text);
    nodes_se := node.selectNodes('search_engines/search_engine/se_url');
    for j := 0 to nodes_se.length - 1 do
    begin
      node := nodes_se.item[j];
      Memo1.Lines.Add('url=' + node.text);
    end;
    Memo1.Lines.Add('--------------');
  end;
end;

Результат:

phrase=key1
url=link1_1
url=link1_2
--------------
phrase=key2
url=link2_1
url=link2_2
--------------

Ссылка на IXMLDOMDocument

person kobik    schedule 14.02.2012
comment
@KenWhite, спасибо вам за заботу о качестве вопросов на SO :) (кстати, вы знаете, почему мой код не раскрашен? :/) - person kobik; 15.02.2012
comment
Я не знаю, почему это не подсвечивается синтаксис. Я пошел и посмотрел на источник, и он выглядит нормально. Я даже добавил явный языковой комментарий без изменений. ??? - person Ken White; 15.02.2012
comment
Еще один вопрос: как получить значение атрибута? - person dedoki; 16.02.2012

Если вы сначала включите эти 3 подпрограммы библиотеки общего назначения....

uses XMLDoc, XMLIntf, xmldom;

function CreateXMLDocument( var Owner1: TComponent): TXMLDocument;
begin
Owner1 := TComponent.Create( nil);
result  := TXMLDocument.Create( Owner1);
result.Options := [doNodeAutoCreate, doNodeAutoIndent, doAttrNull,
                   doAutoPrefix, doNamespaceDecl];
result.DOMVendor := GetDOMVendor( 'MSXML');
end;

function XPATHSelect( const FocusNode: IXMLNode; const sXPath: string): TArray<IXMLNode>;
var
  DomNodeSelect: IDomNodeSelect;
  DOMNode      : IDomNode;
  DocAccess    : IXmlDocumentAccess;
  Doc          : TXmlDocument;
  DOMNodes     : IDOMNodeList;
  iDOMNode     : integer;
begin
SetLength( result, 0);
if assigned( FocusNode) and
   Supports( FocusNode.DOMNode, IDomNodeSelect, DomNodeSelect) then
    DOMNodes := DomNodeSelect.SelectNodes( sXPath);
if not assigned( DOMNodes) then exit;
SetLength( result, DOMNodes.Length);
for iDOMNode := 0 to DOMNodes.Length - 1 do
  begin
  Doc := nil;
  DOMNode := DOMNodes.item[iDOMNode];
  if Supports( DOMNode, IXmlDocumentAccess, DocAccess) then
    Doc := DocAccess.DocumentObject;
  result[ iDOMNode] := TXmlNode.Create( DOMNode, nil, Doc) as IXMLNode;
  end
end;


function XPATHSelectFirst( const FocusNode: IXMLNode; const sXPath: string; var SelectedNode: IXMLNode): boolean;
var
  DomNodeSelect: IDomNodeSelect;
  DOMNode      : IDomNode;
  DocAccess    : IXmlDocumentAccess;
  Doc          : TXmlDocument;
begin
SelectedNode := nil;
if assigned( FocusNode) and
   Supports( FocusNode.DOMNode, IDomNodeSelect, DomNodeSelect) then
  DOMNode := DomNodeSelect.selectNode( sXPath);
if assigned( DOMNode) and
   Supports( DOMNode.OwnerDocument, IXmlDocumentAccess, DocAccess) then
  Doc := DocAccess.DocumentObject;
if Assigned( DOMNode) then
  SelectedNode := TXmlNode.Create( DOMNode, nil, Doc);
result := assigned( SelectedNode)
end;

Тогда более изящным решением будет...

procedure TForm2.btn1Click(Sender: TObject);
const
  DocumentSource =  'http://softez.pp.ua/gg.xml';
var
  Doc: IXMLDocument;
  DocOwner: TComponent;
  RowNode, PhraseNode, UrlNode: IXMLNode;

  procedure PutLn( const LineFmt: string; const Args: array of const);
  begin
  memo2.Lines.Add( Format( LineFmt, Args))
  end;

begin
memo2.Clear;
Doc := CreateXMLDocument( DocOwner);
Doc.LoadFromFile( DocumentSource);
for RowNode in XPATHSelect( Doc.DocumentElement, '//row[phrase]') do
  begin
  if not XPATHSelectFirst( RowNode, 'phrase', PhraseNode) then continue;
  PutLn( 'phrase=%s', [PhraseNode.NodeValue]);
  for UrlNode in XPATHSelect( RowNode, 'search_engines/search_engine/se_url') do
    PutLn( 'url=%s', [UrlNode.NodeValue]);
  PutLn('--------------',[])
  end;
DocOwner.Free;
end;

Это было протестировано на Delphi 2010 и работает отлично.

person Sean B. Durkin    schedule 16.02.2012

И на всякий случай вот еще один ответ, если вы не боитесь добавить немного XSLT!

const Transform =
'<?xml version="1.0" encoding="utf-8"?>' +
'<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> ' +
' <xsl:output method="text" indent="no"/>' +
' <xsl:template match="text()|@*"/>' +
' <xsl:template match="//row">' +
'  <xsl:text>phrase=</xsl:text>' +
'  <xsl:value-of select="phrase"/>' +
'  <xsl:text>&#10;</xsl:text>' +
'  <xsl:apply-templates/>' +
'  <xsl:text>--------------&#10;</xsl:text>' +
' </xsl:template>' +
' <xsl:template match="search_engines/search_engine/se_url">' +
'  <xsl:text>url=</xsl:text>' +
'  <xsl:value-of select="."/>' +
'  <xsl:text>&#10;</xsl:text>' +
' </xsl:template>' +
'</xsl:stylesheet>';


procedure TForm2.btn1Click( Sender: TObject);
const
  DocumentSource =  'http://softez.pp.ua/gg.xml';
var
  Doc, Style: IXMLDocument;
  DocOwner, StyleOwner: TComponent;
  sOut: widestring;
begin
  memo2.Clear;
  Doc := CreateXMLDocument( DocOwner);
  Doc.LoadFromFile( DocumentSource);
  Style := CreateXMLDocument( StyleOwner);
  Style.LoadFromXML( Transform);
  Doc.DocumentElement.TransformNode( Style.DocumentElement, sOut);
  memo2.Lines.Add( sOut);
  DocOwner.Free;  
  StyleOwner.Free
end;
person Sean B. Durkin    schedule 16.02.2012
comment
Кажется, я получаю сообщение об ошибке. Он говорит, что необъявленный идентификатор: «CreateXMLDocument». Я использую Delphi 10.1 Berlin Starter Edition и FireMonkey. Затем у меня также есть следующий список использования, связанный с XML: Xml.xmldom, Xml.XMLIntf, Xml.adomxmldom, Xml.XMLDoc. - person Shaun Roselt; 10.02.2017