XSLT:没有属性且没有子元素转换为父属性的元素
XSLT: element that has no attributes and no children transform to parent attribute
给定的 .xml 文件的结构、名称和值未知。
对于每个具有简单结构(没有子节点,没有属性,但有文本且不为空)的非根元素,将其转换为父元素的属性。
我有 .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>
怎么样:
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>
请记住,属性必须在子元素之前创建。
为响应需求变化而添加:
在某些时候,条件的数量足以证明只写一次,并通过根据集合差异(即非-交集):
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>
您的代码
您有两个模板:
<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>
给定的 .xml 文件的结构、名称和值未知。
对于每个具有简单结构(没有子节点,没有属性,但有文本且不为空)的非根元素,将其转换为父元素的属性。
我有 .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>
怎么样:
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>
请记住,属性必须在子元素之前创建。
为响应需求变化而添加:
在某些时候,条件的数量足以证明只写一次,并通过根据集合差异(即非-交集):
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>
您的代码
您有两个模板:
<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>