How to select XML tag based on value of its child

2020-07-28 11:41发布

问题:

I'm trying to search for a text "LIBRA ESTERLINA" in a child element of XML element and retrieve values of others child elements. But my query yields nothing.

My code is based on response from How to read multiple XML nodes? (Inno Setup) and response from XPath: How to select elements based on their value?

function LoadValuesFromXMLMoneda(FileName: string): Boolean;
var
  XMLNode: Variant;
  XMLNodeList: Variant;
  XMLDocument: Variant;  
  Index: Integer;
  id, moneda, dollar, abr, singPlur, caracter : String;
begin
  XMLDocument := CreateOleObject('Msxml2.DOMDocument.6.0');
  try
    XMLDocument.async := False;
    XMLDocument.load(FileName);
    if (XMLDocument.parseError.errorCode <> 0) then
    begin
      Log('The XML file could not be parsed. ' + XMLDocument.parseError.reason);
      Result := False;
    end
      else
    begin
      XMLDocument.setProperty('SelectionLanguage', 'XPath');
      XMLNodeList :=
        XMLDocument.SelectNodes('//listaMonedas/item/moneda[text()="LIBRA ESTERLINA"]');
     for Index := 0 to XMLNodeList.length - 1 do
      begin
        XMLNode := XMLNodeList.item[Index];

        id       :=  XMLNode.SelectSingleNode('id').Text;
        moneda   :=  XMLNode.SelectSingleNode('moneda').Text;
        dollar   :=  XMLNode.SelectSingleNode('dollar').Text;
        abr      :=  XMLNode.SelectSingleNode('abr').Text;
        singPlur :=  XMLNode.SelectSingleNode('singPlur').Text;
        caracter :=  XMLNode.SelectSingleNode('caracter').Text;
        MsgBox(id+moneda+dollar+abr+singPlur+caracter,mbInformation,MB_OK); 
      end;
      Result := True;
    end;
  except
    Log('An error occured!' + #13#10 + GetExceptionMessage);
    Result := False;
  end;
end;

Here is XML:

<ns1:obtenerMonedasResponse 
    xmlns:ns1="urn:v1.001" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:tns="http://www.example.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <listaMonedas xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:WSMoneda[21]">
    <item xsi:type="tns:WSMoneda">
        <id xsi:type="xsd:integer">1</id>
        <moneda xsi:type="xsd:string">LIBRA ESTERLINA</moneda>
        <dollar xsi:type="xsd:string">0.7767</dollar>
        <abr xsi:type="xsd:string">GBP</abr>
        <singPlur xsi:type="xsd:string">LIBRA:LIBRAS</singPlur>
        <caracter xsi:type="xsd:string">£</caracter>
        <flag16 xsi:type="xsd:string">Base64Img</flag16> 
        <flag24 xsi:type="xsd:string">Base64Img</flag24> 
        <flag32 xsi:type="xsd:string">Base64Img</flag32>   
     </item>
     <item xsi:type="tns:WSMoneda">
        <id xsi:type="xsd:integer">1</id>
        <moneda xsi:type="xsd:string">PESO MEXICANO</moneda>
        <dollar xsi:type="xsd:string">18.7000</dollar>
        <abr xsi:type="xsd:string">MXN</abr>
        <singPlur xsi:type="xsd:string">PESO:PESOS</singPlur>
        <caracter xsi:type="xsd:string">$</caracter>
        <flag16 xsi:type="xsd:string">Base64Img</flag16> 
        <flag24 xsi:type="xsd:string">Base64Img</flag24> 
        <flag32 xsi:type="xsd:string">Base64Img</flag32>   
     </item> 
  </listaMonedas>
</ns1:obtenerMonedasResponse>

回答1:

Remember, that I've suggested you this question as a closer match to your needs:
XPath to select element based on childs child value.

So, your XPath should be:

XMLNodeList :=
  XMLDocument.SelectNodes('//listaMonedas/item[moneda/text()="LIBRA ESTERLINA"]');

Meaning, that you want to select the item tag, that contains moneda child tag with LIBRA ESTERLINA text.


Also as you expect a single match only (don't you?), you should use SelectSingleNode and your code will be much simpler:

XMLNode :=
  XMLDocument.SelectSingleNode('//listaMonedas/item[moneda/text()="LIBRA ESTERLINA"]');

id       :=  XMLNode.SelectSingleNode('id').Text;
moneda   :=  XMLNode.SelectSingleNode('moneda').Text;
dollar   :=  XMLNode.SelectSingleNode('dollar').Text;
abr      :=  XMLNode.SelectSingleNode('abr').Text;
singPlur :=  XMLNode.SelectSingleNode('singPlur').Text;
caracter :=  XMLNode.SelectSingleNode('caracter').Text;

MsgBox(id+moneda+dollar+abr+singPlur+caracter, mbInformation, MB_OK);