【问题标题】:XSLT moving nodes into a listXSLT 将节点移动到列表中
【发布时间】:2014-10-15 11:36:59
【问题描述】:

我是 Xslt 新手,遇到以下问题

<root>
<parentnode>
<childnode1>value1</childnode1>
<childnode2>value2</childnode2>
<childnode3>value3</childnode3>
<childnode4>value4</childnode4>
<childnodelist>
</childnodelist>
</parentnode>
</root>

我想要的输出:

如果 childnode3 或 childnode4 中有值,我需要将值移动到 childnodelist 节点中,然后删除原始节点,使其显示如下:

<root>
<ParentNode>
<childnode1>value1</childnode1>
<childnode2>value2</childnode1>
<childnodelist>
<Value name="childnode3">value3</Value>
<Value name="childnode4">Value4</Value>
</childnodelist>
</parentnode>
</root>

我当前的 xslt 有以下代码,但我不确定如何测试节点中是否有值,如果有,如何创建新节点。我想尽量避免使用“xsl:if”,因为我已经读过它不是最佳实践。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    <xsl:if test="CommercialDataOutput\StructuralVariable\Spare1">

  <!--Unsure what do do here-->

<xsl:template match='childnode3|childnode4'/>

【问题讨论】:

  • 您的节点实际上是否在其名称中包含计数,或者这只是一个示例? (节点名称中的计数器总是让一切变得更加困难。)
  • 没有计数器。这些数字仅用于示例目的。
  • 那么我会要求您从样品中删除计数器,它们没有任何好处。我的建议是尽可能接近您的实际输入。 (这也导致了进一步的问题:应该包装哪个子节点?最后两个?那些在第二个之后?)
  • 您的示例也不是格式良好的 XML。请在你处理它的时候更正它。
  • @Tomalak 感谢您指出 xml 中的错误,我已经更改了它们。我不打算更改 xml 标签中的数字,我同意你的观点,但由于已经有答案,现在这会让事情变得更加混乱!

标签: c# .net xml xslt xslt-1.0


【解决方案1】:

我认为你只是需要

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


<xsl:template match="parentNode[normalize-space(foo) or normalize-space(bar)]">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()[not(self::foo | self::bar)]"/>
    <list>
      <xsl:apply-templates select="foo | bar" mode="wrap"/>
    </list>
  </xsl:copy>
</xsl:template>

<xsl:template match="foo | bar" mode="wrap">
  <value name="{local-name()}">
    <xsl:apply-templates/>
  </value>
</xsl:template>

其中foobar 是您想要检查和转换的元素(如果有内容)。

【讨论】:

  • 谢谢马丁。这个解决方案很棒,但是当 foo 或 bar 没有值时,仍然会产生一个空元素。是否可以只在元素有值时才创建,或者在创建后将其删除?
  • @RichardWatts,match="parentNode[normalize-space(foo) or normalize-space(bar)]" 应确保整个模板仅用于和执行至少具有 foo 或带有某些内容的 bar 元素的 parentNode 元素,这样list 元素的添加也仅限于这种情况。如果您不想要空的value name="foo",请将&lt;xsl:apply-templates select="foo | bar" mode="wrap"/&gt; 更改为&lt;xsl:apply-templates select="foo[normalize-space()] | bar[normalize-space()]" mode="wrap"/&gt;
  • 非常感谢您的帮助,它现在完全符合我的需要
【解决方案2】:

解决此问题的一种方法是像这样覆盖身份模板:

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

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

    <xsl:template match="childnodelist[not('' = (../childnode3|../childnode4))]">
        <xsl:copy>
            <xsl:copy-of select="../childnode3|../childnode4"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="childnode3[. != '']" />
    <xsl:template match="childnode4[. != '']" />
</xsl:stylesheet>

您的(已清理的)输入样本的结果:

<root>
  <parentnode>
    <childnode1>value1</childnode1>
    <childnode2>value2</childnode2>
    <childnodelist>
      <childnode3>value3</childnode3>
      <childnode4>value4</childnode4>
    </childnodelist>
  </parentnode>
</root>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-05
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多