使用XSLT,当子元素包含nil true属性时删除父元素

时间:2016-02-24 16:19:38

标签: xml xslt xpath

我有一个xml数据文件。我还有一个转换该文件的xslt文件。这是我现在拥有的。原始的xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="file:///C:/Users/chvl/Desktop/New%20folder%20(2)/UntitledDS.xslt"?>
<L4 type="schema" guid="de2f8f94-8db6-4979-b32f-8db4bfac3f3c" versionNumber="16" DpaLength="4">
<L4 type="group">
    <FirmID type="item">
        <value>872</value>
    </FirmID>
    <FirmName type="item">
        <value>Generic Name Here</value>
    </FirmName>
    <FirmRef type="item">
        <value>LC</value>
    </FirmRef>
    <ReportingEndDate type="item">
        <value>2015-05-31</value>
    </ReportingEndDate>
    <L4 type="group">
        <_x0030_257 type="item">
            <value xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
        </_x0030_257>
        <_x0030_258 type="item">
            <value>1791886</value>
        </_x0030_258>
        <_x0030_725 type="item">
            <value xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
        </_x0030_725>
        <_x0030_726 type="item">
            <value>407897</value>
        </_x0030_726>
    </L4>
</L4>
</L4>

这是通过xslt

后的新xml文件
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="file:///C:/Users/chvl/Desktop/New%20folder%20(2)/UntitledDS.xslt"?>
<Root xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" name="L4" type="schema" guid="de2f8f94-8db6-4979-b32f-8db4bfac3f3c" versionNumber="16" DpaLength="4">
<Main name="L4" type="group">
    <FirmID type="item">
        <value>872</value>
    </FirmID>
    <FirmName type="item">
        <value>Generic Name Here</value>
    </FirmName>
    <FirmRef type="item">
        <value>LC</value>
    </FirmRef>
    <ReportingEndDate type="item">
        <value>2015-05-31</value>
    </ReportingEndDate>
    <Data name="L4" type="group">
        <DataItem dpa="_x0030_257">
            <value></value>
        </DataItem>
        <DataItem dpa="_x0030_258">
            <value>1791886</value>
        </DataItem>
        <DataItem dpa="_x0030_725">
            <value></value>
        </DataItem>
        <DataItem dpa="_x0030_726">
            <value>407897</value>
        </DataItem>
    </Data>
</Main>
</Root>

在原始的xml中,您会看到子元素,其中xsi:nil =&#34; true&#34;?好吧,当发生这种情况时,我需要删除它的父元素。在此示例中,我需要删除&lt; _x0030_257 type =&#34; item&#34;&gt;和&lt; _x0030_725 type =&#34; item&#34;&gt;及其所有子要素。

现在,我尝试过将此代码放在XSLT的末尾。

<xsl:template match="/*/*/*/node()[./*/(@xsi:nil = 'true')]|@*">/xsl:template>

它可以工作,除了它还从我的根元素以及其他地方删除属性。但我需要所有这些属性。我觉得我离解决这个问题只有一步之遥,但这一步骤正在成为一次巨大的飞跃。

任何帮助都将不胜感激。

以下是带有代码段代码的完整XSLT,用于删除nil父元素(上面的示例未使用代码段运行以删除nils)。

    <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>


    <xsl:template match="/*">
    <Root>
       <xsl:attribute name="name"  >
                     <xsl:value-of select="name(.)"/>
       </xsl:attribute>

        <xsl:choose>
                <xsl:when test="/*[type='schema']"> 
                    <xsl:apply-templates select="ignore"/>
             </xsl:when>
             <xsl:otherwise>
                <xsl:apply-templates select="@*|node()"/>
             </xsl:otherwise>
        </xsl:choose>
    </Root>
</xsl:template>

    <xsl:template match="/*/*[@type='group']">

   <Main>
       <xsl:attribute name="name"  >
                     <xsl:value-of select="name(.)"/>
       </xsl:attribute>

        <xsl:choose>
                <xsl:when test="child::*[type='item']">
                    <xsl:apply-templates select="ignore"/>
             </xsl:when>
             <xsl:otherwise>
                <xsl:apply-templates select="@*|node()"/>
             </xsl:otherwise>
        </xsl:choose>
    </Main>
