在 XSLT 3.0 中使用流的更好方法 [英] A better way to use streaming in XSLT 3.0

查看:43
本文介绍了在 XSLT 3.0 中使用流的更好方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用流式传输的文档具有如下结构

A document I am trying to transform using streaming have the structure as follows

<Document>
    <Header>
        <Number>23</Number>
        <Type>3</Type>
    </Header>
    <Lines>
        <Line>
            <LineNumber>1</LineNumber>
        </Line>
        <Line>
            <LineNumber>2</LineNumber>
        </Line>
    </Lines>
    <Summary>
        <Total>42</Total>
    </Summary>
</Document>

真正的输出应该有更复杂的结构,但目前我把它简化为只是有一个不同的命名

The real output should have more complex structure, but for the moment I simplified it to just having a different naming

<Transformed>
    <DocumentHeader>
        <DocumentNumber>23</DocumentNumber>
        <DocumentType>P</DocumentType>
    </DocumentHeader>
    <DocumentLines>
        <DocumentLine>
            <LineNumber>1</LineNumber>
        </DocumentLine>
        <DocumentLine>
            <LineNumber>2</LineNumber>
        </DocumentLine>
    </DocumentLines>
    <DocumentTotal>42</DocumentTotal>
</Transformed>

正如我所见,一种方法是为我需要处理的每个元素使用单独的模板.在这种情况下,所有模板都可以是 stremable,但似乎很难维护这样的实现.与上面的示例不同,现实生活中的文档将包含更多要处理的字段

As I could see one way to do it is to have separate templates for each element I need to process. All the templates could be stremable in this case, but it seems like it would be hard to maintain such an implementation. Unlike the sample above, a real-life document would contain many more fields to be processed

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">

    <xsl:mode streamable="yes"/>

    <xsl:template match="/Document">
        <xsl:element name="Transformed">
            <xsl:apply-templates select="*"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="/Document/Header">
        <xsl:element name="DocumentHeader">
            <xsl:apply-templates select="*"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="/Document/Header/Type">
        <xsl:element name="DocumentType">
            <xsl:value-of select="if (text()='3') then 'P' else 'K'"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="/Document/Header/Number">
        <xsl:element name="DocumentNumber">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="/Document/Lines">
        <xsl:element name="DocumentLines">
            <xsl:apply-templates select="*"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="/Document/Lines/Line">
        <xsl:element name="DocumentLine">
            <xsl:element name="LineNumber">
                <xsl:value-of select="LineNumber"/>
            </xsl:element>
        </xsl:element>
    </xsl:template>

    <xsl:template match="/Document/Summary">
        <xsl:element name="DocumentTotal">
            <xsl:value-of select="Total"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

另一个选项是所谓的突发模式,这是我尝试使用它,在我看来,当我测试元素名称以选择应使用哪种模式时,它看起来很难看

Another option is so-called burst-mode and here is my attempt to use it, in my opinion it looks ugly when I test an element name to choose which mode should be used

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">

    <xsl:mode streamable="yes"/>

    <xsl:template match="/">
        <xsl:element name="Transformed">
            <xsl:for-each select="Document/*">
                <xsl:choose>
                    <xsl:when test="self::Lines">
                        <xsl:apply-templates select="."/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:apply-templates select="copy-of(.)" mode="non-streamable"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

    <xsl:template match="Header" mode="non-streamable">
        <xsl:element name="DocumentHeader">
            <xsl:element name="DocumentNumber">
                <xsl:value-of select="Number"/>    
            </xsl:element>
            <xsl:if test="string-length(Type) > 0">
                <xsl:element name="DocumentType">
                    <xsl:value-of select="if (Type='3') then 'P' else 'K'"/>
                </xsl:element>
            </xsl:if>
        </xsl:element>
    </xsl:template>

    <xsl:template match="Lines">
        <xsl:element name="DocumentLines">
            <xsl:apply-templates select="copy-of(Line)" mode="non-streamable"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="Line" mode="non-streamable">
        <xsl:element name="DocumentLine">
            <xsl:element name="LineNumber">
                <xsl:value-of select="LineNumber"/>
            </xsl:element>
        </xsl:element>
    </xsl:template>

    <xsl:template match="Summary" mode="non-streamable">
        <xsl:element name="DocumentTotal">
            <xsl:value-of select="Total"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

