Como obter o conteúdo de uma tag XML utilizando xPath - Delphi

Esse é um método alternativo ao apresentado no post http://ciranda.me/tsdn/blog-da-consultoria-tecnica-tecnospeed/post/getvaluetag-e-getvaluetagfilho-sem-depender-do-componente-tecnospeed

Em situações onde precisamos selecionar mais de uma tag com o mesmo nome, trabalhar com propriedades ou namespaces o GetValueTag acaba não suprindo essa necessidade. Para esses casos é necessário que utilizemos xPath , assim podemos navegar em qualquer XML. Esse exemplo mostra como remover o namespace do XML de consulta de uma NF-e(Nota Fiscal Eletrônica) e como localizar uma ou várias tags a partir de sua estrutura dentro do XML.

Implemente a função a baixo, essa é responsável por tratar o XML e remover seu namespace . Não se preocupe em compreender seu funcionamento.

 function TfrmPrincipal.RemoveNameSpaces(XMLString: String): String;
const
  // An XSLT script for removing the namespaces from any document. It will remove the prefix as well.
  // From   http://wiki.tei-c.org/index.php/Remove-Namespaces.xsl  
  cRemoveNSTransform =
    '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">'
    + '<xsl:output method="xml" indent="no"/>' +

    '<xsl:template match="/|comment()|processing-instruction()">' +
    '    <xsl:copy>' + '      <xsl:apply-templates/>' + '    </xsl:copy>' +
    '</xsl:template>' +

    '<xsl:template match="*">' + '    <xsl:element name="{local-name()}">' +
    '      <xsl:apply-templates select="@*|node()"/>' + '    </xsl:element>' +
    '</xsl:template>' +

    '<xsl:template match="@*">' + '    <xsl:attribute name="{local-name()}">' +
    '      <xsl:value-of select="."/>' + '    </xsl:attribute>' +
    '</xsl:template>' +

    '</xsl:stylesheet>';

var
  Doc, XSL: IXMLDOMDocument2;
  Res: string;
  p: integer;
begin
  Doc := CoDOMDocument60.Create;
  Doc.ASync := false;
  XSL := CoDOMDocument60.Create;
  XSL.ASync := false;
  try
    Doc.loadXML(XMLString);
    XSL.loadXML(cRemoveNSTransform);
    Res := Doc.TransFormNode(XSL);
    // This now contains the original text with a <?xml version="1.0" encoding="UTF-16"?> prepended; remove it:
    p := Pos('?>', Res);
    result := Copy(Res, p + 2, Length(Res));
  except
    on E: Exception do
      result := E.Message;
  end;
end; 

É necessário implementar uma Procedure que chamaremos de ConfiguraObjeto nela iremos criar o CoDomDocument e então carregar o XML que será tratado através do método loadXML .

 procedure TfrmPrincipal.ConfiguraObjeto(XML: widestring);
begin
  try
    lXMLDoc := CoDOMDocument60.Create;
    lXMLDoc.setProperty('SelectionLanguage', 'XPath');
    if XML <> '' then
      lXMLDoc.loadXML(RemoveNameSpaces(XML))
    else
      raise Exception.Create('XML não informado!');
  except
    on E: Exception do
      Exception.Create(E.Message);
  end;
end; 

Para realizar a leitura do conteúdo das tags podemos posicionar ou percorrer, utilizando a lista de Nodes . Essa lista será gerada a partir da estrutura do XML ou hierarquia, como preferir. A forma mais fácil de visualizar essa estrutura é indentando o XML, no exemplo anexado utilizei a seguinte estrutura “//nfeRetAutorizacaoResult/retConsReciNFe/protNFe/infProt/xMotivo” , com base no XML abaixo:

 <nfeRetAutorizacaoResult xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeRetAutorizacao3">
    <retConsReciNFe versao="3.10" xmlns="http://www.portalfiscal.inf.br/nfe">
        <tpAmb>2</tpAmb>
        <verAplic>PR-v3_2_2</verAplic>
        <nRec>411110211911421</nRec>
        <cStat>104</cStat>
        <xMotivo>Lote processado</xMotivo>
        <cUF>41</cUF>
        <dhRecbto>2014-11-04T10:00:28-02:00</dhRecbto>
        <protNFe versao="3.10">
            <infProt>
                <tpAmb>2</tpAmb>
                <verAplic>PR-v3_2_2</verAplic>
                <chNFe>41141108187168000160558112855494631864800349</chNFe>
                <dhRecbto>2014-11-04T10:00:25-02:00</dhRecbto>
                <nProt>141140001675586</nProt>
                <digVal>Fj+0fLuRlampv0t0KeS85qRWUjc=</digVal>
                <cStat>100</cStat>
                <xMotivo>Lote processado</xMotivo>
                <xMotivo>Autorizado o uso da NF-e</xMotivo>
            </infProt>
        </protNFe>
    </retConsReciNFe>
</nfeRetAutorizacaoResult> 

Na implementação é necessário chamar a Procedure ConfiguraObjeto passando o XML que deseja ler, em seguida utilize o método selectNodes para selecionar a tag a partir da estrutura desejada, logo após podemos obter seu conteúdo posicionando ou percorrendo o lNodeList .

 procedure TfrmPrincipal.btnCapturarClick(Sender: TObject);
var i: integer;
begin
  ConfiguraObjeto(mmXML.Text);

  lNodeList := lXMLDoc.selectNodes(TRIM(edtEstrutura.Text));
  if (lNodeList.length > 0 ) then
  begin
    //Exemplo de leitura 01 - percorre
    for i := 0 to lNodeList.length - 1 do
    begin
      lNode := lNodeList.item[i];
      mmRetorno.Lines.Add(lNode.text);
    end;

    //Exemplo de leitura 02 - posiciona
    lNode := lNodeList.item[0];
    edtRetorno.Text := lNode.text;
  end
  else
    raise Exception.Create('Estrutura não encontrada no XML');
end; 

Exemplo de xPath em Delphi anexado logo abaixo.
https://tsdn.tecnospeed.com.br/files/render/a/Tm_SajCIDP4/m/2Aal0HUq8WfTgM9w0oBgnbfLjiGHDqSDEWdrYvM8FZz0W6fPg6Y9Sd4pl53VxQ_St3k6J1xnf4g