【问题标题】:XSLT : transform flat XML to hierarchic XMLXSLT:将平面 XML 转换为分层 XML
【发布时间】:2018-07-14 14:24:50
【问题描述】:

我是 xml 新手,我很难理解 XSLT 的工作原理。 你能帮我修复我的 xslt 文件中的一些错误吗? 我想转换这个输入文件:

<?xml version="1.0" encoding="utf-8"?>
<data>
    <parent><string >AAA</string></parent>
    <nb><string >2</string></nb>
    <child1>aaa-1</child1>
    <child1>aaa-2</child1>
    <parent><string >BBB</string></parent>
    <nb><string>1</string></nb>
    <child2>bbb-1</child2>
    <parent><string >CCC</string></parent>
    <nb><string >0</string></nb>
</data>

进入:

<?xml version="1.0" encoding="utf-8"?>
<data>
    <parent>
        <string >AAA</string>
        <nb><string >2</string></nb>
        <child1>aaa-1</child1>
        <child1>aaa-2</child1></parent>
    <parent>
        <string >BBB</string>
        <nb><string >1</string></nb>
        <child2>bbb-1</child2></parent>
    <parent>
        <string >CCC</string>
        <nb><string >0</string></nb></parent>
</data>

规则是:

  1. “nb”节点表示每个父节点的子节点数。它可以 为 0。

  2. 节点“child1”和“child2”不同。它们很复杂, 带有嵌套循环等。我上面的输入文件为演示而简化。
    我 想想,我必须使用“复制”指令。

  3. 对我来说困难的是:

    • 对于每个父节点,我必须读取当前父节点之后定义的子节点数(“nb”)
    • 当“父”值为“AAA”时,我必须读取“child1”
      当“父”值为 !=AAAA 时,我必须读取“childe2”节点。李>

这是我的 XSLT 文件,结果并不完全符合预期:

    <?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:template match="/">
        <xsl:comment>--- </xsl:comment>
        <xsl:comment>1 : parent nodes </xsl:comment>
        <xsl:for-each select ="/data/parent">
            <p> 
                <string>
                    <xsl:value-of select="string"/>
                </string>
                <xsl:comment>======================= </xsl:comment>
                <xsl:comment>2 : nb nodes (how many childs for a parent ) </xsl:comment>
                <xsl:for-each select ="/data/nb">
                    <xsl:if test="((position() &lt; 2) and (normalize-space(position() &gt;= 1)))">
                        <xsl:comment>Ex. for tThe first value only </xsl:comment>
                        <xsl:comment>How to do a dynamic test here (expected : AAA->3 (first nb value), BBB->1 (second nb value) ...)  ?</xsl:comment>
                        <xsl:comment>How to synchronise loop on parent and nb  ?</xsl:comment>
                        <nb>
                            <string>
                                <xsl:value-of select="string"/>
                            </string>
                        </nb>
                    </xsl:if>
                </xsl:for-each>
                <xsl:comment>======================= </xsl:comment>
                <xsl:comment>3 : child nodes </xsl:comment>
                <xsl:comment>How to manage the position and number of nodes to read ?</xsl:comment>
                <xsl:comment>Test 'string =AAA' is KO : always child2 </xsl:comment>
                <xsl:choose>
                    <xsl:when test='string =AAA'>
                        <xsl:copy-of select="/*/child1" />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="/*/child2" />
                    </xsl:otherwise>
                </xsl:choose>               
            </p>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

提前致谢
最好的问候

【问题讨论】:

  • 对不起,已经完成了

标签: xml xslt


【解决方案1】:

如果数据是一致的,也就是说,如果nb 元素指示的child1/child2 元素的数量和parent 值要求,那么您可以简单地处理不同类型的parent 元素

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0">

  <xsl:output indent="yes"/>

  <xsl:template match="data">
      <xsl:copy>
          <xsl:apply-templates select="parent"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="parent[string = 'AAA']">
      <xsl:copy>
          <xsl:variable name="n" select="following-sibling::nb[1]"/>
          <xsl:copy-of select="string, following-sibling::nb[1], following-sibling::child1[position() &lt;= $n]"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="parent[string != 'AAA']">
      <xsl:copy>
          <xsl:variable name="n" select="following-sibling::nb[1]"/>
          <xsl:copy-of select="string, following-sibling::nb[1], following-sibling::child2[position() &lt;= $n]"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/94hvTzk/0 有该示例,您也可以在 XSLT 1 中这样做

