【问题标题】:Group a number of template matches without a parent element in XSLT 2在 XSLT 2 中对多个没有父元素的模板匹配进行分组
【发布时间】:2015-02-26 17:22:38
【问题描述】:

我有一个非常非结构化的 XML 文档(取自 Pandoc 转换的 docx 到 docbook 格式),我正在尝试使用 XSLT 清理它。 xml的格式是这样的;

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
                  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<article>
  <articleinfo>
    <title></title>
  </articleinfo>
<informaltable>
  <tgroup cols="2">
    <colspec align="left" />
    <colspec align="left" />
    <thead>
      <row>
        <entry>
          <emphasis role="strong">How did you assist
          Customer?</emphasis>
        </entry>
        <entry>
          <emphasis>Lorem ipsum dolor sit amet.</emphasis>
        </entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
          <emphasis role="strong">What difference did this make for the
          Customer?</emphasis>
        </entry>
        <entry>
          <emphasis>Lorem ipsum dolor sit amet.</emphasis>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
    </tbody>
  </tgroup>
</informaltable>
<para>
  Staff Member: John Smith
</para>
<informaltable>
  <tgroup cols="2">
    <colspec align="left" />
    <colspec align="left" />
    <thead>
      <row>
        <entry>
          <emphasis role="strong">How did you assist
          Customer?</emphasis>
        </entry>
        <entry>
          <emphasis>Lorem ipsum dolor sit amet.</emphasis>
        </entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
          <emphasis role="strong">What difference did this make for the
          Customer?</emphasis>
        </entry>
        <entry>
          <emphasis>Lorem ipsum dolor sit amet.</emphasis>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
    </tbody>
  </tgroup>
</informaltable>
<para>
  Staff Member: John Smith
</para>
<informaltable>
  <tgroup cols="2">
    <colspec align="left" />
    <colspec align="left" />
    <thead>
      <row>
        <entry>
          <emphasis role="strong">How did you assist
          Customer?</emphasis>
        </entry>
        <entry>
        </entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
          <emphasis role="strong">What difference did this make for the
          Customer?</emphasis>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
      <row>
        <entry>
        </entry>
        <entry>
        </entry>
      </row>
    </tbody>
  </tgroup>
</informaltable>
<para>
  Staff Member: _________________________
</para>
</article>

我已经使用以下 XSLT 成功地精简了它;

<?xml version="1.0"?>

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>

    <xsl:variable name="fileDateStamp">
        <xsl:analyze-string select="base-uri(.)" regex="\s*(\d\d\d\d\-\d\d\-\d\d)\s*">
            <xsl:matching-substring>
                <xsl:value-of select="regex-group(1)"/>
            </xsl:matching-substring>
        </xsl:analyze-string>       
    </xsl:variable>

    <xsl:template match="/">
        <impactStatements>
            <xsl:apply-templates/>
        </impactStatements>
    </xsl:template>

    <xsl:template match="informaltable/tgroup/thead/row/entry">
        <xsl:analyze-string select="normalize-space(.)" regex="\s*How(.*)\s*">
            <xsl:matching-substring>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <Assisted>
                    <xsl:value-of select="(.)"/>    
                </Assisted>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>

    <xsl:template match="informaltable/tgroup/tbody/row/entry">
        <xsl:analyze-string select="normalize-space(.)" regex="\s*What(.*)\s*">
            <xsl:matching-substring>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <Difference>
                    <xsl:value-of select="(.)"/>
                </Difference>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>

    <xsl:template match="para">
        <xsl:analyze-string select="normalize-space(.)" regex="\s*\Staff Member: ([A-Z].*)\s*">
            <xsl:matching-substring>
                <Staff><xsl:value-of select="regex-group(1)"/></Staff>
                <DateCreated><xsl:value-of select="$fileDateStamp"/></DateCreated>
            </xsl:matching-substring>
        </xsl:analyze-string>
    </xsl:template>

</xsl:stylesheet> 

但我缺少的是能够在每个“记录”周围添加一个标签。因为&lt;informaltable&gt;&lt;para&gt; 都是&lt;article&gt; 的孩子,所以我最基本的XSLT 知识完全让我失望。我明白了

