访问也用于文件中其他区域的特定节点

时间:2014-07-10 08:39:58

标签: php xml

如果我的XML文件中包含以下数据;

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CstmrDrctDbtInitn>
<PmtInf>
      <PmtInfId>5n7gfUaPGK</PmtInfId>
      <PmtMtd>DD</PmtMtd>
      <NbOfTxs>1</NbOfTxs>
      <CtrlSum>200.2</CtrlSum>
      <PmtTpInf>
        <SvcLvl>
          <Cd>SEPA</Cd>
        </SvcLvl>
        <LclInstrm>
          <Cd>CORE</Cd>
        </LclInstrm>
        <SeqTp>RCUR</SeqTp>
      </PmtTpInf>
      <DrctDbtTxInf>
      <PmtId>
          <EndToEndId>nmu5AOhE7G</EndToEndId>
      </PmtId>
      </DrctDbtTxInf>
 </PmtInf>
 <PmtInf>
      <PmtInfId>5jAcoNoId3</PmtInfId>
      <PmtMtd>DD</PmtMtd>
      <NbOfTxs>3</NbOfTxs>
      <CtrlSum>100.5</CtrlSum>
      <PmtTpInf>
        <SvcLvl>
          <Cd>SEPA</Cd>
        </SvcLvl>
        <LclInstrm>
          <Cd>CORE</Cd>
        </LclInstrm>
        <SeqTp>FRST</SeqTp>
      </PmtTpInf>
      <DrctDbtTxInf>
          <PmtId>
              <EndToEndId>nmu5AbdfG</EndToEndId>
          </PmtId>
      </DrctDbtTxInf>
      <DrctDbtTxInf>
          <PmtId>
              <EndToEndId>nmu5A3r5jgG</EndToEndId>
          </PmtId>
      </DrctDbtTxInf>
</PmtInf>
</CstmrDrctDbtInitn>
</Document>

我如何访问第二个<NbOfTxs>块(值为3)中的<PmtInf>而不是<NbOfTxs>块中的<PmtInf>(其中值为1) )?

如果我只使用以下代码行;

$FRSTTransaction = $xml->getElementsByTagName('NbOfTxs')->nodeValue;

我不知道我试图访问哪个<NbOfTxs>

每个付款清单之间的唯一区别是<SeqTp>。总共将有4个支付块。

我正在尝试计算每个付款清单中<DrctDbtTxInf>个版块的数量,然后将此值放入<NbOfTxs>

      <PmtId>
          <EndToEndId>nmu5AOhE7G</EndToEndId>
      </PmtId>
      </DrctDbtTxInf>

我尝试的代码如下;

$filename = date('Y-W').'.xml'; //2014-26.xml
$xml = new DOMDocument;
$xml->load($filename);
$NumberTransactions = 0;
$RCURTransaction = $xml->getElementsByTagName('DrctDbtTxInf');
$NodeValue = $xml->getElementsByTagName('NbOfTxs')->nodeValue;
foreach ($RCURTransaction as $Transaction) {
$NodeValue = $NodeValue + 1;
}

$Document = simplexml_load_file($filename);
$Document->CstmrDrctDbtInitn->PmtInf->NbOfTxs = $NodeValue; 
$Document->asXML($filename);

我没有收到任何错误,它似乎无法访问节点值。

1 个答案:

答案 0 :(得分:1)

Read the manualDOMDocument::getElementsByTagName不返回节点,它返回DOMNodeList类的实例。这个类实现了Traversable接口(这意味着你可以foreach),并且拥有自己的item 一个方法(再次参见{{3} }})。

您正在尝试访问认为nodeValue实例的DOMNode属性,但实际上是DOMNodeList实例。如您所见the manual,没有nodeValue属性可用。而是从此列表中获取特定节点,然后获取节点值:

$nodes = $xml->getElementsByTagName('NbOfTxs');
foreach ($nodes as $node)
    echo $node->nodeValue, PHP_EOL;//first, second, third node

或者,如果您愿意,例如,只能看到此节点第三次出现的值:

if ($nodes->length > 2)//zero-indexed!
    echo $nodes->item(2)->nodeValue, PHP_EOL;
else
    echo 'Error, only ', $nodes->length, ' occurrences of that node found', PHP_EOL;

通常,底线确实是RTM。 DOMDocument::getElementsByTagName的文档清楚地显示了给定方法的返回类型。如果它是特定类的实例,则可以在PHP网站上单击该返回类型,并将您链接到该类的手册页。导航API并不比那更简单,恕我直言:

//from the docs
public DOMNodeList DOMDocument::getElementsByTagName ( string $name )
       //          class::methodName                  arguments + expected type
       |-> return type, links to docs for this class

更新
解决你在更新的问题中提到的事情:

  • 如何计算节点的特定子节点

我假设每个PmtInf都是付款广告,但我认为所有SeqTp标记都是PmtTpInf标记的子标记。由于我们正在使用DOMNodeList,其中包含DOMNode个实例。 in the manual是第一件事。正如您所看到的,每个DOMNode都有许多方便的属性和方法:$childNodes$nodeName$parentNode是我们将在此处使用的内容。

$payments = $xml->getElementsByTagName('PmtTpInf');//don't get PmtInf, we can access that through `PmtTpInf` nodes' parentNode property
$idx = -1;
$counts = array();
$parents = array();
foreach ($payments as $payment)
{
    if (!$parents || $parents[$idx] !== $payment->parentNode)
    {//current $payment is not a child of last processed payment block
        $parents[++$idx] = $payment->parentNode;//add reference to new payment block
        $count[$idx] = 0;//set count to 0
    }
    foreach ($payment->childNodes as $child)
    {
        if ($child->nodeName === 'SeqTp')
            ++$counts[$idx];//add 1 to count
    }
}

好的,现在我们有2个数组$parents,其中包含每个付款块,$counts,其中包含该付款清单中所有SeqTp个模块的总数。让我们着手添加/更新这些数据:

foreach ($parents as $idx => $block)
{//iterate over the payment blocks
    $nbNode = null;//no node found yet
    for ($i=0;$i<$block->childNodes->length;++$i)
    {
        if ($block->childNodes->item($i)->nodeName === 'NbOfTxs')
        {
            $nbNode = $block->childNodes->item($i);
            break;//found the node, stop here
        }
    }
    if ($nbNode === null)
    {//NbOfTxs tag does not exist yet
        $nbNode = $xml->createElement('NbOfTxs', 0);//create new node
        $block->appendChild($nbNode);//add as child of the payment-block node
    }
    $nbNode->nodeValue = $counts[$idx];//set value using the counts array we constructed above
}

最后,要保存此更新的XML dom:

$xml->save($filename);

全部,根本不需要simplexml_load_file,因为它解析了DOMDocument已为您做过的XML DOM。