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

发布于 2025-01-07 11:36:49 字数 909 浏览 0 评论 0原文

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

考虑我有一个这样的 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 的外部主体了。

What would be the best (preferably most efficient) method to do the following:

Consider I have an XML document as such:

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

Now, given that I specify the “path” to each data-block, i.e. in this case:

path: /comments/comment

I would like to break up the document into n-number of sub-parts, in this case 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>

So, essentially, what I am trying to do is get each node produced by “/comments/comment”, but also retain all “outer” parent nodes data.

EDIT:

Note: this needs to be dynamic, or generic. I.e. the above data xml is just an example. I want to be able to transform any xml document to this effect, given a “path” representing each data-node. And the rest is the outer body of the xml.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

痞味浪人 2025-01-14 11:36:49

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

A generic "shredding" solution can be found in my answer to this question: https://stackoverflow.com/a/8597577/36305

Here is the complete transformation:

<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>

When this transformation is applied on the provided XML document:

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

the wanted, correct result is produced:

<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>
向地狱狂奔 2025-01-14 11:36:49

如果您确实想使用 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>

If you actuall want to transform your Xml using Xslt it should look like this:

<?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>

... will give you the desired output:

<?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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文