0

情况如下:我有一些表示长格式文档记录的 XML 文件,其中分页符用 void 元素表示:<pb />。

我想要做的是构建某种数据结构(数组很好),其中每个元素代表每个“页面”的有效 XML,由 pb void 元素(又名自关闭又名单例)分隔。

问题是, pb 元素可以(并且确实)出现在任何任意父节点内的任意嵌套级别。

我的想法是使用 XPath 来识别每个 pb 元素,然后以某种方式关闭所有元素的父标签 - 结束页面 - 并重新打开它们以开始下一页。我正在使用PHP DOMXpath

下面是 XML 的示例:

<text id="foobar">
 <div type="zing" n="zee">
   <pb n=1 />
   <p>some text</p>
   <p>more text</p>
   <fw><pb n=2 />page 2</fw>
   <p>blah blah</p>
   <p>blah blah blah blah <fw><pb n=3 /></fw> blah</p>
 </div>
</text>

这就是我想要将其转换为:

<text id="foobar">
 <div type="zing" n="zee">
   <p>some text</p>
   <p>more text</p>
   <fw></fw>
 </div>
</text>
<text id="foobar">
 <div type="zing" n="zee">
   <fw>page 2</fw>
   <p>blah blah</p>
   <p>blah blah blah blah <fw></fw>
  </div>
</text>
<text id="foobar">
 <div type="zing" n="zee">
   <fw></fw> blah</p>
 </div>
</text>

任何人都想到了一种聪明或优雅的方式来做到这一点?

我现在拥有的是另一个循环内的递归循环,每次迭代都有多个 xpath 查询,而且它是 fuuuugly。

4

1 回答 1

0

到目前为止,这是我想出的。就像我说的,fuuuugly。

// Given an XML transcript, return an array of pages indexed by <pb> element definitions.
function transcript_to_pages($transcript) {
  $dom = new DOMDocument();

  // 1. Compile the list of pagebreak DOM nodes we're looking for
  // 2. Build a string of all their anscestors
  // 3. chunk the transcript by pagebreaks
  // 4. prepend the ancestor strings to each chunk.
  // 5. build an array of the munged chunks
  $dom->loadHtml($transcript);
  $xpath = new DOMXPath($dom);
  $pbnodes = $xpath->query('//pb');

  $close_string = $open_string = '';
  $i = 0;
  $total = $pbnodes->length;
  foreach ($pbnodes as $pbnode) {
    $page = '';
    $string = $dom->saveXML($pbnode);
    list($page, $transcript) = split($string, $transcript, 2);
    // The first <pb> element should have appeared as the first element of the
    // transcript. This would produce a leading blank page, which we'll skip. 
    if ($i++ == 0) {
      continue;
    }
    if (!empty($open_string)) {
      $page = $open_string . $page;
    }
    $parent = $pbnode->parentNode;
    $close_string = $open_string = '';
    while (!empty($parent)) {
      $close_string .= "</$parent->tagName>";
      $open_tag = "<$parent->tagName";
      if (!empty($parent->attributes)) {
        foreach ($parent->attributes as $key => $value) {
          $val = str_replace('"', '&quot;', $value->value);
          $open_tag .= " $key=\"" . $val . '"';
        }
      }
      $open_tag .= '>';
      $open_string = $open_tag . $open_string;
      if ($parent->tagName == 'text' || empty($parent->parentNode)) {
        break;
      }
      $parent = $parent->parentNode;
    }
    $page .= $close_string;
    $pages[$string] = $page;
  }
  return $pages;
}
于 2013-01-03T22:14:06.270 回答