XSLT:将带有没有孩子的孩子的节点转换为属性? [英] XSLT: Converting a node with a child who doesn't have a child into an attribute?

查看:20
本文介绍了XSLT:将带有没有孩子的孩子的节点转换为属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要生成一个 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