Лучший способ использовать потоковую передачу в XSLT 3.0 - PullRequest
0 голосов
/ 11 июля 2020

Документ, который я пытаюсь преобразовать с помощью потоковой передачи, имеет следующую структуру:

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

Реальный вывод должен иметь более сложную структуру, но на данный момент я упростил его до другого имени

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

Как я мог видеть, один из способов сделать это - иметь отдельные шаблоны для каждого элемента, который мне нужно обработать. В этом случае все шаблоны могут быть потоковыми, но, похоже, будет сложно поддерживать такую ​​реализацию. В отличие от приведенного выше примера, реальный документ будет содержать гораздо больше полей для обработки

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

Другой вариант - это так называемый пакетный режим, и вот моя попытка его использовать, на мой взгляд, это выглядит уродливо, когда я проверяю имя элемента, чтобы выбрать, какой режим следует использовать

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

Поэтому мне интересно, можно ли это сделать более приятным способом?

1 Ответ

0 голосов
/ 11 июля 2020

Если вам нужно переименовать каждый узел элемента, то обычный способ - написать шаблон для каждого элемента, выполняющего это микропреобразование. В любом случае я бы использовал буквальные элементы результата, например

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

, а не xsl:element. Непонятно, что вы хотите сохранить или где, по вашему мнению, для потоковой передачи для вашего первого примера требуются эти шаблоны, это кажется естественным решением XSLT для вашей задачи преобразования, с потоковой передачей или без нее.

Единственное использование из xsl:element имеет смысл исключить некоторые шаблоны, например,

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

, в этом случае вам нужно будет добавить шаблон для дочернего элемента text(), например (с expand-text="yes" на месте)

<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, я думаю, вы можете просто написать соответствующий шаблон:

<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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...