所以我想知道是否可以以更愉快的方式完成?

So I am wonder if it could be done in a more pleasant way?

推荐答案

如果您需要重命名每个元素节点,那么通常的方法是为每个执行该微转换的元素编写一个模板.在任何情况下,我都会使用文字结果元素,例如

If you need to rename each element node then the usual way is to write a template for each element performing that micro-transformation. In any case I would use literal result elements e.g.

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

而不是 xsl:element.不清楚您想保存什么,或者您认为第一个示例的流式传输需要这些模板的位置,它似乎是适合您的转换任务的自然 XSLT 解决方案,无论是否使用流式传输.

and not xsl:element. It is not clear what you want to save or where you think that streaming for your first example requires those templates, it seems to be the natural XSLT solution for your transformation task, with or without streaming.

仅使用 xsl:element 就可以消除一些模板,例如

The only use of xsl:element would make sense to eliminate some templates with e.g.

<xsl:template match="Number | Lines | Line">
  <xsl:element name="Document{local-name()}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

但不清楚这种重命名模式是简化样本的一部分还是真正的要求.

but it is not clear whether that renaming pattern is part of a simplification of your sample or a real requirement.

在基于纯元素的模板匹配中您唯一不能做的更改是您执行的内容更改

The only change you can't do in a pure element based template matching is the content change you perform with

<xsl:template match="/Document/Header/Type">
    <xsl:element name="DocumentType">
        <xsl:value-of select="if (text()='3') then 'P' else 'K'"/>
    </xsl:element>
</xsl:template>

在这种情况下,您需要为 text() 子项添加模板,例如(使用 expand-text="yes" 到位)

in that case you would need to add a template for the text() child e.g. (with expand-text="yes" in place)

<xsl:template match="Document/Header/Type/text()">{if (. = 3) then 'P' else 'K'}</xsl:template>

否则我会很想使用

<xsl:template match="Document/Header/Type/text()[. = 3]">P</xsl:template>
<xsl:template match="Document/Header/Type/text()">K</xsl:template>

根据您的评论,您想缩短展示的第二个 XSLT 示例,正如我所说,我认为不需要任何 xsl:element 使用;至于xsl:choose,我想你可以写一个匹配的模板:

Based on your comments you want to shorten the second XSLT sample you have shown, as I said, I see no need for any xsl:element use; as for the xsl:choose, I think you can just write a matching template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">
    
    <xsl:strip-space elements="*"/>
    
    <xsl:output indent="yes"/>
    
    <xsl:mode streamable="yes"/>
    
    <xsl:template match="/*">
        <Transformed>
            <xsl:apply-templates/>
        </Transformed>
    </xsl:template>
    
    <xsl:template match="Document/*[not(self::Lines)]">
        <xsl:apply-templates select="copy-of()" mode="non-streamable"/>
    </xsl:template>
    
    <xsl:template match="Header" mode="non-streamable">
        <DocumentHeader>
            <DocumentNumber>
                <xsl:value-of select="Number"/>    
            </DocumentNumber>
            <xsl:if test="string-length(Type) > 0">
                <DocumentType>
                    <xsl:value-of select="if (Type='3') then 'P' else 'K'"/>
                </DocumentType>
            </xsl:if>
        </DocumentHeader>
    </xsl:template>
    
    <xsl:template match="Lines">
        <DocumentLines>
            <xsl:apply-templates select="Line!copy-of()" mode="non-streamable"/>
        </DocumentLines>
    </xsl:template>
    
    <xsl:template match="Line" mode="non-streamable">
        <Line>
            <LineNumber>
                <xsl:value-of select="LineNumber"/>
            </LineNumber>
        </Line>
    </xsl:template>
    
    <xsl:template match="Summary" mode="non-streamable">
        <DocumentTotal>
            <xsl:value-of select="Total"/>
        </DocumentTotal>
    </xsl:template>
    
</xsl:stylesheet>

这篇关于在 XSLT 3.0 中使用流的更好方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