XSLT:将带有没有孩子的孩子的节点转换为属性? [英] XSLT: Converting a node with a child who doesn't have a child into an attribute?
问题描述
我需要生成一个 XML 文档以与第 3 方供应商集成.他们的范例是任何字符串元素都被设置为属性,而任何复杂对象都是具有相同规则的单独节点.
I have an XML document I need to generate to integrate with a 3rd party vendor. Their paradigm is that any string elements are set as attributes, while any complex objects are separate nodes with the same rule.
我正在尝试使用 XSLT 将此对象的序列化表示转换为基于其 .dtd 的文档格式.不幸的是,我无法使用 [XmlAttribute] 装饰这些特定元素,因为它们的值可能为 null(我在发送请求之前使用 XSLT 删除这些节点).
I'm trying to use XSLT to convert our serialized representation of this object into their document format based on their .dtd. Unfortunately, I can't decorate these particular elements with [XmlAttribute] because their value could be null (I'm using XSLT to remove these nodes before sending the request).
基本上,我试图在 XSLT 中找出如何执行以下操作:对于我元素的每个子元素,如果该子元素没有子元素,则将该元素转换为我元素上的属性."
Basically, I'm trying to figure out in XSLT how to do the following: "For each child of my element, if that child has no children, convert that element to a property on my element."
我正在使用以下代码片段,我发现它似乎可以满足我的需要:
I'm working with the following snippet I've found that appears to do what I need:
<!-- Match elements that are parents -->
<xsl:template match="*[*]">
<xsl:choose>
<!-- Only convert children if this element has no attributes -->
<!-- of its own -->
<xsl:when test="not(@*)">
<xsl:copy>
<!-- Convert children to attributes if the child has -->
<!-- no children or attributes and has a unique name -->
<!-- amoung its siblings -->
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="not(*) and not(@*) and
not(preceding-sibling::*[name( ) =
name(current( ))])
and
not(following-sibling::*[name( ) =
name(current( ))])">
<xsl:attribute name="{local-name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
然而,这个错误:
Exception::System.Xml.Xsl.XslTransformException:属性和命名空间节点不能添加到文本后的父元素,已添加注释、pi 或子元素节点.
Exception::System.Xml.Xsl.XslTransformException: Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added.
我的 XSLT 技能有点弱.任何帮助将不胜感激!
My XSLT skills are a bit weak. Any help would be appreciated!
有关详细信息,请说文档如下所示:
For more info, say the doc looks like this:
<Root>
<type>Foo</type>
<includeHTML>Yes</includeHTML>
<SubRoot>
<SubSubRoot>
<ID>2.4</ID>
</SubSubRoot>
</SubRoot>
</Root>
我希望结果如下所示:
<Root type="foo" includeHTML="Yes">
<SubRoot>
<SubSubRoot ID="2.4" />
</SubRoot>
</Root>
编辑 我仅使用 .NET 4.5.1、XSLT 1.0.哦!
推荐答案
我不完全确定您要问什么,但我会尝试一下.
I am not entirely sure what you are asking, but I'll give it a try.
这个样式表假设只包含文本节点的元素被归入父元素的一个属性——即使文本节点在某种意义上是元素的子元素.
This stylesheet assumes that elements containing only text nodes are subsumed as an attribute of the parent element - even if text nodes are children of an element, in a sense.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*|*[*]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:choose>
<xsl:when test="*[not(./*) and not(./@*)]">
<xsl:for-each select="*[not(./*) and not(./@*)]">
<xsl:attribute name="{current()/local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
<xsl:apply-templates select="*[*]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(./*) and not(./@*)]"/>
<xsl:template match="@*|text()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
应用于示例输入:
<?xml version="1.0" encoding="utf-8"?>
<root>
<car nr="1">
<color>blue</color>
<speed>210</speed>
</car>
<car nr="2">
<color>red</color>
<speed>100</speed>
</car>
</root>
您得到以下结果:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<car nr="1" color="blue" speed="210"/>
<car nr="2" color="red" speed="100"/>
</root>
编辑:应用于您更新的输入,我得到:
EDIT: Applied to your updated input, I get:
<?xml version="1.0" encoding="UTF-8"?>
<Root type="Foo" includeHTML="Yes">
<SubRoot>
<SubSubRoot ID="2.4"/>
</SubRoot>
</Root>
这篇关于XSLT:将带有没有孩子的孩子的节点转换为属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!