</xsl:template>


    <xsl:template match="/*/*/*[@type='group']">
   <Data>
       <xsl:attribute name="name"  >
                     <xsl:value-of select="name(.)"/>
       </xsl:attribute>     
        <xsl:choose>
                <xsl:when test="child::*[type='item']">
                    <xsl:apply-templates select="ignore"/>
             </xsl:when>
             <xsl:otherwise>
                <xsl:apply-templates select="@*|node()"/>
             </xsl:otherwise>
        </xsl:choose>
    </Data>
</xsl:template>

    <xsl:template match="/*/*/*[@type='list']">
   <Cube>
       <xsl:attribute name="name"  >
                     <xsl:value-of select="name(.)"/>
       </xsl:attribute>     
        <xsl:choose>
                <xsl:when test="child::*[type='item']">
                    <xsl:apply-templates select="ignore"/>
             </xsl:when>
             <xsl:otherwise>
                <xsl:apply-templates select="@*|node()"/>
             </xsl:otherwise>
        </xsl:choose>
    </Cube>
</xsl:template>

    <xsl:template match="/*/*/Filters[@type='group']"/>


    <xsl:template match="/*/*/*/*[@type='group']">
   <xsl:variable name="count">
        <xsl:number/>
   </xsl:variable>   
   <Data grpcnt="{$count}">
       <xsl:attribute name="name"  >
                     <xsl:value-of select="name(.)"/>
       </xsl:attribute>     
        <xsl:choose>
                <xsl:when test="child::*[type='item']">
                    <xsl:apply-templates select="ignore"/>
             </xsl:when>
             <xsl:otherwise>
                <xsl:apply-templates select="@*|node()"/>
             </xsl:otherwise>
        </xsl:choose>
    </Data>
</xsl:template>

    <xsl:template match="*/*/*/*[@type='item']">
   <DataItem>
        <xsl:attribute name="dpa"  >
                   <xsl:value-of select="name(.)"/>
        </xsl:attribute>
        <value><xsl:value-of select="value"/></value>
    </DataItem>
</xsl:template>

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

    <xsl:template match="/*/*/*/node()[./*/(@xsi:nil = 'true')]|@*">

</xsl:template>



</xsl:stylesheet>

PS:请记住,元素名称改变了我使用星号而不是硬编码标签名称的原因。

1 个答案:

答案 0 :(得分:2)

我认为只从最后一个模板的模板匹配中删除|@*可能会解决问题:

<xsl:template match="/*/*/*/node()[./*/(@xsi:nil = 'true')]">
  

它可以工作,除了它还从我的根元素以及其他地方删除属性。

那是因为在你的原始表达中:

/*/*/*/node()[./*/(@xsi:nil = 'true')] | @*

|或“union”表示以下内容:|左侧的所有内容或其右侧的所有内容(假设只有一个此类符号)。

因此,表达式意味着:在文档中的任何位置具有真实nil属性 OR 任何属性的节点。

既然你在问题中提到了“humongous”,那么你的表达是:

/*/*/*/node()[./*/(@xsi:nil = 'true')]

也非常棒。如果嵌套级别非常重要,并且您希望在其他位置保留元素/*/*/*/,则仅使用nil="true"

如果嵌套不重要,最终会以

结束
node()[./*/(@xsi:nil = 'true')]

然后,node()过于笼统,因为它是对任何类型节点的测试,而不仅仅是元素节点。使用*代替引用元素:

*[./*/(@xsi:nil = 'true')]

在谓词([...])中,没有必要以./开头,因为无论如何都隐含地假设了这个上下文:

 *[*/(@xsi:nil = 'true')

不需要括号:

*[*/@xsi:nil = 'true']

如果xsi:nil属性仅属于,当其值为“true”时,您甚至可以写

*[*/@xsi:nil]

关于这个表达式的最后一句话:我猜你在表达式中包含|@*,因为你还想排除这些元素的所有属性,对吧?

,不要担心
<xsl:template match="*[*/@xsi:nil]"/>

不会处理这些元素的任何属性或内容。

正如用户wero指出的那样,您还需要在XSLT样式表中声明XSI名称空间以使其正常工作:

<xsl:stylesheet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

更一般的建议:似乎你从文档节点开始所有模板匹配,使模式绝对。如果您是故意这样做的话,这很好,但它也限制了XSLT样式表的多功能性。