【问题标题】:Transforming of generic XML-document using XPath/XSLT使用 XPath/XSLT 转换通用 XML 文档
【发布时间】:2012-03-11 20:54:36
【问题描述】:

执行以下操作的最佳(最好是最有效)方法是什么:

假设我有一个这样的 XML 文档:

<comments question_id="123">
    <comment id="1">
       This is the first comment
    </comment>
    <comment id="2">
       This is the second comment
    </comment>
</comments>

现在,假设我指定了每个数据块的“路径”,即在这种情况下:

path: /comments/comment

我想将文档分成 n 个子部分,在本例中为 2:

<comments question_id="123">
    <comment id="1">
       This is the first comment
    </comment>
</comments>

<comments question_id="123">
    <comment id="2">
       This is the second comment
    </comment>
</comments>

所以,本质上,我要做的是让每个节点由“/comments/comment” 生成,但还要保留所有“外部”父节点数据。

编辑:

注意:这需要是动态的或通用的。 IE。以上数据xml只是一个例子。我希望能够将任何 xml 文档转换为这种效果,给定一个代表每个数据节点的“路径”。剩下的就是xml的外体了。

【问题讨论】:

  • XPath 允许您选择现有文档中的节点,而不是对其进行转换。对于该任务,您使用 XSLT,而不是 XPath。你想使用 XSLT 吗?你想要几个输出文件吗?或者只是将所有这些片段组合在一个文档中?
  • @MartinHonnen 感谢您的评论。我不介意使用 Xslt,所以我编辑了这个问题。我实际上想要四个单独的文件。
  • @Larry:是的,存在通用解决方案。
  • Larry,如果您想从一个 XSLT 转换中获得多个结果文档,您应该查看 XSLT 2.0 和 xsl:result-document:w3.org/TR/xslt20/#creating-result-trees。

标签: xml xslt xpath xml-parsing


【解决方案1】:

可以在我对这个问题的回答中找到通用的“粉碎”解决方案:https://stackoverflow.com/a/8597577/36305

这是完整的转换:

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

     <xsl:param name="pLeafNodes" select="//comment"/>

     <xsl:template match="/">
      <t>
        <xsl:call-template name="StructRepro"/>
      </t>
     </xsl:template>

     <xsl:template name="StructRepro">
       <xsl:param name="pLeaves" select="$pLeafNodes"/>

       <xsl:for-each select="$pLeaves">
         <xsl:apply-templates mode="build" select="/*">
          <xsl:with-param name="pChild" select="."/>
          <xsl:with-param name="pLeaves" select="$pLeaves"/>
         </xsl:apply-templates>
       </xsl:for-each>
     </xsl:template>

      <xsl:template mode="build" match="node()|@*">
          <xsl:param name="pChild"/>
          <xsl:param name="pLeaves"/>

         <xsl:copy>
           <xsl:apply-templates mode="build" select="@*"/>

           <xsl:variable name="vLeafChild" select=
             "*[count(.|$pChild) = count($pChild)]"/>

           <xsl:choose>
            <xsl:when test="$vLeafChild">
             <xsl:apply-templates mode="build"
                 select="$vLeafChild
                        |
                          node()[not(count(.|$pLeaves) = count($pLeaves))]">
                 <xsl:with-param name="pChild" select="$pChild"/>
                 <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
             <xsl:apply-templates mode="build" select=
             "node()[not(.//*[count(.|$pLeaves) = count($pLeaves)])
                    or
                     .//*[count(.|$pChild) = count($pChild)]
                    ]
             ">

                 <xsl:with-param name="pChild" select="$pChild"/>
                 <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
            </xsl:otherwise>
           </xsl:choose>
         </xsl:copy>
     </xsl:template>
     <xsl:template match="text()"/>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<comments question_id="123">
    <comment id="1">
      This is the first comment
  </comment>
    <comment id="2">
      This is the second comment
 </comment>
</comments>

产生想要的正确结果:

<t>
   <comments question_id="123">
      <comment id="1">
      This is the first comment
  </comment>
   </comments>
   <comments question_id="123">
      <comment id="2">
      This is the second comment
 </comment>
   </comments>
</t>

【讨论】:

  • 非常感谢,这看起来很棒。一个问题:这段代码中唯一的变量是我们指定select="//comment” 的那一行吗?如果我想要这个完全通用的,比如我想将这个转换应用到 Java 中的一系列 xml 文档,那么最好的方法是什么?
  • @Larry:这不是一个变量——这是一个全局的xsl:param,它的主要目的正是你所要求的——它可以由调用转型。如何设置转换的参数取决于实现。您必须阅读 XSLT 处理器的文档。我知道如何为 .NET XslCompiledTransform 或 MSXML 或 Saxon 执行此操作。
  • 能否请您告诉我:我在 Java 中使用 JAXP,并尝试使用:trans.setParameter("pLeafNodes", "//comment”) 设置参数 - 虽然我收到以下错误:SystemId Unknown; Line #0; Column #0; Can not convert #STRING to a NodeList! 并且,(当没有参数时设置好了,转换效果很好!)
  • @Larry:我没有使用过 JAXP,但在你的情况下错误很明显——你正在传递一个字符串(未计算的 XPath 表达式),但你应该传递类似 @987654329 的东西@,这是对 XPath 表达式 //comment 求值的结果。
【解决方案2】:

如果您真的想使用 Xslt 转换您的 Xml,它应该如下所示:

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

    <xsl:template match="/">
        <!-- You need a document root to produce valid Xml output -->
        <yourdocumentroot>
            <xsl:apply-templates />
        </yourdocumentroot>
    </xsl:template>

    <xsl:template match="comments">
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="comment">
        <xsl:element name="comments">
            <xsl:attribute name="question_id">
                <xsl:value-of select="ancestor::comments/@question_id"/>
            </xsl:attribute>
            <xsl:element name="comment">
                <xsl:attribute name="id">
                    <xsl:value-of select="./@id" />
                </xsl:attribute>
                <xsl:value-of select="./text()" />
            </xsl:element>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

... 将为您提供所需的输出:

<?xml version="1.0" encoding="utf-8"?>
<yourdocumentroot>
    <comments question_id="123">
        <comment id="1">This is the first comment</comment>
    </comments>
    <comments question_id="123">
        <comment id="2">This is the second comment</comment>
    </comments>
</yourdocumentroot>

【讨论】:

  • 谢谢,但我忘了说,我想要一些通用的东西。我现在已将其编辑到我的问题中。您能否更新您的回复以启用此功能。
猜你喜欢
  • 2020-11-22
  • 2012-01-12
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 2020-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多