繁体   English   中英

复杂的编辑xml文件

[英]Complex edit xml file

例如,我们有以下xml:

<x>
    <y>some text</y>
    <y>[ID] hello</y>
    <y>world [/ID]</y>
    <y>some text</y>
    <y>some text</y>
</x>

并且我们需要删除单词“ [[ID]”,“ [/ ID]”和它们之间的文本(在解析时我们不知道),当然不会损坏xml格式。

我能想到的唯一解决方案是:

  1. 使用正则表达式在xml中查找文本,例如: "/\\[ID\\].*?\\[\\/ID\\]/" 在我们的例子中,结果将是"[ID]hello</y><y>world[/ID]"

  2. 上一步的结果是,我们需要使用以下正则表达式查找不带xml标签的文本: "/(?<=^|>)[^><]+?(?=<|$)/" ,然后删除此文本。 结果将是"</y><y>"

  3. 通过像这样进行smth在原始xml中进行更改:

    str_replace($step1string,$step2string,$xml);

这是正确的方法吗? 我只是认为这种“ str_replace”并不是最佳的编辑xml的方法,所以也许您知道更好的解决方案?

为了娱乐和娱乐,您可能需要阅读以下内容: RegEx匹配除XHTML自包含标签以外的其他开放标签

“正确”的解决方案是使用XML库并搜索节点以执行操作。 但是,即使有可能损坏XML格式,仅使用str_replace可能也会容易得多。 您必须评估收到类似<a href="[ID]">类的内容的可能性,以及防范此类情况的重要性,并权衡这些因素与开发时间。

删除特定的字符串很简单:

<?php
$xml = '<x>
    <y>some text</y>
    <y>[ID] hello</y>
    <y>world [/ID]</y>
    <y>some text</y>
    <y>some text</y>
</x>';

$d = new DOMDocument();
$d->loadXML($xml);
$x = new DOMXPath($d);
foreach($x->query('//text()[(contains(.,\'[ID]\') or contains(.,\'[/ID]\'))]') as $elm){
    $elm->nodeValue = preg_replace('/\[\/?ID\]/','',$elm->nodeValue);
}
var_dump($d->saveXML());
?>

当只删除特定标签中的textnode时,可以将te preg_replace更改为以下两个:

 $elm->nodeValue = preg_replace('/\[ID\].*$/','',$elm->nodeValue);
 $elm->nodeValue = preg_replace('/^.*\[/ID\]/','',$elm->nodeValue);

结果为您的示例:

<x>
<y>some text</y>
<y></y>
<y></y>
<y>some text</y>
<y>some text</y>
</x>

但是,在不破坏格式正确的XML的情况下删除它们之间的标记非常棘手。 在尝试大量DOM操作之前,您要如何处理:

DOM树中较高的[/ ID]:

<foo>[ID] foo
    <bar> lorem [/ID] ipsum </bar>
</foo>

DOM树中较低的[/ ID]

<foo> foo
    <bar> lorem [ID] ipsum </bar>
    [/ID]
</foo>

并按照您的示例打开/关闭跨越的同级:

<foo> foo
    <bar> lorem [ID] ipsum </bar>
    <bar> lorem [/ID] ipsum </bar>
</foo>

一个真正的难题突破了:嵌套是否可能,嵌套是否格式正确,它应该怎么做?

<foo> foo
    <bar> lo  [ID] rem [ID] ipsum </bar>
    <bar> lorem [/ID] ipsum </bar>
    [/ID]
</foo>

没有进一步的知识应该如何处理这些案件,就没有真正的答案。


编辑,并提供了更多的信息,实际的故障安全解决方案(例如:解析XML,不使用正则表达式)似乎很长,但可以在99.99%的情况下使用(当然,不包括个人错别字和脑筋急转弯:) ):

<?php
$xml = '<x>
    <y>some text</y>
    <y>
      <a> something </a>
      well [ID] hello
      <a> and then some</a>
    </y>
    <y>some text</y>
    <x>
      world
      <a> also </a>
        foobar [/ID] something
      <a> these nodes </a>
    </x>
    <y>some text</y>
    <y>some text</y>
</x>';
echo $xml;
$d = new DOMDocument();
$d->loadXML($xml);
$x = new DOMXPath($d);
foreach($x->query('//text()[contains(.,\'[ID]\')]') as $elm){
        //if this node also contains [/ID], replace and be done:
        if(($startpos = strpos($elm->nodeValue,'[ID]'))!==false && $endpos = strpos($elm->nodeValue,'[/ID]',$startpos)){
                $elm->replaceData($startpos, $endpos-$startpos + 5,'');
                var_dump($d->saveXML($elm));
                continue;
        }
        //delete all siblings of this textnode not being text and having [/ID]
        while($elm->nextSibling){
                if(!($elm->nextSibling instanceof DOMTEXT) || ($pos =strpos($elm->nodeValue,'[/ID]'))===false){
                        $elm->parentNode->removeChild($elm->nextSibling);
                } else {
                        //id found in same element, replace and go to next [ID]
                        $elm->parentNode->appendChild(new DOMTExt(substr($elm->nextSibling->nodeValue,$pos+5)));
                        $elm->parentNode->removeChild($elm->nextSibling);
                        continue 2;
                }
        }
        //siblings of textnode deleted, string truncated to before [ID], now let's delete intermediate nodes
        while($sibling = $elm->parentNode->nextSibling){ // in case of example: other <y> elements:
                //loop though childnodes and search a textnode with [/ID]
                while($child = $sibling->firstChild){
                        //delete if not a textnode
                        if(!($child instanceof DOMText)){
                                $sibling->removeChild($child);
                                continue;
                        }
                        //we have text, check for [/ID]
                        if(($pos = strpos($child->nodeValue,'[/ID]'))!==false){
                                //add remaining text in textnode:
                                $elm->appendData(substr($child->nodeValue,$pos+5));
                                //remove current textnode with match:
                                $sibling->removeChild($child);
                                //sanity check: [ID] was in <y>, is [/ID]?
                                if($sibling->tagName!= $elm->parentNode->tagname){
                                        trigger_error('[/ID] found in other tag then [/ID]: '.$sibling->tagName.'<>'.$elm->parentNode->tagName, E_USER_NOTICE);
                                }
                                //add remaining childs of sibling to parent of [ID]:
                                while($sibling->firstChild){
                                        $elm->parentNode->appendChild($sibling->firstChild);
                                }
                                //delete the sibling that was found to hold [/ID]
                                $sibling->parentNode->removeChild($sibling);
                                //done: end both whiles
                                break 2;
                        }
                        //textnode, but no [/ID], so remove:
                        $sibling->removeChild($child);
                }
                //no child, no text, so no [/ID], remove:
                $elm->parentNode->parentNode->removeChild($sibling);
        }
}
var_dump($d->saveXML());
?>

我能想到的唯一其他选择是,是否可以将XML格式设置为不同格式。

<x>
  <y>
    <z>[ID]</z>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM