使用XPath / XSLT转换通用XML文档

时间:2012-02-21 14:36:24

标签: xml xslt xpath xml-parsing

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

考虑我有一个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”生成的每个节点,同时保留所有“外部”父节点数据。

修改

注意:这需要是动态的或通用的。即以上数据xml只是一个例子。我希望能够将任何xml文档转换为此效果,给定表示每个数据节点的“路径”。其余的是xml的外部主体。

2 个答案:

答案 0 :(得分:0)

如果你真的想用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>

答案 1 :(得分:0)

在我对这个问题的回答中可以找到通用的“粉碎”解决方案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>