Проверка существования дочернего узла и применение шаблонов

#xslt

#xslt

Вопрос:

У меня есть следующая упрощенная структура XML:

 <?xml version="1.0" encoding="UTF-8"?>
<ExportData>
<TransportHeader>
    <Timestamp>2011-01-16 06:00:33</Timestamp>
    <From>
        <Name>DynamicExport</Name>
        <Version>1.</Version>
    </From>
    <MessageId>d7b5c5b69a83</MessageId>
</TransportHeader>
<ExportConfig>
    <DateTimeFormat>yyyy-MM-dd HH:mm:ss</DateTimeFormat>
    <DecimalSymbol>.</DecimalSymbol>
</ExportConfig>
<DataSet> 
    <Tables>
        <Table>
            <RH>...</RH>
            <Rows>
                <R>Data1</R>
                <R>Data2</R>
                <R>Data3</R>
                <R>Data4</R>
                <R>Data5</R>
            </Rows>
        </Table>
    </Tables>
</DataSet>
</ExportData>
  

Я должен проверить, существуют <R> элементы или нет. Если никаких <R> элементов не существует, сопоставление должно быть прервано, в противном случае необходимо создать <Line> элемент для <R> каждого.

Я придумал это решение, которое пока работает отлично:

 <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="ISO-8859-1" method="xml" indent="yes" />

<!-- suppress nodes that are not matched -->
<xsl:template match="text() | @*">
    <xsl:apply-templates select="text() | @*"/>
</xsl:template>

<xsl:template match="/">
    <xsl:choose>
        <xsl:when test="not(ExportData/DataSet/Tables/Table/Rows/node())">
            <xsl:message terminate="yes">No line items</xsl:message>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="/ExportData/DataSet/Tables/Table/Rows">
    <INVOIC02>
        <!-- apply LINE ITEMS template -->
        <xsl:apply-templates select="R"/>
    </INVOIC02>
</xsl:template>

<!-- Template creating LINE ITEMS -->
<xsl:template match="R">
    
    <Line>
        <elements></elements>
    </Line>
</xsl:template>


</xsl:stylesheet>
  

Если есть <R> элементы, результат будет таким:

 <?xml version="1.0" encoding="ISO-8859-1"?>
<INVOIC02>
<Line>
    <elements/>
</Line>
<Line>
    <elements/>
</Line>
<Line>
    <elements/>
</Line>
<Line>
    <elements/>
</Line>
<Line>
    <elements/>
</Line>
</INVOIC02>
  

Если есть только <Rows/> и нет <R> s, сопоставление прерывается.

Теперь у меня есть два вопроса:

-Надежен ли мой тест на <R> элементы: test="not(ExportData/DataSet/Tables/Table/Rows/node())" ?

-Я использую <xsl:apply-templates> для создания <Line> элементов вместо <xsl:for-each> конструкции. Мои выражения XPath в порядке или я мог бы сделать их лучше?

Ответ №1:

Надежен ли мой тест для элементов: test=»not(exportData / DataSet / Tables / Таблица / Строки / узел())»?

Ну, вы хотите, чтобы он завершался сбоем, если в нем нет R элементов, или завершался сбоем, если Rows у него нет дочернего узла node() , который включал бы любой элемент (не только R ), text() , comment() или processing-instruction() ?

Если вы действительно хотите убедиться, что существует хотя бы один R элемент, который является дочерним для Rows , вам следует скорректировать критерии тестирования, чтобы они были более конкретными:

 test="not(ExportData/DataSet/Tables/Table/Rows/R)"
  

В противном случае он может пройти этот тест и продолжить обработку, а не сгенерировать нужный вам контент.

 I am using <xsl:apply-templates> to create the <Line> items instead of an 
<xsl:for-each> construct. 
Are my XPath expressions okay or could I make them better?
  

Вы могли бы избавиться от <xsl:if> условной логики внутри вашего шаблона для корневого узла и переместить эту логику в шаблон для, Rows который не содержит R дочерних элементов. Включение логики в xsl:template @match критерии упрощает оптимизацию процессоров XSLT, что может привести к увеличению производительности.

 <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output encoding="ISO-8859-1" method="xml" indent="yes" />

    <!-- suppress nodes that are not matched -->
    <xsl:template match="text() | @*">
        <xsl:apply-templates select="text() | @*"/>
    </xsl:template>

    <!--All Rows must contain an R.  
        If we encounter any that do not, terminate the transform -->
    <xsl:template match="/ExportData/DataSet/Tables/Table/Rows[not(R)]">
        <xsl:message terminate="yes">No line items</xsl:message>
    </xsl:template>

    <!--match for Rows that have R children -->
    <xsl:template match="/ExportData/DataSet/Tables/Table/Rows[R]">
        <INVOIC02>
            <!-- apply LINE ITEMS template -->
            <xsl:apply-templates select="R"/>
        </INVOIC02>
    </xsl:template>

    <!-- Template creating LINE ITEMS -->
    <xsl:template match="R">      
        <Line>
            <elements></elements>
        </Line>
    </xsl:template>

</xsl:stylesheet>
  

Ответ №2:

Это проверит, имеет ли R-узел дочерний элемент:

 <xsl:if test="R">
   <!--What you want to do here-->
</xsl:if>