【问题标题】:Delphi7 - OmniXML - select specific node with multiple parametersDelphi7 - OmniXML - 选择具有多个参数的特定节点
【发布时间】:2016-02-02 14:37:13
【问题描述】:

XML 文件:

<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>

在我的 XML 文件中,我有重复节点 &lt;Partner&gt;。每个合作伙伴都有自己的标识号写入节点&lt;K3&gt;

每个合作伙伴都可以拥有多张发票。

我需要在发票中的 &lt;R6&gt;&lt;R7&gt; 中查找和读取值,其中 我知道&lt;R2&gt;&lt;R3&gt;&lt;R8&gt; 的值。

在搜索条件为字段&lt;K3&gt; 的合作伙伴的多个字段&lt;R2&gt;&lt;R3&gt;&lt;R8&gt; 的情况下,我如何搜索特定发票并获取&lt;R6&gt;&lt;R7&gt; 的字段值?

如何给SelectSingleNode添加多条件?

我的代码:

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;

【问题讨论】:

  • IIRC,这行得通:'.//Racuni/Racun[R2=' + '4-02R0113-12' + ']' + ' [R3=' + '2014-12-29' + ']' + '[R8=' + '802.39' + ']'
  • 我刚刚试了一下。对我来说它不起作用。节点为零。

标签: delphi omnixml


【解决方案1】:

OmniXMLXPath 不支持开箱即用。但是,我去年添加了对连续过滤器的支持,但忘记将更改推送到公共站点:(

使用更新的版本,这将起作用:

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.

现在,您可以在这里获得新鲜的OmniXMLXPath.pashttps://www.dropbox.com/s/nnvrz6wnmnpmxzn/OmniXMLXPath.pas

【讨论】:

    【解决方案2】:

    鉴于上面的示例,您可以按照以下方式进行操作:

    //Partner[K2=3]/Invoices/Invoice[R1=1 and R5=398]
    

    这将获得 K2=3 的合作伙伴的语音,其中语音具有 r1 和 r5 的特定值。然后,根据您需要如何处理符合这些条件的发票(例如,如果有多个发票),您可以使用 XPath 表达式(如 sum 或其他)返回从这些 Invoice 元素计算的单个值。

    【讨论】:

    • 写了这个 iNodepartner := FXMLDocument.SelectSingleNode('//Partner[K3=0254304]/Invoices/Invoice[R2=4-02R0113-12 and R8=802.39]');节点为 nill,所以仍然无法工作。
    • 我认为您需要在 xpath 查询中包含 R2 值的引号(通常适用于所有字符串值)。我试过了,它在问题中的示例 XML 上运行良好(即它找到了发票): //Partner[K3=5254304]/Invoices/Invoice[R2='4-02R0113-12' and R8=802.39] I在这里测试:freeformatter.com/xpath-tester.html#ad-output
    • OmniXML 不支持“和”。
    【解决方案3】:

    根据 OmniXMLXPath.pas 文件,OmniXML 不支持 XPath 中的逻辑操作。所以你不能同时搜索 2+ 属性。

    您可以通过调用 SelectNodes('//Partner[K3=5254304]/Invoices/Invoice[R2='4-02R0113-12']) 选择多个节点并通过其他属性检查节点.

    如果可以的话,或者使用 MSXML 解析器(OmniXML_MSXML 单元)。这将适用于您的演示数据:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      xml: IXMLDocument;
      iNodePartner: IXMLNode;
    begin
      xml := CreateXMLDoc;
      xml.Load('c:\test.xml');
      iNodePartner := XML.selectSingleNode('//Partner[K3=5254304]/Invoices/Invoice[R2=''4-02R0113-12'' and R8=''802.39'']');
      if iNodePartner = nil then
        MessageDlg('Not found',mterror,[mbok],0);
    end;
    

    【讨论】:

    • 谢谢,但这不起作用 iNodePartner := XML.selectSingleNode('//Partner[K3=5254304]/Invoices/Invoice[R2=''4-02R0113-12'' 和 R8= ''802.39'']');如果我像这个节点一样放置它确实存在 iNodePartner := XML.selectSingleNode('//Partner[K3=5254304]/Invoices/Invoice[R2=''4-02R0113-12'']');但是一旦您添加另一个查询,它就不起作用 iNodePartner := XML.selectSingleNode('//Partner[K3=5254304]/Invoices/Invoice[R2=''4-02R0113-12'' and R8='' 802.39'']');
    • 什么不行?上面的代码在您的示例 xml 数据上进行了验证。
    • @joskona,我的示例代码仅适用于 MSXML 解析器,您必须将 uses 子句中的 OmniXML 单元替换为 OmniXML_MSXML 单元! OmniXML 本身不支持 XPath 中的“AND”运算符。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多