XSL使所有叶节点以小写字母'标签属性开头

时间:2013-04-28 18:09:55

标签: xml xslt-1.0 xslt-2.0

我正在尝试进行XSL转换,将所有以小写字母'[a-z]'开头的叶标记转换为父标记的属性。一切正常,直到叶标签在标签顺序中真正落下。 e.g。

请参阅下面的xml。转换会跳过'sessionId'标记,因为它放在最后;但是如果我把它放在'PaginationData'标签之前,转换就会正常工作。

Q值。是否可以纠正XSL,使其不考虑标签排序。

Q值。有人可以解释为什么标签顺序如'sessionId'标签,问题存在吗?

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="Carz.com">
<SOAP-ENV:Body>
    <ns1:getCarValuedAvail>
        <CarValuedAvailRQ>
            <PaginationData>
                <pageNumber>1</pageNumber>
                <itemsPerPage/>
            </PaginationData>
            <BuyDate>
                <date>20130509</date>
                <time/>
            </BuyDate>
            <SellDate>
                <date>20130511</date>
                <time/>
            </SellDate>
            <Destination>
                <Name/>
                <ZoneList/>
                <type>SIMPLE</type>
                <code>PMI</code>
            </Destination>
            <OccupancyList>
                <CarOccupancy>
                    <SpaceCount>1</SpaceCount>
                    <Occupancy/>
                    <ServiceOccupancy>
                        <AdultCount>1</AdultCount>
                        <ChildCount>0</ChildCount>
                        <GuestList/>
                    </ServiceOccupancy>
                </CarOccupancy>
            </OccupancyList>
            <CarCodeList/>
            <CategoryList/>
            <BoardList/>
            <ShowDirectPayment/>
            <ShowNetPrice/>
            <Credentials>
                <User>xyz</User>
                <Password>xyz</Password>
            </Credentials>
            <sessionId>DummySessionId</sessionId>
        </CarValuedAvailRQ>
    </ns1:getCarValuedAvail>
</SOAP-ENV:Body>

请参阅下面的XSL -

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

 <xsl:template match="//*[not(*)][contains('abcdefghijklmnopqrstuvwxyz',substring(local-name(),0,2))]">
    <xsl:attribute name="{name()}">
        <xsl:value-of select="text()"/>
    </xsl:attribute>
</xsl:template>

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

预期输出:如您所见,所有以小(非大写)字符开头的标记都已转换为其父标记的属性。

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="Carz.com">
<SOAP-ENV:Body>
    <ns1:getCarValuedAvail sessionId="DummySessionId">
        <CarValuedAvailRQ>
            <PaginationData pageNumber="1" itemsPerPage=""/>
            <BuyDate date="20130509" time=""/>
            <SellDate date="20130511" time=""/>
            <Destination type="Simple" code="PMI">
                <Name/>
                <ZoneList/>
            </Destination>
            <OccupancyList>
                <CarOccupancy>
                    <SpaceCount>1</SpaceCount>
                    <Occupancy/>
                    <ServiceOccupancy>
                        <AdultCount>1</AdultCount>
                        <ChildCount>0</ChildCount>
                        <GuestList/>
                    </ServiceOccupancy>
                </CarOccupancy>
            </OccupancyList>
            <CarCodeList/>
            <CategoryList/>
            <BoardList/>
            <ShowDirectPayment/>
            <ShowNetPrice/>
            <Credentials>
                <User>xyz</User>
                <Password>xyz</Password>
            </Credentials>
        </CarValuedAvailRQ>
    </ns1:getCarValuedAvail>
</SOAP-ENV:Body>

1 个答案:

答案 0 :(得分:1)

我认为您的方法存在一个潜在问题,即规范要求必须在创建任何子节点之前创建结果树中的任何属性节点;如果在创建子节点后尝试创建属性节点,则处理器需要发出错误信号或丢弃该属性。

所以基本上你需要稍微改变一下方法,以确保在其他子元素之前处理要转换为属性的任何元素。正如您已将问题标记为XSLT 2.0,我发布了一个XSLT 2.0解决方案:

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

<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* , node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="*[*[not(*) and matches(local-name(), '^[a-z]')]]">
  <xsl:copy>
    <xsl:apply-templates select="@* , *[not(*) and matches(local-name(), '^[a-z]')]"/>
    <xsl:apply-templates select="node() except *[not(*) and matches(local-name(), '^[a-z]')]"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="*[not(*) and matches(local-name(), '^[a-z]')]">
  <xsl:attribute name="{name()}" select="."/>
</xsl:template>

</xsl:stylesheet>

如果您需要使用XSLT 1.0解决它,那么您可以按如下方式解决:

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

<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="*[*[not(*) and contains('abcdefghijklmnopqrstuvwxyz', substring(local-name(), 1, 1))]]">
  <xsl:copy>
    <xsl:apply-templates select="@* | *[not(*) and contains('abcdefghijklmnopqrstuvwxyz', substring(local-name(), 1, 1))]"/>
    <xsl:apply-templates select="node()[not(self::*[not(*) 
                                        and 
                                        contains('abcdefghijklmnopqrstuvwxyz', substring(local-name(), 1, 1))])]"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="*[not(*) and contains('abcdefghijklmnopqrstuvwxyz', substring(local-name(), 1, 1))]">
  <xsl:attribute name="{name()}">
    <xsl:value-of select="."/>
  </xsl:attribute>
</xsl:template>

</xsl:stylesheet>