<?xml version="1.0" encoding="UTF-8"?>
<impactStatements>
   <Assisted>Lorem ipsum dolor sit amet.</Assisted>
   <Difference>Lorem ipsum dolor sit amet.</Difference>
   <Staff>John Smith</Staff>
   <DateCreated>2014-01-01</DateCreated>
   <Assisted>Lorem ipsum dolor sit amet.</Assisted>
   <Difference>Lorem ipsum dolor sit amet.</Difference>
   <Staff>John Smith</Staff>
   <DateCreated>2014-01-01</DateCreated>
</impactStatements>

但我想要;

<?xml version="1.0" encoding="UTF-8"?>
<impactStatements>
    <statement>
        <Assisted>Lorem ipsum dolor sit amet.</Assisted>
        <Difference>Lorem ipsum dolor sit amet.</Difference>
        <Staff>John Smith</Staff>
        <DateCreated>2014-01-01</DateCreated>
    </statement>
    <statement>
        <Assisted>Lorem ipsum dolor sit amet.</Assisted>
        <Difference>Lorem ipsum dolor sit amet.</Difference>
        <Staff>John Smith</Staff>
        <DateCreated>2014-01-01</DateCreated>
    </statement>
</impactStatements>

这是一项临时工作,我知道我可以通过其他方式更改 XML,但我确信我只是缺乏一些基本知识来更改 XSLT,我必须做我想做的事。我尝试了各种不同的方法并用谷歌搜索但无济于事。我尝试过的所有操作都会破坏生成的 XML 的格式。

【问题讨论】:

    标签: xml xslt xslt-2.0


    【解决方案1】:

    我会先添加一个模板

    <xsl:template match="article">
      <xsl:for-each-group select="*" group-starting-with="informaltable">
        <statement>
          <xsl:apply-templates select="current-group()"/>
        </statement>
      </xsl:for-each-group>
    </xsl:template>
    

    对于您的示例(在添加 &lt;xsl:strip-space elements="*"/&gt; 以提高可读性之后)我得到了输出

    <impactStatements>
       <statement/>
       <statement>
          <Assisted>Lorem ipsum dolor sit amet.</Assisted>
          <Difference>Lorem ipsum dolor sit amet.</Difference>
          <Staff>John Smith</Staff>
          <DateCreated/>
       </statement>
       <statement>
          <Assisted>Lorem ipsum dolor sit amet.</Assisted>
          <Difference>Lorem ipsum dolor sit amet.</Difference>
          <Staff>John Smith</Staff>
          <DateCreated/>
       </statement>
       <statement/>
    </impactStatements>
    

    我不确定空的statement元素是因为缺少样本数据还是要排除某些元素被处理,你需要解释输入中的哪些元素应该创建结果statement .

    【讨论】:

    • 谢谢马丁,对不起,我没有说清楚我实际上过滤掉了空的 informaltablepara 元素(它们都是链接的,从技术上讲不仅仅是空的 para 而是没有中的名称)。我尝试了一个for-each-group,但设法得到所有assisted,然后是所有difference,而不是一起记录!既然我有一些命令,我​​可以删除另一遍中的空语句!
    【解决方案2】:

    一个有趣且问得很好的问题!将匹配/的模板更改为

    <xsl:template match="/article">
        <impactStatements>
        <xsl:for-each select="informaltable">
            <statement>
                <xsl:apply-templates select=". | following-sibling::*[self::para][1]"/>
            </statement>
        </xsl:for-each>
        </impactStatements>
    </xsl:template>
    

    结果是:

    <?xml version="1.0" encoding="UTF-8"?>
    <impactStatements>
       <statement>
          <Assisted>Lorem ipsum dolor sit amet.</Assisted>
          <Difference>Lorem ipsum dolor sit amet.</Difference>
          <Staff>John Smith</Staff>
          <DateCreated/>
       </statement>
       <statement>
          <Assisted>Lorem ipsum dolor sit amet.</Assisted>
          <Difference>Lorem ipsum dolor sit amet.</Difference>
          <Staff>John Smith</Staff>
          <DateCreated/>
       </statement>
       <statement/>
    </impactStatements>
    

    我认为这几乎是正确的。最后有一个空的statement,因为输入中有3个informaltable元素。你想怎么处理?

    【讨论】:

    • 感谢 Mathias,我曾尝试使用 XPath 但完全失败,这是我使用的一个很好的例子。我拥有的实际 XML 文件末尾有很多“空”informaltable 元素,我想过滤掉这些元素,但我当然可以在第二遍中执行此操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 2014-05-13
    • 2019-09-05
    • 2012-11-24
    • 2019-05-05
    • 1970-01-01
    相关资源
    最近更新 更多