Php 使用void元素作为分隔符处理XML

Php 使用void元素作为分隔符处理XML,php,xml,xpath,Php,Xml,Xpath,情况是这样的:我有一些表示长格式文档副本的XML文件,其中分页符用void元素表示: 我想做的是构建某种类型的数据结构(数组很好),其中每个元素表示每个“页面”的有效XML,由pb void元素(也称为self-closing,也称为singleton)分隔 关键是,pb元素可以(而且确实)出现在任意嵌套级别的任意父节点中 我的想法是使用XPath标识每个pb元素,然后以某种方式关闭元素的所有父标记(结束页面),然后重新打开它们以开始下一个页面。我正在使用 下面是XML的示例: <text

情况是这样的:我有一些表示长格式文档副本的XML文件,其中分页符用void元素表示:

我想做的是构建某种类型的数据结构(数组很好),其中每个元素表示每个“页面”的有效XML,由pb void元素(也称为self-closing,也称为singleton)分隔

关键是,pb元素可以(而且确实)出现在任意嵌套级别的任意父节点中

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

下面是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>

一些文本

更多文本

第2页 废话

废话废话废话

以下是我想将其转化为:

<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>

一些文本

更多文本

第2页 废话

废话废话 废话

有人想到一个聪明或优雅的方法来做到这一点吗


我现在在另一个循环中有一个递归循环,每个迭代都有多个xpath查询,这很糟糕。

以下是我到目前为止的想法。就像我说的,丑死了

// 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;
}
//给定XML转录本,返回按元素定义索引的页面数组。
职能部门成绩单至成绩单页($成绩单){
$dom=新的DOMDocument();
//1.编译我们正在查找的pagebreak DOM节点列表
//2.建立一系列他们的应答者
//3.用分页符将成绩单分块
//4.为每个区块预先添加祖先字符串。
//5.构建一个咀嚼块数组
$dom->loadHtml($transcript);
$xpath=newdomxpath($dom);
$pbnodes=$xpath->query('//pb');
$close\u string=$open\u string='';
$i=0;
$total=$pbnodes->length;
foreach($pbnodes作为$pbnode){
$page='';
$string=$dom->saveXML($pbnode);
列表($page,$transcript)=拆分($string,$transcript,2);
//第一个元素应该作为
//成绩单。这将产生一个前导空白页,我们将跳过它。
如果($i++==0){
继续;
}
如果(!empty($open_string)){
$page=$open_字符串。$page;
}
$parent=$pbnode->parentNode;
$close\u string=$open\u string='';
而(!空($parent)){
$close_string.=“标记名>”;
$open_tag=“tagName”;
如果(!空($parent->attributes)){
foreach($parent->attributes as$key=>$value){
$val=str_replace(“”,“”,$value->value);
$open\u标记。=“$key=\”.$val.”;
}
}
$open_标签=“>”;
$open_string=$open_标记。$open_string;
如果($parent->tagName=='text'| |空($parent->parentNode)){
打破
}
$parent=$parent->parentNode;
}
$page.=$close\u字符串;
$pages[$string]=$page;
}
返回$pages;
}

如果id属性是xsd:id类型,那么您的计划当然不会起作用。您应该考虑将s转换为容器。通常,您需要一个XML解析器,它允许您跟踪打开的元素堆栈,每次您都要“压缩”打开一个新的元素,然后再次“解压缩”。