带命名空间的 SimpleXML 访问节点和不带命名空间的子节点

SimpleXML access nodes with namespace and subnodes without namespace

我正在尝试访问具有命名空间声明的节点中没有命名空间声明的节点列表。我的 XML 文件有一个带命名空间 ehd 的主节点,在同一命名空间内有两个子节点 header、body。但是,body 节点中的所有子节点都没有进一步的命名空间声明。我正在努力使用 SimpleXML.

访问这些节点

摘自 xml 文件:

<?xml version="1.0" encoding="ISO-8859-15"?>
<ehd:ehd ehd_version="1.40" xmlns:ehd="urn:ehd/001" xmlns="urn:ehd/go/001">
    <ehd:header>
    </ehd:header>
    <ehd:body>
        <gnr_liste>
            <gnr V="01100"></gnr>
            <gnr V="01101"></gnr>
            <gnr V="01102"></gnr>
        </gnr_liste>
</ehd:body>
</ehd:ehd>

我的代码如下:

$xml = simplexml_load_file($file) or die("Failed to load");   
    $ehd = $xml->children('ehd', true)->body;
    simplexml_dump($ehd);
    $gnr_liste = $ehd->children('gnr_liste')->children('gnr');
    simplexml_dump($gnr_liste);

输出为:

SimpleXML object (1 item)
[
    Element {
        Namespace: 'urn:ehd/001'
        Namespace Alias: 'ehd'
        Name: 'ehd'
        String Content: ''
        Content in Namespace ehd
            Namespace URI: 'urn:ehd/001'
            Children: 2 - 1 'body', 1 'header'
            Attributes: 0
        Content in Default Namespace
            Children: 0
            Attributes: 1 - 'ehd_version'
    }
]
SimpleXML object (1 item)
[
    Element {
        Namespace: 'urn:ehd/001'
        Namespace Alias: 'ehd'
        Name: 'body'
        String Content: ''
        Content in Default Namespace
            Namespace URI: 'urn:ehd/go/001'
            Children: 1 - 1 'gnr_liste'
            Attributes: 0
    }
]

如何从 gnr_liste 节点访问所有 gnr 项?

注意:我正在使用 simplexml_dump 进行调试

就我个人而言,我发现 DomDocument much more intuitive to work with – once you get over the barrier of XPath syntax。不管您使用什么工具,命名空间都会让一切变得更加困难!

$xml = <<< XML
<?xml version="1.0" encoding="ISO-8859-15"?>
<ehd:ehd ehd_version="1.40" xmlns:ehd="urn:ehd/001" xmlns="urn:ehd/go/001">
    <ehd:header>
    </ehd:header>
    <ehd:body>
        <gnr_liste>
            <gnr V="01100"></gnr>
            <gnr V="01101"></gnr>
            <gnr V="01102"></gnr>
        </gnr_liste>
</ehd:body>
</ehd:ehd>
XML;

$dom = new DomDocument;
$dom->loadXML($xml);
$xp = new DomXPath($dom);
// need to get tricky due to namespaces 
$nodes = $xp->query("//*[local-name()='gnr']/@V");
foreach ($nodes as $node) {
    printf("%s\n", $node->value);
}

输出:

01100
01101
01102

->children() 的参数始终是名称空间标识符或本地前缀,而不是标签名称。如果这些元素在 "no namespace" 中,您将使用 ->children('').

访问它们

但是,本文档中没有 prefix 的元素并不没有 namespace - 它们在默认命名空间中,在此案例 urn:ehd/go/001(由 xmlns="urn:ehd/go/001" 定义)。

如果您使用完整的命名空间标识符而不是前缀(如果提要发生变化,前缀也不太可能中断),您应该能够轻松访问这些:

$xml = simplexml_load_file($file) or die("Failed to load");   
$ehd = $xml->children('urn:ehd/001')->body;
$gnr_liste = $ehd->children('urn:ehd/go/001')->gnr_liste;
foreach ( $gnr_liste->gnr as $gnr ) {
    simplexml_dump($gnr);
}

您可能希望为命名空间指定您自己的名称,这样您就不必使用完整的 URI,但不依赖于 XML 生成的前缀;一种常见的方法是定义常量:

const XMLNS_EHD_MAIN = 'urn:ehd/001';
const XMLNS_EHD_GNR = 'urn:ehd/go/001';

$xml = simplexml_load_file($file) or die("Failed to load");   
$ehd = $xml->children(XMLNS_EHD_MAIN)->body;
$gnr_liste = $ehd->children(XMLNS_EHD_GNR)->gnr_liste;
foreach ( $gnr_liste->gnr as $gnr ) {
    simplexml_dump($gnr);
}