Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > XML-парсер. Помогите прочитать тег,


Автор: navodri 28.5.2010, 16:07
Есть XML-файл примерно следующего характера

Код

<weather ver="2.0">
 <dayf>
  <day d="0" t="Friday" dt="May 28">

   <part p="d">
     <icon>12</icon>
   </part>

   <part p="n">
    <icon>16</icon>
   </part>

  </day>
 </dayf>
</weather>


Для его парсинга использую следующую структуру: 

Код

 for i:=1 to GetNodesCountByName(['//weather','//dayf'],'day') do 
  begin
   Label1.Caption := День - ' + GetNodeItemTextFromID(['//weather','//dayf'], i, 't')) + ', ' +  GetNodeItemTextFromID(['//weather','//dayf'], i, 'dt'));

 end;


Всё бы хорошо, если бы небыло тега part, в котором мне надо прочитать номер значка (icon). 
Подскажите, как это сделать?


Ниже представляю код парсера, которым пользуюсь:

Код

unit XMLParser;

interface

uses
  ComObj;

var
 XML: Variant;  //главный объект XML: загружает/сохраняет XML и т.п.

function CreateXML: boolean;
procedure LoadXML(XML_:string); //XML_ - строка_XML или имя_файла или URL
procedure SaveXML(FileName: string);
function GetNodeText(Path: array of string): string;
function GetNodeItemText(Path: array of string; Item:string): string;
function GetNodeItemTextFromID(Path: array of string; ID:integer; Item:string): string;
function GetNodeItemTextByNameFromID(Path: array of string; NodeName:string; ID:integer; Item:string): string;
function GetNodesCount(Path: array of string): integer;//кол-во всех элементов у последнего "нода" в Path
function GetNodesCountByName(Path: array of string;NodeName:string): integer; //кол-во элементов с именем NodeName у последнего "нода" в Path
function GetNodeTextFromID(Path: array of string; ID:integer): string;
function GetNodeTextByNameFromID(Path: array of string; NodeName:string; ID:integer):string;

implementation

function CreateXML: boolean;
begin
 XML := CreateOleObject('Microsoft.XMLDOM');
 XML.async := false; //если true то вроде динамически будет подгружать XML
  Result := (XML.parseError.reason = '');
end;

procedure LoadXML(XML_:string);
begin
 XML.load(XML_);
end;

procedure SaveXML(FileName:string);
begin
 XML.save(FileName);
end;

function GetNodeText(Path:array of string):string;
var
 Node: variant;
 i:integer;
begin
 Node:=XML.documentElement;
 for i:=0 to High(Path) do Node := XML.SelectSingleNode(Path[i]);
 if VarIsEmpty(Node)
  then Result := ''
  else Result := Node.Text;
end;

function GetNodeItemText(Path:array of string;Item:string): string;
var
 Node: variant;
 i: integer;
begin
 Node := XML.documentElement;
 for i:=0 to High(Path) do Node := XML.SelectSingleNode(Path[i]);
 if VarIsEmpty(Node)
  then Result := ''
  else Result := Node.attributes.getNamedItem(Item).text;
end;

function GetNodeItemTextFromID(Path:array of string;ID:integer;Item:string):string;
var
 Node: variant;
 i:integer;
begin
 Node:=XML.documentElement;
 for i:=0 to High(Path) do Node:=XML.SelectSingleNode(Path[i]);
 if VarIsEmpty(Node)
  then Result := ''
  else Result := Node.childNodes.item[ID].attributes.getNamedItem(Item).Text;
end;

function GetNodeItemTextByNameFromID(Path:array of string; NodeName:string; ID:integer; Item:string):string;
var
 Node: variant;
 i:integer;
begin
 Node:=XML.documentElement;
 for i:=0 to High(Path) do Node:=Node.SelectSingleNode(Path[i]);
 if VarIsEmpty(Node)
  then Result := ''
  else Result := Node.SelectNodes(NodeName).item[ID].attributes.getNamedItem(Item).text;
end;

function GetNodesCount(Path:array of string):integer;
var
 Node: variant;
 i:integer;
begin
 Node:=XML.documentElement;
 for i:=0 to High(Path) do Node:=Node.SelectSingleNode(Path[i]);
 if VarIsEmpty(Node)
  then Result := 0
  else Result := Node.childNodes.length;
end;

function GetNodesCountByName(Path:array of string;NodeName:string):integer;
var
 Node: variant;
 i:integer;
begin
 Node:=XML.documentElement;
 for i:=0 to High(Path) do Node:=XML.SelectSingleNode(Path[i]);
 if VarIsEmpty(Node)
  then Result := 0
  else Result:=Node.SelectNodes(NodeName).length;
end;

function GetNodeTextFromID(Path:array of string;ID:integer):string;
var
 Node: variant;
 i:integer;
begin
 Node:=XML.documentElement;
 for i:=0 to High(Path) do Node:=XML.SelectSingleNode(Path[i]);
 if VarIsEmpty(Node)
  then Result := ''
  else Result := Node.childNodes.item[ID].Text;
end;

function GetNodeTextByNameFromID(Path: array of string; NodeName: string; ID: integer): string;
var
 Node: variant;
 i:integer;
begin
 Node:=XML.documentElement;
 for i:=0 to High(Path) do Node:=XML.SelectSingleNode(Path[i]);
  if VarIsEmpty(Node)
  then Result := ''
  else begin
   Node := Node.childNodes.item[ID];
   Result := Node.SelectSingleNode(NodeName).text;
  end;
end;

end.

Автор: navodri 29.5.2010, 10:20
Ну, хотя бы намекните, в какую сторону копать?

