【问题标题】:Strip all elements inside a specific element when descendant elements have same name as ancestor当后代元素与祖先同名时,剥离特定元素内的所有元素
【发布时间】:2015-07-08 09:23:05
【问题描述】:

我正在使用 PHP,我想去掉特定标签内的所有标签,只保留纯文本。我坚持的问题是有一些子标签与父母标签具有相同的名称:

<corpo>
    <num>1.</num>
    <mod id="mod167">
        String 1
        <commas id="mod167-vir1" type="word">String 2</commas>
        <com id="mod166-vir1-20090024-art13-com16.1"><num>&lt;&lt;16.</num></com>
        <rif xlink:href="urn" xlink:type="simple">String 3</rif><h:p>Something here</h:p>
        <corpo>String 4</corpo>
   </mod>
</corpo>

这里,例如,corpo 有一个同名的子标签 (&lt;corpo&gt;String 4&lt;/corpo&gt;),并且在父标签 corpo 内使用了两次 num 标签(&lt;num&gt;1.&lt;/num&gt;&lt;num&gt;&amp;lt;&amp;lt;16.&lt;/num&gt;) .

从最高的corpo 标签开始,我想去掉每个子标签,只保留纯文本。结果应该是:

<corpo>
    String 1 String 2 &lt;&lt;16. String 3 Something here String 4
</corpo>

到目前为止,我通过添加我想要保留的所有标签来尝试使用 SimpleXML 和 PHP strip_tags,但当然它并没有给出我期望的结果。

$result = strip_tags($xml, "<corpo></corpo>";

【问题讨论】:

  • 您的 XML 无效,您删除了命名空间定义 (xmlns:*)。你想读取字符串,还是真的需要新建一个xml?
  • 报告的 XML 是从 dB 导出的复杂 XML 的摘录。我没有复制确切的 XML,因为我认为可以使用正则表达式解决问题,因为我只需要读取第一个 corpo 标记内的所有字符串(纯文本)。
  • 对于 XML,答案几乎永远不会是 RegEx。如果您删除命名空间定义,它会更改并破坏 XML。答案可能无效,因为缺少信息。

标签: php xml xpath


【解决方案1】:

这与@ThW 所写的内容非常相关,只是更侧重于 SimpleXML。我还在 xpath 上显示了一些不同的角度来选择 corpo 元素。

给定一个文档与您的问题中的字符串 $buffer 相同或具有更多祖先,这里是一个 XML 示例:

$xml = simplexml_load_string($buffer);

foreach ($xml->xpath('//corpo[not(ancestor::corpo)]') as $corpo) {
    $corpo[0] = dom_import_simplexml($corpo)->textContent;
}

$xml->asXML('php://output');

一个示例性的输出是:

<a xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h="ns:h">
    <b>
        <corpo>
            1.

                String 1
                String 2

                    &lt;&lt;16.

                String 3
                Something here
                String 4

        </corpo>
    </b>
</a>

它的工作原理如下:

获取没有同名祖先的每个 corpo 元素。这是由 xpath 完成的:

//corpo[not(ancestor::corpo)]

那么由于这是一个 SimpleXMLElement 并且您想要文本内容,因此可以通过 $corpo 关联的 DOMElement 节点访问它:

dom_import_simplexml($corpo)->textContent;

剩下的表达式

$corpo[0] = ...

只是告诉更新那个 SimpleXMLElement 的内容(所谓的自引用)。

顺便说一句,您可以在这里使用strip_tags($corpo-&gt;asXML()) 而不是dom_import_simplexml($corpo)-&gt;textContent,但我不建议这样做,因为我不知道strip_tags 到底有多稳定。它至少不符合 XML 标准。

现在您可能还想应用一些空白规范化,因为 preg_replace 与 UTF-8 标志很方便,UTF-8 标志是 SimpleXMLElementDOMElement 使用的字符串编码强>:

foreach ($xml->xpath('//corpo[not(ancestor::corpo)]') as $corpo) {
    $text     = dom_import_simplexml($corpo)->textContent;
    $corpo[0] = preg_replace('~\s+~u', ' ', $text);
}

此变体为您提供:

<?xml version="1.0"?>
<a xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h="ns:h">
    <b>
        <corpo> 1. String 1 String 2 &lt;&lt;16. String 3 Something here String 4 </corpo>
    </b>
</a>

完整示例一览Demo:

<?php

$buffer = <<<XML
<a xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h="ns:h">
    <b>
        <corpo>
            <num>1.</num>
            <mod id="mod167">
                String 1
                <commas id="mod167-vir1" type="word">String 2</commas>
                <com id="mod166-vir1-20090024-art13-com16.1">
                    <num>&lt;&lt;16.</num>
                </com>
                <rif xlink:href="urn" xlink:type="simple">String 3</rif>
                <h:p>Something here</h:p>
                <corpo>String 4</corpo>
            </mod>
        </corpo>
    </b>
</a>
XML;


$xml = simplexml_load_string($buffer);

foreach ($xml->xpath('//corpo[not(ancestor::corpo)]') as $corpo) {
    $text     = dom_import_simplexml($corpo)->textContent;
    $corpo[0] = preg_replace('~\s+~u', ' ', $text);
}

$xml->asXML('php://output');

【讨论】:

    【解决方案2】:

    如果您将 XML 加载到 DOM 中,您可以读取 DOMNode::$textContent 属性。

    $document = new DOMDocument();
    $document->loadXml($xml);
    
    var_dump($document->documentElement->textContent);
    

    输出包含包含所有空格的文本内容。

    string(113) "
        1.
    
            String 1
            String 2
            <<16.
            String 3Something here
            String 4
    
    "
    

    在更复杂的结构中,使用 Xpath 获取值。函数normalize-space() 将第一个节点转换为字符串,删除前导和尾随空格并将所有其他空白组转换为单个空格。

    $xpath = new DOMXpath($document);
    var_dump($xpath->evaluate('normalize-space(/corpo)'));
    

    输出:

    string(58) "1. String 1 String 2 <<16. String 3Something here String 4"
    

    要创建删除标签的 XML,请导入不带子节点的 corpo 节点并附加文本内容:

    $target = new DOMDocument();
    foreach ($xpath->evaluate('/corpo') as $corpo) {
      $target
        ->appendChild(
          $target->importNode($corpo)
        )->appendChild(
          $target->createTextNode(
            $xpath->evaluate('normalize-space(.)', $corpo)
          )
        );
    }
    
    echo $target->saveXml();
    

    输出:

    <?xml version="1.0"?>
    <corpo xmlns:xlink="urn:xlink" xmlns:h="urn:h">1. String 1 String 2 &lt;&lt;16. String 3Something here String 4</corpo>
    

    【讨论】:

    • 您为我指明了正确的方向,谢谢,尽管我仍然需要一些帮助。你的第二种方法很棒。它假定“corpo”是根节点(正如我在帖子中所述),但实际上并非如此。我通过以下方式解决:$xml-&gt;xpath('//a:LeggeRegionale[@id="urn:nir:legge:2011-08-11;11"]/a:articolato/a:articolo[@id="art10"]'); 如何相应地更改您的代码?
    • 这取决于实际的结构和命名空间定义。您将需要调整 Xpath 表达式。 SimpleXMLElement::xpath() 但是有限,它只能返回 SimpleXMLElement 实例的数组,该示例仅适用于 DOM。 DOMXpath::evaluate() 的第二个参数是表达式的上下文节点。
    猜你喜欢
    • 2019-06-10
    • 1970-01-01
    • 2016-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多