如何对同一XML表应用多个XSLT转换。

huangapple go评论58阅读模式
英文:

How to apply multiple XSLT transformations to the same XML table

问题

我正在处理客户交给我的一些XML文档,有时它们不够"干净"。因此,我在将它们传递到下一步之前使用XSLT 2.0来清理它们。

我遇到的一个问题是,表格中的空行或孤立行("")会破坏后续的处理,而我无法更改该处理。因此,在XSLT转换期间,我需要去掉这些空行。

以下是我正在处理的XML的缩短版本...

<table outputclass="OriginalTableClass">
    <tgroup cols="2">
        <tbody>
            <row>
                <entry>
                    <p outputclass="TBL_Heading">Heading</p>
                </entry>
                <entry>
                    <p outputclass="TBL_Heading">Heading</p>
                </entry>
            </row>
            <row>
                <entry morerows="1">
                    <p outputclass="TBL_Body_Text">Content</p>
                </entry>
                <entry>
                    <p outputclass="TBL_Body_Text">Content</p>
                </entry>
            </row>
            <row/>
            <row/>
        </tbody>
    </tgroup>
</table>

所以你会看到最后两行是空的。我需要去掉这些,所以我想到了下面的方法,似乎可以正常工作...

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:x2w="http://www.orbistechnologies.com/xml2word"
    exclude-result-prefixes="xs"
    version="2.0">
    
    <xsl:template match="*" mode="ABC">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates mode="ABC"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- get rid of any orphaned rows -->
    <xsl:template match="tbody/row[not(child::*)]" mode="ABC"/>

我的逻辑是,如果一行没有子元素,就删除它。这个方法一直运行得很好,直到我稍后必须添加一个转换,以更改具有标题行的相同表的类。它看起来像这样...

<!-- change the outputclass for tables that have a header row -->
<xsl:template match="table[@outputclass='OriginalTableClass']" mode="ABC">
    <xsl:copy>
        <xsl:element name="table">
            <xsl:choose>
                <!-- does the table have a header row? -->
                <xsl:when test="*/descendant::p[@outputclass='TBL_Heading']">
                    <!-- if yes, then assign the new class -->
                    <xsl:attribute name="outputclass">NewTableClass</xsl:attribute>
                </xsl:when>
                <xsl:otherwise>
                    <!-- if not, then let the current class ride -->
                    <xsl:attribute name="outputclass">
                        <xsl:value-of select="@outputclass"/>
                    </xsl:attribute>
                </xsl:otherwise>
            </xsl:choose>
            <!-- copy all of the content inside the table -->
            <xsl:copy-of select="./node()"/>
        </xsl:element>
    </xsl:copy>
</xsl:template>

这个转换也运行得很好。然而...这两个转换不能很好地结合在一起。也就是说,如果我包括第二个转换,那么孤立的行仍然存在。我有点理解为什么会这样,因为我试图两次转换相同的内容,这是不允许的。但是...我对XSLT还比较陌生,无法弄清楚如何在同一个表中同时实现这两个目标。

到目前为止,我尝试的方法是在第二个转换中包含第一个转换(去除孤立行)。因为在"copy-of"发生的地方应该可以做到这一点。但我无法很好地理解如何对子/后代元素(那些行)进行更改,而不是对父元素进行匹配(表)。或者如何在表的子/后代内容中循环,因为表的级别和结构可能会有所变化(意味着:不总是table/tgroup/tbody/row)。

我还尝试给第一个转换赋予更高的优先级值,并给它们不同的"mode"值。尽管我怀疑这两者都不会起作用...而且它们确实没有起作用。

我可能在这里错过了XSLT的基本概念是什么?如果我必须对同一个表进行另一个转换怎么办?一旦我应用了类似这样的父级转换,表内的所有元素都无法访问吗?

感谢任何帮助。谢谢!

英文:

I'm working with some XML documents that have been handed to me by the client, and sometimes they aren't as "clean" as they should be. So I'm using XSLT 2.0 to clean them up before they move on to the next step.

One of the obstacles I'm running into is that empty/orphan rows in tables (" < r o w / > ") are breaking the processing that happens further downstream, and I'm not in a position to change that processing. So I need to get rid of those empty rows during the XSLT transformation.

Below is a shortened version of the XML that I'm working with ...

    &lt;table outputclass=&quot;OriginalTableClass&quot;&gt;
        &lt;tgroup cols=&quot;2&quot;&gt;
            &lt;tbody&gt;
                &lt;row&gt;
                    &lt;entry&gt;
                        &lt;p outputclass=&quot;TBL_Heading&quot;&gt;Heading&lt;/p&gt;
                    &lt;/entry&gt;
                    &lt;entry&gt;
                        &lt;p outputclass=&quot;TBL_Heading&quot;&gt;Heading&lt;/p&gt;
                    &lt;/entry&gt;
                &lt;/row&gt;
                &lt;row&gt;
                    &lt;entry morerows=&quot;1&quot;&gt;
                        &lt;p outputclass=&quot;TBL_Body_Text&quot;&gt;Content&lt;/p&gt;
                    &lt;/entry&gt;
                    &lt;entry&gt;
                        &lt;p outputclass=&quot;TBL_Body_Text&quot;&gt;Content&lt;/p&gt;
                    &lt;/entry&gt;
                &lt;/row&gt;
                &lt;row/&gt;
                &lt;row/&gt;
            &lt;/tbody&gt;
        &lt;/tgroup&gt;
    &lt;/table&gt;