Автор: kuzyara 29.5.2010, 14:48
так вам что надо то? 
найти что внутри тега icon находится?

если так, то проще юзать http://sergey.vilgelm.info/projects/delphi/regexp
Код

uses RegExpr;
...
with TRegExpr.Create do try
   Expression := '<icon>(.*?)</icon>';
   if Exec (Memo1.Text) then
    REPEAT
      Memo2.Lines.Add(Match [1])
    UNTIL not ExecNext;
  finally Free;
end;
...


Автор: navodri 30.5.2010, 09:35
Цитата

так вам что надо то? 
найти что внутри тега icon находится?


Именно! Но я не хочу использовать RegExp. Для меня это еще более запутанней. Думаю, модуль XMLParser, представленный выше, может вполне справится с моей задачей. Но моих знаний не хватает. Плиз, помогите!

Автор: pseud 31.5.2010, 17:56
Цитата(navodri @  30.5.2010,  09:35 Найти цитируемый пост)
Думаю, модуль XMLParser, представленный выше, может вполне справится с моей задачей.

Твой парсер с этой задачей не справиться.
Код

iDayCount := GetNodesCountByName(['//weather','//dayf'],'day');
for i := 0 to iDayCount - 1 do 
begin
  Label1.Caption := 'День - ' + GetNodeItemTextFromID(['//weather','//dayf'], i, 't')) + ', ' +  GetNodeItemTextFromID(['//weather','//dayf'], i, 'dt'));
  // здесь необходимо организовать цикл по нодам <part> внутри дня (их несколько, если ты заметил).
  // глядя на код твоего парсера, ясно, что он "вполне" с этим НЕ СПРАВИТСЯ.
end;


Добавлено через 3 минуты и 24 секунды
В Дельфи (в частности, Delphi 6) есть прекрасный парсер TXMLDocument (страница Internet), который точно справится. 
Он инкапсулирует ДомДокумент. 
Ну или можешь в лоб с ДомДокументом работать.

Автор: navodri 1.6.2010, 15:43
Пишу без визуала, поэтому TXMLDocument не подходит.
А кроме, GetNodeItemTextFromID, в модуле присутствуют и другие функцие.

Цитата

// здесь необходимо организовать цикл по нодам <part> внутри дня (их несколько, если ты заметил).


можно хотя бы ориентировочный пример?

Автор: Dom 1.6.2010, 16:19
TXMLDocument это не визуальный компонент.
А кроме того есть и бесплатные парсеры - NativeXML например. Весьма шустрый.

Автор: pseud 1.6.2010, 18:14
Цитата(navodri @  1.6.2010,  15:43 Найти цитируемый пост)
Пишу без визуала, поэтому TXMLDocument не подходит.

не вижу связи
Цитата(navodri @  1.6.2010,  15:43 Найти цитируемый пост)
А кроме, GetNodeItemTextFromID, в модуле присутствуют и другие функцие.

я изучил представленный тобой модуль, потому и написал.
Цитата(pseud @  31.5.2010,  17:56 Найти цитируемый пост)
// глядя на код твоего парсера, ясно, что он "вполне" с этим НЕ СПРАВИТСЯ.

может ты его представил не целиком.
Цитата(navodri @  1.6.2010,  15:43 Найти цитируемый пост)
можно хотя бы ориентировочный пример? 

на базе твоего парсера? не хватает функционала.

Автор: navodri 1.6.2010, 18:36
ладно. понял. тогда на базе любого функционала можете представить код, который прочитает значение <icon> в  <part p="d"> и в <part p="n"> ?

Автор: pseud 1.6.2010, 18:46
вот пример разбора твоего xml компонентом TXMLDocument.
модуль целиком рабочий (Delphi 6).
Код

unit Unit2;

interface

uses
  Classes;

procedure DoExample(AOwner: TComponent);

implementation

uses
  XMLDoc, XMLIntf;

procedure DoExample(AOwner: TComponent);
var
  i: Integer;
  doc: TXMLDocument;
  nodeRoot: IXMLNode;
  nodeDay: IXMLNode;
  nodePart: IXMLNode;
  iDay: Integer;
  iDayCount: Integer;
  iPart: Integer;
  iPartCount: Integer;
  iIcon: Integer;
  s: string;
begin
  doc := TXMLDocument.Create(AOwner);
  try
    with doc.XML do
    begin
      Add('<weather ver="2.0">');
      Add(' <dayf>');
      Add('  <day d="0" t="Friday" dt="May 28">');
      Add('   <part p="d">');
      Add('    <icon>12</icon>');
      Add('   </part>');
      Add('   <part p="n">');
      Add('    <icon>16</icon>');
      Add('   </part>');
      Add('  </day>');
      Add(' </dayf>');
      Add('</weather>');
    end;
    doc.Active := True;

    nodeRoot := doc.DocumentElement.ChildNodes.Nodes['dayf'];

    iDayCount := nodeRoot.ChildNodes.Count;
    for iDay := 0 to iDayCount - 1 do
    begin
      // действия с днем
      nodeDay := nodeRoot.ChildNodes.Nodes[iDay];
      iPartCount := nodeDay.ChildNodes.Count;
      for iPart := 0 to iPartCount - 1 do
      begin
        // действия с временем суток
        nodePart := nodeDay.ChildNodes.Nodes[iPart];
        iIcon := nodePart.ChildNodes.Nodes['icon'].NodeValue;
        // действия с номером иконки
      end;
    end;

    doc.Active := False;
  finally
    doc.Free;
  end;
end;

end.

Автор: navodri 2.6.2010, 07:35
Спасибо. Буду разбиратся.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)