【问题标题】:How do I get the inner text of a xml tag when there is already content in it?当xml标签中已经有内容时,如何获取它的内部文本?
【发布时间】:2012-12-03 14:50:43
【问题描述】:

这是我正在使用的示例 xml:

<contact id="43956">
 <personal>
      <name>
           <first>J</first>
           <middle>J</middle>
           <last>J</last>
           Some text...
      </name>
      <title>Manager</title>
      <employer>National</employer>
      <dob>1971-12-22</dob>
 </personal>
</contact>

我得到了Some text...,但现在我需要我的代码来读取整个 xml 文档。它也没有读取 xml 中的值...正如您所知,我以前从未使用过 XMLReader

这是我得到的:

Array ( [contact] =&gt; Array ( [id] =&gt; 43956 [value] =&gt; some sample value ) [first] =&gt; [middle] =&gt; [last] =&gt; [#text] =&gt; Some text... [name] =&gt; [title] =&gt; [employer] =&gt; [dob] =&gt; [personal] =&gt; )

这是我现在拥有的代码:

function xml2array($file, array $result = array()) {
$lastElementNodeType = '';
$xml = new XMLReader();
if(!$xml->open($file)) {
    die("Failed to open input file");
}
while($xml->read()) {
    switch ($xml->nodeType) {
        case $xml::END_ELEMENT:
            $lastElementNodeType = $xml->nodeType;
        case $xml::TEXT:
            $tag = $xml->name;
            if($lastElementNodeType == 15) {
                $result[$tag] = $xml->readString();                 
            }
        case $xml::ELEMENT:
            $lastElementNodeType = $xml->nodeType;
            $tag = $xml->name;
            if($xml->hasAttributes) {
                while($xml->moveToNextAttribute()) {
                    $result[$tag][$xml->name] = $xml->value;
                }
            }
    }
}
print_r($result);
}

我想过让这个函数递归,但是当我尝试这样做时,它使数组变得非常混乱。

我有这个版本,但它仍然没有输出first中的J等:

function xml2assoc($xml) { 
$tree = null; 
while($xml->read()) 
    switch ($xml->nodeType) { 
        case XMLReader::END_ELEMENT: return $tree; 
        case XMLReader::ELEMENT: 
            $node = array('tag' => $xml->name, 'value' => $xml->isEmptyElement ? '' : xml2assoc($xml)); 
            if($xml->hasAttributes) 
                while($xml->moveToNextAttribute()) 
                    $node['attributes'][$xml->name] = $xml->value; 
            $tree[] = $node; 
        break; 
        case XMLReader::TEXT: 
        case XMLReader::CDATA: 
            $tree .= $xml->value; 
    } 
return $tree; 
}

【问题讨论】:

  • 你真的应该使用内置的SimpleXML 类,而不是那种......不需要的复制/粘贴的abomination。它建于世纪之交,当时很糟糕,现在更糟。
  • @Charles 我也使用了这段代码$array = json_decode(json_encode((array)simplexml_load_string(file_get_contents($this-&gt;file))),1);' 但它速度较慢,性能非常重要。它也没有包含我需要的文本。
  • 您在问题中发布的所有大量代码,您无法提取文本值的位置(在 php 代码中)到底在哪里寻找?你可能不想在你的情况下使用SimpleXML,而是XMLReader
  • 我刚刚编辑了我的问题。我正在使用XMLReader,就像你建议的那样。现在我的问题是如何在到达下一个内部标签之前只抓取文本?

标签: php xml arrays


【解决方案1】:

拍1

我认为您需要做的是保存最近节点的类型或至少保存最后一个节点的类型,以便对其进行测试。简而言之,至少当您在示例 XML 中呈现它时,您将遇到一个 ELEMENT_END 节点类型、一个带有您要查找的文本的 TEXT 节点类型,然后是另一个 ELEMENT_END 节点类型.

所以您将需要一个case $xml::TEXT,并且您还需要保存以前的节点类型,以便您的解析器知道,在正常情况下它应该期待一个新的ELEMENT 事件, 或 END_ELEMENT 事件,但已收到 TEXT。这将是您需要使用readString() 将文本捕获到临时变量并保存它以供您使用,或者等待查看下一个节点是否也是ELEMENT_END 的信号,此时您可以保存它并清除临时变量。

采取 2

现在我们对您希望得到的结果有了更多了解(即,由于您希望捕获整个树,而不仅仅是从中获取特定信息),我建议您坚持使用递归版本的功能。我稍微修改了你的那个(请参阅 TEXT 和 CDATA 案例以了解主要的实质性变化)。

function xml2assoc($xml)
{
    $tree = null;
    while($xml->read())
    {
        switch ($xml->nodeType)
        {
            case XMLReader::END_ELEMENT:
                return $tree;
            case XMLReader::ELEMENT: 
                $node = array('tag' => $xml->name, 'value' => $xml->isEmptyElement ? '' : xml2assoc($xml));
                if($xml->hasAttributes)
                    while($xml->moveToNextAttribute()) 
                        $node['attributes'][$xml->name] = $xml->value; 
                $tree[] = $node;
                break;
            case XMLReader::TEXT:
                $tree["text"] = $xml->value;
                break;
            case XMLReader::CDATA:
                $tree["cdata"] = $xml->value;
                break;
        }
    }
    return $tree;
}

本例中的输出如下所示:

Array
(
    [0] => Array
        (
            [tag] => contact
            [value] => Array
                (
                    [0] => Array
                        (
                            [tag] => personal
                            [value] => Array
                                (
                                    [0] => Array
                                        (
                                            [tag] => name
                                            [value] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [tag] => first
                                                            [value] => Array
                                                                (
                                                                    [text] => J
                                                                )
                                                        )
                                                    [1] => Array
                                                        (
                                                            [tag] => middle
                                                            [value] => Array
                                                                (
                                                                    [text] => J
                                                                )
                                                        )
                                                    [2] => Array
                                                        (
                                                            [tag] => last
                                                            [value] => Array
                                                                (
                                                                    [text] => J
                                                                )
                                                        )
                                                    [text] => Some text...
                                                )
                                        )
                                    [1] => Array
                                        (
                                            [tag] => title
                                            [value] => Array
                                                (
                                                    [text] => Manager
                                                )
                                        )
                                    [2] => Array
                                        (
                                            [tag] => employer
                                            [value] => Array
                                                (
                                                    [text] => National
                                                )
                                        )
                                    [3] => Array
                                        (
                                            [tag] => dob
                                            [value] => Array
                                                (
                                                    [text] => 1971-12-22
                                                )
                                        )
                                )
                        )
                )
            [attributes] => Array
                (
                    [id] => 43956
                )
        )
)

