PHP xpath在foreach循环中按属性获取元素


PHP xpath get elements by attribute in foreach loop

我试图循环所有的LINE_ITEMS,它运行良好,但在foreach循环中,我试图通过使用xpath按属性访问单个LINE_ITEM,但我没有得到任何结果。有人知道这个问题吗?

 foreach($items = $xmlData->xpath('//zw:LINE_ITEM') as $item) {
    $item->xpath('//namespace:PID[@type="erp_pid"]'); No result
}
$xmlData->xpath('//zw:LINE_ITEM') 

工作正常,我得到了所有的LINE_ITEMS,但是当我尝试对该项进行xpath时,没有得到任何结果。

如何访问PID值,例如"erp_PID"?

<?xml version="1.0" encoding="UTF-8"?>
<NM_DOCS>
    <NM_DOC>
        <DOCUMENT qualifier="default" role="original" test="false" type="orders">
            <VERSION>4.0</VERSION>
            <HEADER>
                <CONTROL_INFO>
                    <LAST_SAVE_DATE>2015-03-18T13:44:32+01:00</LAST_SAVE_DATE>
                    <PROCESS_TYPE>silent</PROCESS_TYPE>
                    <SOURCE>sales</SOURCE>
                </CONTROL_INFO>
                <SOURCING_INFO>
                    <REFERENCES>
                        <REFERENCE type="order_nexmart">
                            <ID>109546063</ID>
                            <DATES>
                                <DATE type="order">2015-03-18T13:44:30+01:00</DATE>
                            </DATES>
                        </REFERENCE>
                    </REFERENCES>
                </SOURCING_INFO>
                <DOCUMENT_INFO>
                    <DOCUMENT_ID>Test bestellnummer</DOCUMENT_ID>
                    <DATES>
                        <DATE type="delivery_ordered">2015-04-19T12:41:41+02:00</DATE>
                    </DATES>
                    <PARTIES>
                        <PARTY type="buyer">
                            <BUSINESS_ROLE>commercial</BUSINESS_ROLE>
                            <PORTAL_ID>BDE600028</PORTAL_ID>
                            <ADDITIONAL_IDS>                             
                            </ADDITIONAL_IDS>
                            <ADDRESS>                                
                            </ADDRESS>
                            <CONTACT_DETAILS>                                
                                <ACCOUNTS>
                                    <ID type="emart">sales.nexmart</ID>
                                </ACCOUNTS>
                            </CONTACT_DETAILS>
                        </PARTY>
                        <PARTY type="supplier">
                            <BUSINESS_ROLE>commercial</BUSINESS_ROLE>
                            <PORTAL_ID>zweygart_app_de</PORTAL_ID>
                            <ADDITIONAL_IDS>
                            </ADDITIONAL_IDS>
                            <ADDRESS>
                            </ADDRESS>
                            <CONTACT_DETAILS>
                            </CONTACT_DETAILS>
                        </PARTY>
                        <PARTY type="delivery">
                            <BUSINESS_ROLE>commercial</BUSINESS_ROLE>
                            <ADDRESS>
                            </ADDRESS>
                            <CONTACT_DETAILS>
                            </CONTACT_DETAILS>
                        </PARTY>
                        <PARTY type="invoice_recipient">
                            <ADDRESS>
                            </ADDRESS>
                            <CONTACT_DETAILS>
                            </CONTACT_DETAILS>
                        </PARTY>
                    </PARTIES>                    
                    <REMARKS>
                        <REMARK type="order">Test bemerkung</REMARK>
                    </REMARKS>
                </DOCUMENT_INFO>
            </HEADER>
            <LINE_ITEMS>
                <LINE_ITEM>                    
                    <PRODUCT_ID>
                        <SUPPLIER_PID>119556</SUPPLIER_PID>
                        <GTIN>4030646269130</GTIN>
                        <ADDITIONAL_PIDS>
                            <PID type="supplier_pid_original">119556</PID>
                            <PID type="gtin_original">4030646269130</PID>
                            <PID type="erp_pid">119556</PID>
                        </ADDITIONAL_PIDS>
                        <DESCRIPTIONS>
                            <DESCR type="short">short desc</DESCR>
                            <DESCR type="short_original">some text</DESCR>
                        </DESCRIPTIONS>
                    </PRODUCT_ID>                    
                </LINE_ITEM>
                <LINE_ITEM>                    
                    <PRODUCT_ID>
                        <SUPPLIER_PID>123456789</SUPPLIER_PID>
                        <GTIN>123456789</GTIN>
                        <ADDITIONAL_PIDS>
                            <PID type="supplier_pid_original">123456</PID>
                            <PID type="gtin_original">123456</PID>
                            <PID type="erp_pid">123456</PID>
                        </ADDITIONAL_PIDS>
                        <DESCRIPTIONS>
                            <DESCR type="short">short desc</DESCR>
                            <DESCR type="short_original">Some description</DESCR>
                        </DESCRIPTIONS>
                    </PRODUCT_ID>                                       
                </LINE_ITEM>
            </LINE_ITEMS>
        </DOCUMENT>
    </NM_DOC>
</NM_DOCS>

问题不在于XPath,而在于SimpleXML。SimpleXMLElement::xpath()是有限的。它将结果转换为SimpleXMLElement对象的数组,但这里还有DOM中的其他节点。更重要的是,您必须再次在每个新的SimpleXMLElement上注册名称空间。

$element = new SimpleXMLElement($xml);
$element->registerXPathNamespace('namespace', 'urn:foo');
foreach($element->xpath('//namespace:LINE_ITEMS/namespace:LINE_ITEM') as $item) {
  $item->registerXPathNamespace('namespace', 'urn:foo');
  var_dump((string)$item->xpath('.//namespace:PID[@type="erp_pid"]')[0]);
}

输出:

string(6) "119556"
string(6) "123456"

您可能会注意到,我在您的详细信息表达式前面加了一个.。表达式开头的斜杠总是使其相对于文档本身,而不是当前节点。.表示当前节点。

如果直接使用DOM,则创建一个单独的DOMXPath对象并在该对象上注册命名空间。此外,您还可以使用返回标量值的XPath表达式。

$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('namespace', 'urn:foo');
foreach($xpath->evaluate('//namespace:LINE_ITEMS/namespace:LINE_ITEM') as $node) {
  var_dump($xpath->evaluate('string(.//namespace:PID[@type="erp_pid"])', $node));
}

这对我有效:

foreach($xmlData->LINE_ITEM as $item) {
    $erp = ( $item->xpath('//PID[@type="erp_pid"]'));
    foreach($erp as $v) {
        echo $v. " / ";
    }
}

只要删除xpath中的namespace,xml就不会使用名称空间。

如果您想遍历xml的一部分,请确保使用正确的路径。