php/simplexml 在文本前后添加元素

php/simplexml adding elements before and after text

我正在尝试将元素插入 xml 文档中的一些文本。部分问题可能是它的格式不正确 xml 并且它需要像纯文本一样更容易被人类阅读。所以我有这样的东西:

<record>
  <letter>
    <header>To Alice from Bob</header>
    <body>Hi, how is it going?</body>
  </letter>
</record>

我需要结束这个:

<record>
  <letter>
    <header>To <to>Alice</to> from <from>Bob</from></header>
    <body>Hi, how is it going?</body>
  </letter>
</record>

类似的东西应该是有效的html:

<p>To <span>Alice</span> from <span>Bob</span></p>

我可以把header的值设置成一个字符串,但是<>被转换成了&lt&gt,这样不好。现在我正在使用 $node->header->addChild('to', 'Alice')$node[0]->header = 'plain text'.

如果我这样做

$node->header->addChild('to', 'Alice'); 
$node->header = 'plain text';
$node->header->addChild('from', 'Bob'); 

然后我得到

<header>plain text <from>Bob</from></header>

'to'被消灭

快速而肮脏的方法就是顺其自然

<header>plain text <to>Alice</to><from>Bob</from></header>

然后再次打开文件并四处移动元素。或者搜索并替换 < 和 >。但这似乎是错误的方式。

这可以用 simpleXML 实现吗?

谢谢!

从 DOM 的角度来看(SimpleXML 是其之上的抽象),您不需要在文本周围插入元素。您将文本节点替换为文本节点和元素节点的混合。 SimpleXML 在混合子节点方面存在一些问题,因此您可能希望直接使用 DOM。这是一个注释示例:

$xml = <<<'XML'
<record>
  <letter>
    <header>To Alice from Bob</header>
    <body>Hi, how is it going?</body>
  </letter>
</record>
XML;

// the words and the tags you would like to create
$words = ['Alice' => 'to', 'Bob' => 'from'];
// a split pattern, you could built this from the array
$pattern = '((Alice|Bob))';

// bootstrap the DOM
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

// iterate any text node with content
foreach ($xpath->evaluate('//text()[normalize-space() != ""]') as $text) {
  // use the pattern to split the text into an list
  $parts = preg_split($pattern, $text->textContent, -1, PREG_SPLIT_DELIM_CAPTURE);
  // if it was split actually
  if (count($parts) > 1) {
    /// iterate the text parts
    foreach ($parts as $part) {
      // if it is a word from the list
      if (isset($words[$part])) {
        // add the new element node
        $wrap = $text->parentNode->insertBefore(
          $document->createElement($words[$part]),
          $text
        );
        // and add the text as a child node to it
        $wrap->appendChild($document->createTextNode($part));
      } else {
        // otherwise add the text as a new text node
        $text->parentNode->insertBefore(
          $document->createTextNode($part),
          $text
        );
      }
    }
    // remove the original text node
    $text->parentNode->removeChild($text);
  }
}

echo $document->saveXml();

输出:

<?xml version="1.0"?>
<record>
  <letter>
    <header>To <to>Alice</to> from <from>Bob</from></header>
    <body>Hi, how is it going?</body>
  </letter>
</record>