So you'll see that those last two rows are empty. I need to get rid of those, and so I came up with this, which seemed to work fine ...

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;xsl:stylesheet xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;
    xmlns:xs=&quot;http://www.w3.org/2001/XMLSchema&quot;
    xmlns:x2w=&quot;http://www.orbistechnologies.com/xml2word&quot;
    exclude-result-prefixes=&quot;xs&quot;
    version=&quot;2.0&quot;&gt;
    
    &lt;xsl:template match=&quot;*&quot; mode=&quot;ABC&quot;&gt;
        &lt;xsl:copy&gt;
            &lt;xsl:copy-of select=&quot;@*&quot;/&gt;
            &lt;xsl:apply-templates mode=&quot;ABC&quot;/&gt;
        &lt;/xsl:copy&gt;
    &lt;/xsl:template&gt;
    
    &lt;!-- get rid of any orphaned rows --&gt;
    &lt;xsl:template match=&quot;tbody/row[not(child::*)]&quot; mode=&quot;ABC&quot;/&gt;

My logic here was that if a row doesn't have a child, get rid of it. And this worked perfectly fine ... until I later had to add a transformation to change the same table's class in cases where there's a header row. That looks like this ...

    &lt;!-- change the outputclass for tables that have a header row --&gt;
    &lt;xsl:template match=&quot;table[@outputclass=&#39;OriginalTableClass&#39;]&quot; mode=&quot;ABC&quot;&gt;
        &lt;xsl:copy&gt;
            &lt;xsl:element name=&quot;table&quot;&gt;
                &lt;xsl:choose&gt;
                    &lt;!-- does the table have a header row? --&gt;
                    &lt;xsl:when test=&quot;*/descendant::p[@outputclass=&#39;TBL_Heading&#39;]&quot;&gt;
                        &lt;!-- if yes, then assign the new class --&gt;
                        &lt;xsl:attribute name=&quot;outputclass&quot;&gt;NewTableClass&lt;/xsl:attribute&gt;
                    &lt;/xsl:when&gt;
                    &lt;xsl:otherwise&gt;
                        &lt;!-- if not, then let the current class ride --&gt;
                        &lt;xsl:attribute name=&quot;outputclass&quot;&gt;
                            &lt;xsl:value-of select=&quot;@outputclass&quot;/&gt;
                        &lt;/xsl:attribute&gt;
                    &lt;/xsl:otherwise&gt;
                &lt;/xsl:choose&gt;
                &lt;!-- copy all of the content inside the table --&gt;
                &lt;xsl:copy-of select=&quot;./node()&quot;/&gt;
            &lt;/xsl:element&gt;
        &lt;/xsl:copy&gt;
    &lt;/xsl:template&gt;

And this transformation also works perfectly fine.

However ... the two transformations don't play nice together. Meaning, if I include the second transformation, then the orphan rows persist. And I kind of understand why -- because I'm trying to transform the same content twice, and that's a no-no. But ... I'm still relatively new to XSLT and can't figure out how to make both things happen in the same table.

What I've tried to do so far is somehow include the first transformation (getting rid of the orphaned rows) inside of the second transformation. Because it seems like I should be able to do this at the point where the "copy-of" happens. But what I can't quite wrap my head around is how to make a change to the child/descendant element (those rows), instead of to the parent, while I'm matching against the parent element (the table). Or how to cycle through the table's child/descendant content when the number of levels and the structure a table may have can vary (meaning: it's not always table/tgroup/tbody/row).

I also tried giving the first transformation a higher priority value and also giving them different "mode" values. Although I suspected neither of these would work ... and they didn't.

What's the basic XSLT concept that I'm probably missing here? And what if I have to make another transformation to the same table? Are all elements inside the table off-limits once I apply a parent-level transformation like this?

Any help would be appreciated. Thanks!

答案1

得分: 0

使用 xsl:copy-of 时,您将绕过所有处理和转换以及匹配模板的能力。

在第二个XSLT中导入或包含第一个XSLT,并更改具有 &lt;xsl:copy-of select=&quot;./node()&quot;/&gt;table 模板,改为 &lt;xsl:apply-templates select=&quot;node()&quot;/&gt;

这将允许模板匹配发生,而对空的 row 的模板匹配将过滤掉内容。

英文:

When you use xsl:copy-of you are bypassing all of the processing and ability to transform and match templates.

Import or include the first XSLT in the second one and change the template for the table where you have &lt;xsl:copy-of select=&quot;./node()&quot;/&gt; and instead &lt;xsl:apply-templates select=&quot;node()&quot;/&gt;.

That will allow template matches to occur, and the template match on the empty row will filter out the content.

答案2

得分: 0

I think instead of

<!-- change the outputclass for tables that have a header row -->
<xsl:template match="table[@outputclass='OriginalTableClass']" mode="ABC">
    <xsl:copy>
        <xsl:element name="table">
            <xsl:choose>
                <!-- does the table have a header row? -->
                <xsl:when test="*/descendant::p[@outputclass='TBL_Heading']">
                    <!-- if yes, then assign the new class -->
                    <xsl:attribute name="outputclass">NewTableClass</xsl:attribute>
                </xsl:when>
                <xsl:otherwise>
                    <!-- if not, then let the current class ride -->
                    <xsl:attribute name="outputclass">
                        <xsl:value-of select="@outputclass"/>
                    </xsl:attribute>
                </xsl:otherwise>
            </xsl:choose>
            <!-- copy all of the content inside the table -->
            <xsl:copy-of select="./node()"/>
        </xsl:element>
    </xsl:copy>
</xsl:template>

you just want and need a template for the attribute change i.e.

<xsl:template match="table[@outputclass='OriginalTableClass'][*/descendant::p[@outputclass='TBL_Heading']]/@outputclass" mode="ABC">
  <xsl:attribute name="{name()}">NewTableClass</xsl:attribute>
</xsl:template>

if you also ensure attributes are processed and transformed and not simply copied through by changing

<xsl:template match="*" mode="ABC">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates mode="ABC"/>
    </xsl:copy>
</xsl:template>

to

<xsl:template match="* | @*" mode="ABC">
    <xsl:copy>
        <xsl:apply-templates select="@*" mode="ABC"/>
        <xsl:apply-templates mode="ABC"/>
    </xsl:copy>
</xsl:template>
英文:

I think instead of

&lt;!-- change the outputclass for tables that have a header row --&gt;
&lt;xsl:template match=&quot;table[@outputclass=&#39;OriginalTableClass&#39;]&quot; mode=&quot;ABC&quot;&gt;
    &lt;xsl:copy&gt;
        &lt;xsl:element name=&quot;table&quot;&gt;
            &lt;xsl:choose&gt;
                &lt;!-- does the table have a header row? --&gt;
                &lt;xsl:when test=&quot;*/descendant::p[@outputclass=&#39;TBL_Heading&#39;]&quot;&gt;
                    &lt;!-- if yes, then assign the new class --&gt;
                    &lt;xsl:attribute name=&quot;outputclass&quot;&gt;NewTableClass&lt;/xsl:attribute&gt;
                &lt;/xsl:when&gt;
                &lt;xsl:otherwise&gt;
                    &lt;!-- if not, then let the current class ride --&gt;
                    &lt;xsl:attribute name=&quot;outputclass&quot;&gt;
                        &lt;xsl:value-of select=&quot;@outputclass&quot;/&gt;
                    &lt;/xsl:attribute&gt;
                &lt;/xsl:otherwise&gt;
            &lt;/xsl:choose&gt;
            &lt;!-- copy all of the content inside the table --&gt;
            &lt;xsl:copy-of select=&quot;./node()&quot;/&gt;
        &lt;/xsl:element&gt;
    &lt;/xsl:copy&gt;
&lt;/xsl:template&gt;

you just want and need a template for the attribute change i.e.

&lt;xsl:template match=&quot;table[@outputclass=&#39;OriginalTableClass&#39;][*/descendant::p[@outputclass=&#39;TBL_Heading&#39;]]/@outputclass&quot; mode=&quot;ABC&quot;&gt;
  &lt;xsl:attribute name=&quot;{name()}&quot;&gt;NewTableClass&lt;/xsl:attribute&gt;
&lt;/xsl:template&gt;

if you also ensure attributes are processed and transformed and not simply copied through by changing

&lt;xsl:template match=&quot;*&quot; mode=&quot;ABC&quot;&gt;
    &lt;xsl:copy&gt;
        &lt;xsl:copy-of select=&quot;@*&quot;/&gt;
        &lt;xsl:apply-templates mode=&quot;ABC&quot;/&gt;
    &lt;/xsl:copy&gt;
&lt;/xsl:template&gt;

to

&lt;xsl:template match=&quot;* | @*&quot; mode=&quot;ABC&quot;&gt;
    &lt;xsl:copy&gt;
        &lt;xsl:apply-templates select=&quot;@*&quot; mode=&quot;ABC&quot;/&gt;
        &lt;xsl:apply-templates mode=&quot;ABC&quot;/&gt;
    &lt;/xsl:copy&gt;
&lt;/xsl:template&gt;

huangapple
  • 本文由 发表于 2023年5月24日 22:18:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324548.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定