<?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:template match="data">
      <xsl:copy>
          <xsl:apply-templates select="parent"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="parent[string = 'AAA']">
      <xsl:copy>
          <xsl:variable name="n" select="following-sibling::nb[1]"/>
          <xsl:copy-of select="string | following-sibling::nb[1] | following-sibling::child1[position() &lt;= $n]"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="parent[string != 'AAA']">
      <xsl:copy>
          <xsl:variable name="n" select="following-sibling::nb[1]"/>
          <xsl:copy-of select="string | following-sibling::nb[1] | following-sibling::child2[position() &lt;= $n]"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

在线https://xsltfiddle.liberty-development.net/94hvTzk/1

如果数据不一致,那么至少在 XSLT 2 或 3 中,for-each-group select="*" group-starting-with="parent"data 元素的上下文中识别属于一起的元素并创建 parent 包装器将是一件容易的事结果。

【讨论】:

  • 很好的答案马丁,它工作正常。 非常感谢
  • 该代码使用例如&lt;xsl:copy-of select="string | following-sibling::nb[1] | following-sibling::child2[position() &amp;lt;= $n]"/&gt;,请参阅xsltfiddle.liberty-development.net/94hvTzk/2,因此应复制包括其内容在内的元素。如果无法解决,请提出一个新问题,详细说明您所做的更改。
  • 是的,效果很好。只是一个小问题:为什么当根行 包含以下内容时它不起作用:aspzone.com/xml/serialization">
  • 我尝试在 slt 文件中添加一个命名空间:xmlns:ser="aspzone.com/xml/serialization"。但是当我在 slt 文件中的所有节点前加上 ser: 时,它仅适用于 nb 节点,不会检索到 child1 和 child2 节点。为什么会失败?
【解决方案2】:

对于 XSLT 3.0,这很简单

<xsl:transform version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="data">
    <data>
      <xsl:for-each-group select="*" group-starting-with="parent">
        <parent>
          <xsl:copy-of select="*, tail(current-group())"/>
        </parent>
      </xsl:for-each-group>
    </data>
  </xsl:template>
</xsl:transform>

【讨论】:

  • 嗨,Michael,在使用 ETL Talend XSLT 组件 (java) 进行测试期间,我收到一条错误消息:Error at xsl:copy-of on line 7 column 59 of exemple1b_slt30_solution2.xsl: XPST0017: XPath syntax error at char 24 on line 7 in {*, tail(current-group())}: Unknown system function tail() Exception in component tXSLT_4 javax.xml.transform.TransformerConfigurationException: Failed to compile stylesheet. 1 error detected. at net.sf.saxon.PreparedStylesheet.prepare(PreparedStylesheet.java:176) at net.sf.saxon.TransformerFactoryImpl.newTemplates 非常感谢问候
  • 安装 xsltproc 很复杂,您能建议我另一种方法来测试您的解决方案吗?
  • @asx,要将 XSLT 3 与 Java 一起使用,您可以使用 Saxon 9.8 的任何版本,Sourceforge 和 Maven 上提供了开源 HE 版本。
  • @asx 这些错误消息看起来好像来自 Saxon,但来自不支持 XSLT 3.0 的 Saxon 版本。检查您使用的是最新版本的 Saxon (9.8.0.x)。或者,tail(x) 函数可以重写 remove(x, 1) 以使用 XSLT 2.0 处理器运行。
  • 我安装了 Saxon-HE-9.8.0-12.jar 而不是 saxon9.jar。 tail 和 remove 函数总是失败。 @Martin,您能阅读我上面关于 XSLT 名称空间的评论吗?我无法修复它。提前致谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 2012-04-04
  • 2012-03-01
相关资源
最近更新 更多