XSLT:没有属​​性且没有子元素转换为父属性的元素

时间:2017-06-14 20:24:40

标签: xml xslt xls

给定的.xml文件的结构,名称,值是未知的。

对于每个具有简单结构的非根元素(没有子节点,没有属性,BUT有文本且不为空),将其转换为父属性。

我有.xml文件:

<list>
   <worker>
      <name atr="ss">val1</name>
   </worker>
   <worker>
      <make1>val2</make1>
   </worker>
   <worker>
      <name>
        <make2>val3</make2>
      </name>
   </worker>
   <worker>
      <name>
        <doo atr="ss1">val4</doo>
        <make3></make3>
      </name>
   </worker>
</list>

我希望得到这个:

<list>
   <worker>
      <name atr="ss">val1</name>
   </worker>
   <worker make1="val2"/>
   <worker>
      <name make2="val3"/>
   </worker>
   <worker>
      <name>
        <doo atr="ss1">val4</doo>
        <make3/>
      </name>
   </worker>
</list>

这是我现在的.xsl(无法正常工作):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes" method="xml"/>
    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:template>
    <xsl:template match="//*[not(*|@*)]">
        <xsl:copy>
            <xsl:attribute name="{name()}">
                <xsl:value-of select="text()"/>
            </xsl:attribute>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

2 个答案:

答案 0 :(得分:2)

怎么样:

XSL 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="*[not(*|@*)]" mode="attribute"/>
        <xsl:apply-templates select="*[*|@*] | text()" />
    </xsl:copy>
</xsl:template>

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

</xsl:stylesheet>

请记住,必须在子元素之前创建属性。

为响应要求的变化而添加:

在某些时候,条件的数量足以证明只写一次它们,并且通过根据集合差异(即非交集)定义“其他”节点集,避免在负面重复它们: / p>

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:variable name="my-set" select="*[text() and not(*|@*)]" />
        <xsl:apply-templates select="$my-set" mode="attribute"/>
        <xsl:apply-templates select="node()[not(count(.|$my-set) = count($my-set)]" />
    </xsl:copy>
</xsl:template>

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

</xsl:stylesheet>

答案 1 :(得分:1)

您的代码

你有两个模板:

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

<xsl:template match="*[not(*) and not(@*)]">
    <xsl:copy>
        <xsl:attribute name="{name()}">
            <xsl:value-of select="text()"/>
        </xsl:attribute>
    </xsl:copy>
</xsl:template>

您的输出会生成<worker><make1 make1="val2"/></worker>而不是<worker make1="val2"/>。这是因为外部<worker>元素由顶部模板处理,顶部模板只复制它,然后传递子节点,由底部模板处理。

一种工作方法

以下适用于我,并且只使用一个模板。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output indent="yes"/>

    <xsl:strip-space elements="*"/>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <!-- Capture any child elements with no attributes and no children. -->
            <xsl:for-each select="*[not(@*) and not(*)]">
                <xsl:attribute name="{name()}">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:for-each>
            <!-- Apply templates to **only** those children that have either
                attributes or children of their own, and to text. -->
            <xsl:apply-templates select="*[@* or *]|text()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

关键区别:符合条件的任何元素 - 没有子元素,没有属性,只有文本 - 通过应用模板进行处理,而是处理在for-each循环中。因此,我们永远不会得到该元素的副本。

更新

我们现在有一个明确的规定,即缺少文本的空元素将作为独立元素保留。因此,对于如下所示的代码段,使用空EXTRA元素:

<worker>
    <name>
        <doo atr="ss1">val4</doo>
        <make3>val4</make3>
        <EXTRA></EXTRA>
    </name>
</worker>

...我们希望输出如下:

<worker>
    <name make3="val4">
        <doo atr="ss1">val4</doo>
        <EXTRA/>
    </name>
</worker>

...将EXTRA维护为独立元素,并且只对make3元素进行属性化。

这个XSL应该可以解决问题。这将重新编写上面代码中的select语句。

<xsl:template match="*">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <!-- Capture any child elements with no attributes and no children,
            and that also have text. -->
        <xsl:for-each select="*[not(@*) and not(*) and text()]">
            <xsl:attribute name="{name()}">
                <xsl:value-of select="."/>
            </xsl:attribute>
        </xsl:for-each>
        <!-- Apply templates to **only** those children that have no text, or
            that have attributes or children of their own, and also apply to text. -->
        <xsl:apply-templates select="*[@* or * or not(text())] | text()"/>
    </xsl:copy>
</xsl:template>