如何通过PHP XMLReader将相同的子注释放入数组中

时间:2016-04-05 12:22:01

标签: php xml xmlreader

<item>
    <name>item2</name>
    <platform>
        <platformType>Android</platformType>
        <location>url_a</location>
    </platform>
    <platform>
        <platformType>iOS</platformType>
        <location>url_b</location>
    </platform>
    <platform>
        <platformType>Windows</platformType>
        <location>url_c</location>
    </platform>
</item>

这是我的XML代码。 如何将每个项目的platformType放入数组中 比如

Array
(
    [0] => Array
        (

            [platformID] => Android, iOS
        )
    [1] => Array
        (

            [platformID] => Android, iOS, Windows
        )
)

1 个答案:

答案 0 :(得分:0)

您想要使用最复杂的XML php parsers之一。

XMLReader是XML Pull parser:与Document Object Model不同,XMLReader不会立即将整个文档加载到内存中,而是按顺序读取文件。这意味着使用它您无法直接找到特定节点,但您必须先读取XML文件,直到找到所需的节点。

当你必须解析非常大的XML文件时,这是必须的选择,但即使在这些情况下,如果与DOM解析器一起使用也会更加舒适,因为XMLReader在开发方面非常复杂。

在下面的示例中,我将向您展示如何使用纯XMLReader获取阵列,与另外两个漫反射解析器相比:SimpleXMLDOMDocument。 SimpleXML可能是最常用的,它被认为是DOMDocument最高效的。相反,DOMDocument更强大,可定制。 SimpleXML和DOMDocument都支持(有限的)XPath查询系统,允许您简化XML内部的导航。

Here您可以找到几乎所有可用的XML / HTML解析器的详细比较。

请仔细注意:

Stack Overflow不是免费的复制粘贴代码服务。以下示例旨在说明不同的解析器行为。我已根据您的示例使用this XML sample测试了每个示例,但可能您的文档结构更复杂,因此您必须使代码适应它。如果在不同的树位置具有相同的节点名称,则尤其如此。

在以下所有示例中,$result最终数组是:

Array
(
    [0] => Android
    [1] => Android, iOS, Windows
)

使用XMLReader:

$xml = new XMLReader();             # Init XMLReader
$xml->open( "file://$filePath" );   # Open XML File

$result = array();                  # Init result array

while( $xml->read() )               # Main read loop
{
    /* If current node is item, start analyzing it: */
    if( $xml->name == 'item' )
    {
        /* Create additional XMLReader for item node: */
        $node = new XMLReader();
        /* Load item XML: */
        $node->xml( $xml->readOuterXML() );
        /* Init $platforms array: */
        $platforms = array();
        while( $node->read() )
        {
            /* Continue reading until platformType if found: */
            while( $node->read() && $node->name !== 'platformType' );
            /* Add platformType value to $platforms array: */
            if( $node->readInnerXML() ) $platforms[] = $node->readInnerXML();
            /* Continue reading until platformType closing tag if found: */
            while( $node->read() && $node->name !== 'platformType' );
        }
        /* Add imploded $platforms to $result: */
        $result[] = implode( ', ', $platforms );
        /* Continue reading until item closing tag if found: */
        while( $xml->read() && $xml->name !== 'item' );
    }
}

如您所见,要构造一个非常简单的数组,我们必须编写大量代码。考虑到这只是一个例子:在现实世界中,您需要使用额外的检查来优化上面的代码,以避免无限循环。如前所述,您可以将XMLReader与DOM解析器结合使用。在此示例中,将$node = new XMLReader()替换为simplexml_load_string( $xml->readOuterXML() )是一个好主意。 Here您可以找到使用XMLReader和SimpleXML的详细示例。

使用SimpleXML:

$xml = simplexml_load_file( $filepath );    # Load XML File into SimpleXML Object
$result = array();                          # Init result array

/* Process each <item> node: */
foreach( $xml->xpath( '//item' ) as $node )
{
    $platforms = array();
    /* Process each <platform> node: */
    foreach( $node->platform as $platform )
    {
        /* Add platformType value to $platforms array: */
        $platforms[] = $platform->platformType[0]->__toString();
    }
    $result[] = implode( ', ', $platforms );
}

在这个例子中,如下所示,我在与XMLReader示例相同的行中省略了注释。

我使用->xpath来选择<item>个节点:在实际的XML示例中没有必要,因为<item>节点是root的直接子节点(我们可以通过{{1但是,如果$xml->item节点处于最深位置,则此XPath模式也将起作用。模式开始时的<item>表示“查找以下模式,无论它在哪里”。

XMLReader的语法更简单:您可以通过//语法或使用XPath表达式直接转到所需的节点。请注意,->nodeTag和XPath都返回数组,因此要引用它,您必须使用数组语法(->)。 SimpleXML总是返回SimpleXMLElement对象,因此要将其用作字符串,您必须使用->platformType[0]->__toString()将其强制转换为字符串。在示例中,您可以省略转换,因为$string = ($string) $platform->platformType[0]会将对象转换为字符串。

使用DOMDocument:

implode()

在阅读了第一个例子后,最后一个例子是自我解释的。您可以注意选择节点的不同语法($dom = New DOMDocument(); # Init DOMDocument Object $dom->load( $filepath ); # Load XML File into DOMDocument Object $xpath = new DOMXPath( $dom ); # Init DOMXpath Object $result = array(); foreach( $xpath->query( '//item' ) as $node ) { $platforms = array(); /* Process each <platformType> node: */ foreach( $node->getElementsByTagName( 'platformType' ) as $platformType ) { /* Add platformType value to $platforms array: */ $platforms[] = trim( $platformType->nodeValue ); } $result[] = implode( ', ', $platforms ); } ->query不返回数组,但是DOMNodeList对象:您可以使用{{1}引用每个集合节点} 句法)。此外,您可以使用->getElementsByTagName检索节点值,而无需进行转换。

现在,您可以选择首选解析器。