我认为这就是您想要做的小修改,尽管我们实际上只是在这里重新发明轮子。我希望你需要解析的 XML 不是特别大。

【讨论】:

  • 好的,我知道了。但是我的代码没有通过整个 xml 文档。我将编辑我的问题,以便您查看我的问题。
  • 我感觉有点像在这里射击移动目标,但我会更新我上面的答案以解决您对问题的更改。
  • 对不起。谢谢。有用。是的,我将使用一个非常大的 xml 文件。这段代码会引起问题吗?我将使用的 xml 文件在这里:
  • 我将使用的 xml 文件在这里:www.expekt.com/exportServlet?category=SOC%25
  • 基于流的解析器的优点是您不必将整个 XML 结构加载到内存中即可使用它(您的代码仍然如此)。它们很复杂,通常需要多 MB 文件。您的文件约为 600k;你应该没事。您可能想要对将结构加载到 SimpleXML 中的时间与此方法进行对比,以确保节省的时间值得头疼。如果 XML 从不更改或很少更改,则使用 SimpleXML 帮助将必要的数据缓慢地传输到 SQLite 或另一个 DB 一次/偶尔在后端并从 db 为前端用户提供服务可能更明智。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-26
  • 2017-07-24
  • 1970-01-01
  • 2014-04-03
相关资源
最近更新 更多