Search code examples
delphiomnixml

Delphi7 - OmniXML - select specific node with multiple parameters


XML file:

<Partner>
...
</Partner>
<Partner>
  <K1>10</K1>
  <K2>3</K2>
  <K3>5254304</K3>
  <K4>test name</K4>
  <K5>637.51</K5>
  <K6>159.38</K6>
  <K7>802.39</K7>
  <K8>0.00</K8>
  <K9>802.39</K9>
  <Invoices>
    <Invoice>
      <R1>1</R1>
      <R2>4-02R0113-12</R2>
      <R3>2014-12-29</R3>
      <R4>2014-12-29</R4>
      <R5>398</R5>
      <R6>637.51</R6>
      <R7>159.38</R7>
      <R8>802.39</R8>
      <R9>0.00</R9>
      <R10>802.39</R10>
    </Invoice>
  </Invoices>
</Partner>
<Partner>
...
</Partner>

In my XML file I have repeating nodes <Partner>. Every partner has its own identification number written in node <K3>.

Every partner can have multiple invoices.

I need to find and read values in <R6> and <R7> in the invoice where I know values for <R2>, <R3>, <R8>.

How do I search for specific Invoice where search criteria are multiple fields <R2>, <R3>, <R8> of partner where search criteria is field <K3> and get field values for <R6> and <R7>?

How to add multi criteria to SelectSingleNode?

My code:

procedure TfrmTest.TestReadOmniXML;
var
  xml: IXMLDocument;
  iNodePartner, iNodePartnerInvoice, iNodePartnerInvoiceR6, iNodePartnerInvoiceR7 : IXMLNode;
begin
  xml := CreateXMLDoc;
  xml.Load('c:\test.xml');
  iNodePartner := XML.SelectSingleNode('//Partner[K3=' + '0254304' + ']');
  iNodePartnerInvoice := iNodePartner.SelectSingleNode(
  //single query works OK
  './/Racuni/Racun[R2=' + '4-02R0113-12' + ']' 

  //but I need to add these fields also
//    ' and [R3=' + '2014-12-29' + ']' +
//    ' and [R8=' + '802.39' + ']'     
  );

  if Assigned( iNodePartnerInvoice ) then
  begin
    iNodePartnerInvoiceR6 := iNodePartnerInvoice.SelectSingleNode('./R6');
    Label1.Caption := iNodePartnerInvoiceR6.Text;
    iNodePartnerInvoiceR7 := iNodePartnerInvoice.SelectSingleNode('./R7');
    Label2.Caption := iNodePartnerInvoiceR7.Text;
  end;

...

end;

Solution

  • OmniXMLXPath doesn't support that out of the box. I have, however, added support for consecutive filters last year, but forgot to push that change to the public site :(

    With the updated version, this will work:

    program Project29;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils,
      OmniXML,
      OmniXMLXpath,
      OmniXMLUtils;
    
    var
      xml: IXMLDocument;
      node: IXMLNode;
    
    begin
      xml := CreateXMLDoc;
      if XMLLoadFromFile(xml, 'c:\0\partner.xml') then begin
        node := xml.SelectSingleNode('//Partner/Invoices/Invoice[R2="4-02R0113-12"][R3="2014-12-29"][R8="802.39"]');
        if not assigned(node) then
          Writeln('not found')
        else begin
          Writeln('R6=', GetNodeTextStr(node, 'R6', ''));
          Writeln('R7=', GetNodeTextStr(node, 'R7', ''));
        end;
      end;
      Readln;
    end.
    

    For now, you can get the fresh OmniXMLXPath.pas here: https://www.dropbox.com/s/nnvrz6wnmnpmxzn/OmniXMLXPath.pas