使用 PHP 和 XPATH 呈现嵌入在 XML 节点中的 XML 节点

Using PHP and XPATH to render XML nodes embedded within an XML node

提问人:thisisready 提问时间:11/5/2021 更新时间:11/5/2021 访问量:52

问:

我正在尝试使用 PHP 解析具有命名空间的 XML 文档以输出 HTML,保留其原始结构。

我在下面的代码中使用了 XPATH 和 foreach 循环来呈现标题、段落和列表,但这不尊重文档的原始结构。我也不清楚如何呈现嵌入在内容中的类似 URL 的东西,该内容也包装在 XML 标签中。

XML 示例:

<a:section>
<c:ref value="1">1</c:ref>
<c:title>Title of content</c:title>

<f:subsection>
<c:ref value="1.1">1.1</c:ref>
<c:title>Subsection title</c:title>

<b:content>Make sure you check out this link: <c:url address="www.google.com" type="https">google.com</c:url> and then review the list below:</b:content>
    <c:list type="bullet">
        <c:listitem>
            <b:content>bullet item 1</b:content>
        </c:listitem>
        <c:listitem>
            <b:content>bullet item 2</b:content>
        </c:listitem>
        <c:listitem>
            <b:content>bullet item 3</b:content>
        </c:listitem>
    </c:list>
<b:content>More content here in text form</b:content>

</f:subsection>

</a:section>

PHP 示例:

$xml = file_get_contents('content.xml');
$sxml = new SimpleXmlElement($xml);
$section = $sxml->xpath('//a:section');

foreach ($section as $s) {
    $sectionnumber = $s->xpath('c:ref');
    $title = $s->xpath('c:title');
    foreach ($title as $t) {
        echo '<h2>'.$sectionnumber[0].' '.$t.'</h2>';
    }
}

$subsection = $s->xpath('f:subsection');
    foreach ($subsection as $ss) {
      $subheadingnumber = $ss->xpath('c:ref');
      $subheading = $ss->xpath('c:title');
      foreach ($subheading as $sh) {
          echo '<h3>'.$subheadingnumber[0].' '.$sh.'</h3>';
      }
      $paragraphs = $ss->xpath('b:content');
      foreach ($paragraphs as $p){
        echo '<p>'.$p.'</p>';
      }
      $lists = $ss->xpath('c:list');
      foreach ($lists as $l){
        $listitem = $l->xpath('c:listitem');
        foreach ($listitem as $item){
          $listcontent = $item->xpath('b:content');
          foreach ($listcontent as $a){
            echo '<li>'.$a.'</li>';
          }
        }
      }
    }
php xml xpath 命名空间

评论

1赞 Martin Honnen 11/5/2021
您是否考虑过让 XSLT 完成 XML 到 HTML 的转换?毕竟,您需要做的就是编写一个 XSLT 样式表并进行转换,例如 等等,例如 文档结构将被保留,只是映射到 HTML。<xsl:template match="c:list"><ul><xsl:apply-templates/></ul></xsl:template><xsl:template match="c:listitem"><li><xsl:apply-templates/></li></xsl:template>
0赞 thisisready 11/6/2021
谢谢@MartinHonnen。对于以前没有真正使用过 XML 的人来说非常有用。采用 XSLT 方法是我试图解决的问题的正确解决方案。

答:

1赞 ThW 11/5/2021 #1

缺少具有命名空间定义的文档元素。他们是 重要,您不应该依赖前缀(它们可以更改并且对于元素是可选的)。

对于这个答案,我添加了一个带有虚拟命名空间的文档元素。

<?xml version="1.0" encoding="utf-8" ?>
<a:content
  xmlns:a="urn:a"
  xmlns:b="urn:b"
  xmlns:c="urn:c"
  xmlns:f="urn:f">
  <a:section>
    ...

XSLT 正是为此目的而设计的模板语言。它允许您定义节点的匹配并转换它们:

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:a="urn:a"
  xmlns:b="urn:b"
  xmlns:c="urn:c"
  xmlns:f="urn:f"
  exclude-result-prefixes="a b c f">

  <xsl:output method="html"/>

  <xsl:template match="/*">
    <div>
      <xsl:for-each select="a:section">
        <h2><xsl:value-of select="c:title"/></h2>
        <xsl:for-each select="f:subsection">
          <h3><xsl:value-of select="c:title"/></h3>
          <div><xsl:apply-templates select="b:content|c:list"/></div>
        </xsl:for-each>
      </xsl:for-each>
    </div>
  </xsl:template>

  <xsl:template match="b:content">
    <p><xsl:apply-templates/></p>
  </xsl:template>

  <xsl:template match="c:list">
    <ul>
      <xsl:for-each select="c:listitem">
        <li><xsl:apply-templates/></li>
      </xsl:for-each>
    </ul>
  </xsl:template>

  <xsl:template match="c:url">
    <a href="{@type}://{@address}"><xsl:apply-templates/></a>
  </xsl:template>

</xsl:stylesheet>

注意匹配 XML 文档中的命名空间。

PHP 将加载 XML 和模板并对其进行处理:

// load the content
$content = new DOMDocument();
$content->load(__DIR__.'/content.xml');
// load the template
$template = new DOMDocument();
$template->load(__DIR__.'/transform.xsl');
// bootstrap XSLT
$processor = new XSLTProcessor();
$processor->importStylesheet($template);
// transform and output
echo $processor->transformToXml($content);

输出:

<div>
  <h2>Title of content</h2>
  <h3>Subsection title</h3>
  <div>
    <p>Make sure you check out this link: <a href="https://www.google.com">google.com</a> and then review the list below:</p>
    <ul>
      <li><p>bullet item 1</p></li>
      <li><p>bullet item 2</p></li>
      <li><p>bullet item 3</p></li>
    </ul>
    <p>More content here in text form</p>
  </div>
</div>

评论

0赞 thisisready 11/6/2021
谢谢@ThW。通过这个例子,我能够将该方法应用于我正在处理的问题。