XSLT - 如何将元素映射到名称值对?

时间:2014-02-14 23:24:03

标签: xml xslt map-force

我需要将XML从源格式转换为目标名称值对以进行通用处理。有关如何实现这一目标的任何提示吗?我试图使用MapForce,如果它更容易。

<products>
    <product>
        <type>Monitor</type>
        <size>22</size>
        <brand>EIZO</brand>
    </product>
    <product>
        ......
    </product>
</products>

<products>
    <product num="1">
        <attribute name="type">Monitor</attribute>
        <attribute name="size">22</attribute>
        <attribute name="brand">EIZO</attribute>
    </product>
    <product num="2">
        ....
    </product>
</products>

我认为我需要在元素中使用xsl:for-each来生成元素?

&#34; num&#34;属性,它基本上只是一个计数器。它可能是位置()?

非常感谢!!

5 个答案:

答案 0 :(得分:2)

对于这样的问题,您通常从构建XSLT标识模板开始

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

它自己按原样复制所有节点,这意味着您只需要为要转换的节点编写匹配的模板。

首先,您希望将 num 属性添加到产品,因此请使用与产品匹配的模板,您只需将其输出使用该属性并继续处理其子项。

<xsl:template match="product">
   <product num="{position()}">
      <xsl:apply-templates select="@*|node()"/>
   </product>
</xsl:template>

请注意在创建 num 属性时使用属性值模板。花括号表示要计算的表达式,而不是字面输出。

然后,您希望模板与产品元素的子元素匹配,并将这些元素转换为属性节点。这是通过一个模式来匹配任何这样的孩子,如此

<xsl:template match="product/*">
   <attribute name="{local-name()}">
      <xsl:apply-templates />
   </attribute>
</xsl:template>

请注意,如果您在子元素中只有文本节点,<xsl:apply-templates />可以替换为<xsl:value-of select="." />

试试这个XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="product">
      <product num="{position()}">
         <xsl:apply-templates select="@*|node()"/>
      </product>
   </xsl:template>

   <xsl:template match="product/*">
      <attribute name="{local-name()}">
         <xsl:apply-templates />
      </attribute>
   </xsl:template>
</xsl:stylesheet>

当应用于XML时,输出以下内容

<products>
  <product num="1">
    <attribute name="type">Monitor</attribute>
    <attribute name="size">22</attribute>
    <attribute name="brand">EIZO</attribute>
  </product>
  <product num="2">
        ......
  </product>
</products>

当然,如果确实想要将子元素转换为适当的属性,而不是名为“attribute”的元素,则可以使用 xsl:attribute 命令。用这个

替换最后一个模板
<xsl:template match="product/*">
   <xsl:attribute name="{local-name()}">
      <xsl:value-of select="." />
   </xsl:attribute>
</xsl:template>

当使用此模板时,输出以下内容(如果您的样本具有子元素,那么它将包含产品2)。

<products>
  <product num="1" type="Monitor" size="22" brand="EIZO"></product>
  <product num="2">
    ......
  </product>
</products>

答案 1 :(得分:0)

我想我有你想要的东西。不适用于每个条款。只是递归模板的神奇之处:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="products">
    <products>
        <xsl:call-template name="transformProducts">
            <xsl:with-param name="nodes" select="product"/>
        </xsl:call-template>
    </products>
</xsl:template>

<xsl:template name="transformProducts">
    <xsl:param name="nodes"/>
    <xsl:param name="counter" select="0"/>

    <xsl:choose>
        <xsl:when test="not($nodes)"></xsl:when>
        <xsl:otherwise>
            <product>
                <xsl:attribute name="num">
                    <xsl:value-of select="$counter + 1"/>
                </xsl:attribute>
                <xsl:attribute name="type">
                    <xsl:value-of select="$nodes[1]/type"/>
                </xsl:attribute>
                <xsl:attribute name="size">
                    <xsl:value-of select="$nodes[1]/size"/>
                </xsl:attribute>
                <xsl:attribute name="brand">
                    <xsl:value-of select="$nodes[1]/brand"/>
                </xsl:attribute>
            </product>
            <xsl:call-template name="transformProducts">
                <xsl:with-param name="nodes" select="$nodes[position() > 1]"/>
                <xsl:with-param name="counter" select="$counter + 1"/>
            </xsl:call-template>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

根据我的测试,这是起始XML:

<products>    
 <product>
    <type>Monitor</type>
    <size>22</size>
    <brand>EIZO</brand>
</product>
<product>
    <type>MonitorTwo</type>
    <size>32</size>
    <brand>SONY</brand>
</product>
<product>
    <type>what</type>
    <size>12</size>
    <brand>VGA</brand>
</product>
</products>

这是输出。我假设通过命名你的元素“属性”,你实际上意味着他们被转换为产品元素的属性节点?

<products>
 <product num="1" type="Monitor" size="22" brand="EIZO"/>
 <product num="2" type="MonitorTwo" size="32" brand="SONY"/>
 <product num="3" type="what" size="12" brand="VGA"/>
</products>

答案 2 :(得分:0)

当然,天堂禁止这应该简短,因为......为什么呢?

版本A(名为“属性”的元素):

<xsl:template match="/">
<products>
    <xsl:for-each select="products/product">
        <product num="{position()}">
           <xsl:for-each select="*">
             <attribute name="{name()}">
                <xsl:value-of select="."/>
             </attribute>
           </xsl:for-each>
        </product>
    </xsl:for-each>
</products>
</xsl:template>

版本B(真实属性):

<xsl:template match="/">
<products>
    <xsl:for-each select="products/product">
        <product num="{position()}">
           <xsl:for-each select="*">
             <xsl:attribute name="{name()}">
                <xsl:value-of select="."/>
             </xsl:attribute>
           </xsl:for-each>
        </product>
    </xsl:for-each>
</products>
</xsl:template>

答案 3 :(得分:0)

全心全意,

Tim和Phillips的方法都非常不同,但两者都很完美。我是否正确的思考

  • Tim的方法基于Source XML,然后使用模板调整指定的内容。如果我有100个元素并且只想调整一些,那么这是一个很好的元素。

  • Phillip方法正在从头开始构建一个新的XML,但是XPath-ing /提取出我想要的东西。说如果我有100个元素,我只想提取和转换一些输出,那么这是一个很好的。

非常感谢!

答案 4 :(得分:-1)

尚未对此进行测试,但您可以执行以下操作:

<products>
    <xsl:for-each select="//product">
        <product num="{position()}">
           <xsl:for-each select="current()/*">
             <attribute name="{name()}">
                <xsl:value-of select="self::node()/text()"/>
             </attribute>
           </xsl>
        </product>
    </xsl>
</products>