无法从平面xml创建嵌套xml

时间:2017-05-05 09:34:07

标签: xml xslt

我正在尝试使用XSLT从平面XML创建嵌套的xml但是我发现它只创建了一个嵌套并忽略了源XML中的其余记录。

我的XML输入如下所示:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!-- Data -->
<table name="ecatalogue">
  <!-- Row 1 -->
  <tuple>
    <atom name="irn">2470</atom>
    <atom name="EADUnitID">da.01</atom>
    <atom name="EADUnitTitle">Some title</atom>
    <tuple name="AssParentObjectRef" />
  </tuple>
    <!-- Row 2 -->
  <tuple>
    <atom name="irn">5416</atom>
    <atom name="EADUnitID">da.01.01</atom>
    <atom name="EADUnitTitle">Child of Some title</atom>
    <tuple name="AssParentObjectRef">
    <atom name="EADUnitTitle">Some Title</atom>
    <atom name="irn">2470</atom>
    </tuple>
  </tuple>
    <!-- Row 3 -->
  <tuple>
    <atom name="irn">6</atom>
    <atom name="EADUnitID">da.01.02</atom>
    <atom name="EADUnitTitle">Child of Some title 2</atom>
    <tuple name="AssParentObjectRef">
    <atom name="EADUnitTitle">Some Title</atom>
    <atom name="irn">2470</atom>
    </tuple>
  </tuple>
    <!-- Row 4 -->
  <tuple>
    <atom name="irn">8</atom>
    <atom name="EADUnitID">da.01.02.01</atom>
    <atom name="EADUnitTitle">3rd Generation</atom>
    <tuple name="AssParentObjectRef">
    <atom name="EADUnitTitle">Child of Some Title 2</atom>
    <atom name="irn">6</atom>
    </tuple>
  </tuple>
    <!-- Row 5 -->
  <tuple>
    <atom name="irn">1130</atom>
    <atom name="EADUnitID">da.02</atom>
    <atom name="EADUnitTitle">Another title</atom>
    <tuple name="AssParentObjectRef" />
  </tuple>
    <!-- Row 6 -->
  <tuple>
    <atom name="irn">54</atom>
    <atom name="EADUnitID">da.02.01</atom>
    <atom name="EADUnitTitle">Child of Another title</atom>
    <tuple name="AssParentObjectRef">
    <atom name="EADUnitTitle">Another Title</atom>
    <atom name="irn">1130</atom>
    </tuple>
  </tuple>
    <!-- Row 7 -->
  <tuple>
    <atom name="irn">16</atom>
    <atom name="EADUnitID">da.02.02</atom>
    <atom name="EADUnitTitle">Child of Another Title 2</atom>
    <tuple name="AssParentObjectRef">
    <atom name="EADUnitTitle">Another Title</atom>
    <atom name="irn">1130</atom>
    </tuple>
  </tuple>
    <!-- Row 8 -->
  <tuple>
    <atom name="irn">22</atom>
    <atom name="EADUnitID">da.02.02.01</atom>
    <atom name="EADUnitTitle">3rd Generation</atom>
    <tuple name="AssParentObjectRef">
    <atom name="EADUnitTitle">Child of Another Title 2</atom>
    <atom name="irn">1130</atom>
    </tuple>
  </tuple>
</table>

XSLT应识别顶级记录,然后添加子级。对于顶级记录,它应分别将其irn和EADUnitTitle复制为TopID和TopTitle。对于每个孩子,它应该包括直接的ParentID和ParentTitle以及TopID和TopTitle。输出应如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<table name="ecatalogue">
   <collection>
      <tuple>
         <atom name="irn">2470</atom>
         <atom name="EADUnitID">da.01</atom>
         <atom name="EADUnitTitle">Some title</atom>
         <atom name="TopTitle">Some title</atom>
         <atom name="TopID">2470</atom>
         <tuple name="children">
            <tuple>
               <atom name="irn">5416</atom>
               <atom name="EADUnitID">da.01.01</atom>
               <atom name="EADUnitTitle">Child of Some title</atom>
               <atom name="ParentTitle">Some title</atom>
               <atom name="ParentID">2470</atom>
               <atom name="TopTitle">Some title</atom>
               <atom name="TopID">2470</atom>
            </tuple>
            <tuple>
                <atom name="irn">6</atom>
               <atom name="EADUnitID">da.01.02</atom>
               <atom name="EADUnitTitle">Child of Some title 2</atom>
               <atom name="ParentTitle">Some title</atom>
               <atom name="ParentID">2470</atom>
               <atom name="TopTitle">Some title</atom>
               <atom name="TopID">2470</atom>
               <tuple name="children">
                  <tuple>
                    <atom name="irn">8</atom>
                    <atom name="EADUnitID">da.01.02.01</atom>
                    <atom name="EADUnitTitle">3rd Generation</atom>
                    <atom name="ParentTitle">Child of Some title 2</atom>
                    <atom name="ParentID">6</atom>
                    <atom name="TopTitle">Some title</atom>
                    <atom name="TopID">2470</atom>
                  </tuple>
               </tuple>
            </tuple>
         </tuple>
      </tuple>
   </collection>
   <collection>
      <tuple>
         <atom name="irn">1130</atom>
         <atom name="EADUnitID">da.02</atom>
         <atom name="EADUnitTitle">Another title</atom>
         <atom name="TopTitle">Another title</atom>
         <atom name="TopID">1130</atom>
         <tuple name="children">
            <tuple>
               <atom name="irn">54</atom>
               <atom name="EADUnitID">da.02.01</atom>
               <atom name="EADUnitTitle">Child of Another title</atom>
               <atom name="ParentTitle">Another title</atom>
               <atom name="ParentID">1130</atom>
               <atom name="TopTitle">Another title</atom>
               <atom name="TopID">1130</atom>
            </tuple>
            <tuple>
                <atom name="irn">16</atom>
               <atom name="EADUnitID">da.02.02</atom>
               <atom name="EADUnitTitle">Child of Another title 2</atom>
               <atom name="ParentTitle">Another title</atom>
               <atom name="ParentID">1130</atom>
               <atom name="TopTitle">Another title</atom>
               <atom name="TopID">1130</atom>
               <tuple name="children">
                  <tuple>
                    <atom name="irn">22</atom>
                    <atom name="EADUnitID">da.02.02.01</atom>
                    <atom name="EADUnitTitle">3rd Generation</atom>
                    <atom name="ParentTitle">Child of Another title 2</atom>
                    <atom name="ParentID">16</atom>
                    <atom name="TopTitle">Another title</atom>
                    <atom name="TopID">1130</atom>
                  </tuple>
               </tuple>
            </tuple>
         </tuple>
      </tuple>

....

   </collection>
</table>

我的XSLT是:

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

<xsl:key name="child" match="tuple" use="tuple[@name='AssParentObjectRef']/atom[@name='irn']" />

<xsl:template match="/table">
    <table name="ecatalogue">
        <collection>
            <xsl:apply-templates select="tuple[not(tuple[@name='AssParentObjectRef']/atom[@name='irn'])]"/>
        </collection>
    </table>
</xsl:template>

<xsl:template match="tuple">
    <tuple>
        <xsl:copy-of select="atom"/>
        <xsl:if test="key('child', atom[@name='irn'])">
            <tuple name="children">
                <xsl:apply-templates select="key('child', atom[@name='irn'])"/>
             </tuple>
        </xsl:if>
    </tuple>
</xsl:template>

</xsl:stylesheet>

虽然这会将记录分组,但输出只是这些集合中的一个。因此,从3524条记录的文件中,我得到一个包含24条记录的集合。

我已经尝试过替换XSLT:

<xsl:template match="/table">
    <table name="ecatalogue">
        <collection>
            <xsl:apply-templates select="tuple[not(tuple[@name='AssParentObjectRef']/atom[@name='irn'])]"/>
        </collection>
    </table>
</xsl:template>

使用:

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

虽然这会返回所有嵌套结构,但它也会复制嵌套中的记录,因此它们会成为集合。

关于我哪里出错的任何想法?

编辑06/06/17

当我使用时:

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

我得到了重复项(注意:以下示例中的'id'已添加用于说明):

 <record id='1'>
   <children>
        <record id='2'>
            <children>
                <record id='3'>
                    <children>
                        <record id='4'></record>
                    </children>
                </record>
            </children>
        </record>
    </children>
</record>

<record id='2'>
        <children>
            <record id='3'>
                <children>
                    <record id='4'></record>
                </children>
            </record>
        </children>
</record>

<record id='3'>
    <children>
        <record id='4'></record>
    </children>
</record>

<record id='2'></record>
<record id='3'></record>
<record id='4'></record>

有没有删除重复项,所以我只剩下嵌套记录了?

编辑 - 问题元组

 <!-- Row 3378 -->
  <tuple>
    <atom name="irn">115024</atom>
    <atom name="ObjectType">Archives</atom>
    <atom name="EADLevelAttribute">Series</atom>
    <atom name="EADUnitID">D42.PL.05</atom>
    <atom name="EADUnitTitle">Correspondence and Company Administration: Box Files</atom>
    <atom name="EADScopeAndContent">Box files of Port Line official company correspondence and administrative papers. These papers were collected towards historical research and include correspondence from earlier periods c.1890 although the bulk of the papers relate to the two periods 1937-1939 and 1949-1951.</atom>
    <atom name="EADBiographyOrHistory"></atom>
    <tuple name="AssParentObjectRef">
    </tuple>
    <atom name="EADArrangement">The papers in this series have been retained in the original order as stored by Port Line Ltd. The contents of each box file are listed as a typescript paper and have been listed in this catalogue. Box file titles have been listed in the title field of each item in this series.</atom>
    <atom name="EADUnitDate">1890-1952</atom>
    <table name="EADExtent_tab">
      <tuple>
        <atom name="EADExtent">7 boxes.</atom>
      </tuple>
    </table>
    <atom name="EADAccruals"></atom>
    <atom name="EADOtherFindingAid"></atom>
    <atom name="EADRelatedMaterial"></atom>
    <tuple name="EADAcquisitionInformationRef">
    </tuple>
    <atom name="EADAppraisalInformation"></atom>
    <atom name="EADSeparatedMaterial"></atom>
    <atom name="EADTitleProper"></atom>
    <atom name="EADPublicationStatement"></atom>
    <atom name="EADCustodialHistory"></atom>
    <atom name="EADSource"></atom>
    <atom name="EADNote"></atom>
    <atom name="EADAccessRestrictions">Some items in this series are closed access.</atom>
    <atom name="EADUseRestrictions"></atom>
  </tuple>

  <!-- Row 3379 -->
  <tuple>
    <atom name="irn">115025</atom>
    <atom name="ObjectType">Archives</atom>
    <atom name="EADLevelAttribute">Item</atom>
    <atom name="EADUnitID">D42.PL.05.01</atom>
    <atom name="EADUnitTitle">File: Australian Homeward Trade</atom>
    <atom name="EADScopeAndContent">Various papers relating to Australian Homeward Trade and includes the following:For proof copies of the Australian Homeward Agreement see D42/PL5/6.</atom>
    <atom name="EADBiographyOrHistory"></atom>
    <tuple name="AssParentObjectRef">
      <atom name="EADUnitTitle">Correspondence and Company Administration: Box Files</atom>
      <atom name="irn">115024</atom>
    </tuple>
    <atom name="EADArrangement"></atom>
    <atom name="EADUnitDate">1920-1936</atom>
    <table name="EADExtent_tab">
      <tuple>
        <atom name="EADExtent">1 file.</atom>
      </tuple>
    </table>
    <atom name="EADAccruals"></atom>
    <atom name="EADOtherFindingAid"></atom>
    <atom name="EADRelatedMaterial"></atom>
    <tuple name="EADAcquisitionInformationRef">
    </tuple>
    <atom name="EADAppraisalInformation"></atom>
    <atom name="EADSeparatedMaterial"></atom>
    <atom name="EADTitleProper"></atom>
    <atom name="EADPublicationStatement"></atom>
    <atom name="EADCustodialHistory"></atom>
    <atom name="EADSource"></atom>
    <atom name="EADNote"></atom>
    <atom name="EADAccessRestrictions"></atom>
    <atom name="EADUseRestrictions"></atom>
  </tuple>

2 个答案:

答案 0 :(得分:2)

如果您希望每个顶级父级collection都有一个tuple元素,我认为您需要做的就是拥有xsl:for-each来获取父级,并移动创建其中的collection元素。

<xsl:template match="/table">
    <table name="ecatalogue">
        <xsl:for-each select="tuple[not(tuple[@name='AssParentObjectRef']/atom[@name='irn'])]">
            <collection>
                <xsl:apply-templates select="." />
            </collection>
        </xsl:for-each>
    </table>
</xsl:template>

答案 1 :(得分:1)

这有点长;我试着解决一些引起我注意的相关问题。

现有模板

首先,让我们分析您自己的XSL代码所发生的事情。

<xsl:template match="/table">
    <table name="ecatalogue">
        <collection>
            <xsl:apply-templates select="tuple[not(tuple[@name='AssParentObjectRef']/atom[@name='irn'])]"/>
        </collection>
    </table>
</xsl:template>
  • 您在模板中<collection>/table匹配。由于只有一个/table匹配,因此输出中只会有一个<collection>

  • 此外,您可以简化选择不引用父对象的顶级tuple s(tuple元素)。而不是:

    select="tuple[not(tuple[@name='AssParentObjectRef']/atom[@name='irn'])]"
    
    你可以更简单地说:

    select="tuple[not(tuple/*)]"
    

    因为我们可以从您的示例中看出,顶级tuple只包含一个空的自我关闭<tuple>标记。

接下来的一点:

<xsl:key name="child" match="tuple" use="tuple[@name='AssParentObjectRef']/atom[@name='irn']" />

因此,我们在tuple上进行了密钥匹配,并使用了父tuple的ID。

<xsl:template match="tuple">
    <tuple>
        <xsl:copy-of select="atom"/>

        <!-- If this `tuple` is a parent (i.e. if it's included in
             the list of parent IDs in the key), then we add a
             wrapper for the children and process the children.  -->
        <xsl:if test="key('child', atom[@name='irn'])">
            <tuple name="children">
                <!-- Now we apply templates to the `tuple`s 
                     in the key -->
                <xsl:apply-templates select="key('child', atom[@name='irn'])"/>
            </tuple>
        </xsl:if>
    </tuple>
</xsl:template>

主要是有效。将此输出与您的样本所需输出进行比较,您缺少的位是<collection>包装标记(如上所述),以及父级和顶级祖先标题和ID(您不需要&#) 39; t有任何XSL代码。)

你说,

&#34;虽然这会对记录进行分组,但输出只是这些集合中的一个。因此,从3524条记录的文件中,我得到一个包含24条记录的集合。&#34;

我可以假设您实际输入的其余部分的XML结构可能与您的XSL目标不同。但是,如果没有看到你的实际输入,我不知道为什么会出现这种情况。

您的编辑

您描述了添加以下模板:

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

这是&#34;身份&#34;模板,如此命名,因为它只是简单地复制元素。您期望看到的重复:XSLT处理器在输入文件中经过tuple s的平面集并复制它们,然后处理其中的一些模板的上下文专门匹配tuple,它将模板应用于与密钥匹配的tuple(嵌套子项)。

替代方法

table模板并没有太大差异:

<xsl:template match="table">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <!-- Target only parent-level `tuple`s.  This excludes
            child-level tuples, helping to prevent duplicates.-->
        <xsl:apply-templates select="tuple[not(tuple/*)]" mode="top"/>
    </xsl:copy>
</xsl:template>

值得注意的是,我们不会在<collection>处执行任何操作 - 我们希望将每个顶级tuple(及其子级)包装在{ {1}},因此我们需要在<collection>级别添加<collection>

tuple

我匹配<!-- Add the `collection` wrapper only to top-level tuples --> <xsl:template match="tuple" mode="top"> <collection> <!-- Pass on this tuple to the main `tuple` template --> <xsl:apply-templates select="."/> </collection> </xsl:template> ,但使用特殊模式 - 我们只想为顶级tuple添加<collection>包装器。然后我有一个单独的模板来处理所有tuple s:

tuple

我无法对3524条记录的完整数据集说话,但针对您的示例输入XML运行上述内容,我的输出与您想要的输出完全相同(示例输入XML中有关引用的一个错误除外) <!-- This is the main template for processing `tuple` elements. Most of the changes needed are common to all `tuple`s, so it makes sense to keep all the logic in one place. --> <xsl:template match="tuple"> <tuple> <!-- Copy each existing `atom` child --> <xsl:copy-of select="atom"/> <!-- Add in metadata about parent and top-level ancestor titles and IDs --> <xsl:choose> <!-- If this is a top-level item, just use its own values --> <xsl:when test="not(tuple/*)"> <atom name="TopTitle"><xsl:value-of select="atom[@name='EADUnitTitle']"/></atom> <atom name="TopID"><xsl:value-of select="atom[@name='irn']"/></atom> </xsl:when> <!-- If this is a descendant, we need to find its parent and its top-level ancestor --> <xsl:when test="tuple/*"> <atom name="ParentTitle"><xsl:value-of select="tuple/atom[@name='EADUnitTitle']"/></atom> <atom name="ParentID"><xsl:value-of select="tuple/atom[@name='irn']"/></atom> <!-- For convenience, grab the top-level ancestor `tuple` and stuff it in a variable. This is vaguely annalogous to your use of `key`. --> <!-- Finding the top-level `tuple` is complicated by the fact that the ID values in `<atom name="irn">` do not have a standardized format, other than that the whole strings appear to consist of atomic values separated by single periods, with descendant `irn` values appending to the precedent values. Examples: Top: `da.04` Descendant: `da.04.11.02` Top: `D42.PL.05` Descendant: `D42.PL.05.01` So chunking the ID values is a problematic approach, since we don't know how many chunks comprise the initial non-numeric portion: `da`, or `D42.PL`, or ... ???. Top-level elements *do* also have empty `<tuple name="AssParentObjectRef">` elements. So we _can_ find all the top-level elements, and then look in those for the one that has an `irn` value that matches the start of the `irn` value of this current `tuple`. --> <xsl:variable name="top" select="/table/tuple[tuple[@name='AssParentObjectRef'][not(*)]] ['The above statement grabs all the `tuple`s that have an empty `tuple[@name=`AssParentObjectRef``. The below statement then goes through all those `tuple`s to find the ones where the `irn` values match the start of the `irn` value of the current `tuple`.'] [starts-with(current()/atom[@name='EADUnitID'], atom[@name='EADUnitID'])]"/> <!-- Now we can reference that variable to get the top-level ancestor values --> <atom name="TopTitle"><xsl:value-of select="$top/atom[@name='EADUnitTitle']"/></atom> <atom name="TopID"><xsl:value-of select="$top/atom[@name='irn']"/></atom> </xsl:when> </xsl:choose> <!-- Process any children of this tuple, based on `irn` values. Basically, we look for any other `tuple`s in the `table` that point to this current `tuple`'s `irn` value. --> <xsl:if test="/table/tuple[tuple/atom[@name='irn'] = current()/atom[@name='irn']]"> <tuple name="children"> <xsl:apply-templates select="/table/tuple[tuple/atom[@name='irn'] = current()/atom[@name='irn']]"></xsl:apply-templates> </tuple> </xsl:if> </tuple> </xsl:template> 值,在您的初始帖子的评论中提及。)

有关所需输出XML的建议

作为一种数据格式,您所需的输出XML有许多方面让我觉得有些奇怪。

  • irn包装器
    这似乎是多余的;它很容易看出给定的元组1)是否是顶级的,2)有任何孩子。

  • <collection>Parent标题和ID
    这些似乎是多余的。只要您的数据按层次结构构建,所有这一切都变得清晰,无需特别包含它。包含此元数据只会复制已有的信息。

  • Top包装器
    在没有任何信息丢失的情况下也可以省略这一点。一个<tuple name="children">元素嵌套在另一个元素中的简单存在就足以证明孩子的存在。

我不知道您是否对输出XML文件格式的设计有任何控制或影响,但如果您这样做,我建议使用更短,更简化的结构,例如以下内容:

tuple

在此结构中,<table name="ecatalogue"> <tuple> <atom name="irn">2470</atom> <atom name="EADUnitID">da.01</atom> <atom name="EADUnitTitle">Some title</atom> <tuple> <atom name="irn">5416</atom> <atom name="EADUnitID">da.01.01</atom> <atom name="EADUnitTitle">Child of Some title</atom> </tuple> <tuple> <atom name="irn">6</atom> <atom name="EADUnitID">da.01.02</atom> <atom name="EADUnitTitle">Child of Some title 2</atom> <tuple> <atom name="irn">8</atom> <atom name="EADUnitID">da.01.02.01</atom> <atom name="EADUnitTitle">3rd Generation</atom> </tuple> </tuple> </tuple> </table> 只能包含tuple个或其他atom个。我们只需查找包含其他tuple的任何顶级tuple即可识别集合。我们只需查找具有tuple父级的tuple即可识别孩子。我们只需选择tuple并进一步查看元素树即可找到顶级和父级标题和ID。

这种结构更简单,避免了数据重复,并且可以说更清晰,更容易处理。那就是说,你知道你的需求!做什么对你有用。 :)

请查看代码和评论,如果您有任何疑问,请与我们联系。

更新2017-06-15:问题元组

我将这些添加到您之前的示例输入XML中,并尝试应用我以前的XSL代码。在回顾问题元组时,我发现了两件事:

  1. 我之前的代码使用了tuple函数,该函数仅在XSL 2.0及更高版本中可用。您的帖子标签没有指定XSL 1.0,我没注意到您在示例XSL标头中指定了这一点。

    我已经重新编写了上面的XSL代码(匹配tokenize的模板),只依赖于XSL 1.0功能。

  2. 初始样本输入XML中的tuple值不具代表性,因此,当应用于更全面,不可见的输入XML时,任何针对该样本专门编码的尝试都将失败。

    您的初始示例仅包含EADUnitID格式的EADUnitID值,其中da.XX是数字,XX模式可以重复。我已经做了一些关于分块这个字符串并比较各个部分的假设。

    但是,您的问题元组的.XX值格式非常不同,显示为EADUnitID,其中DXX.PL.XX为数字,XX模式为结束可以重复。这意味着依赖于时段之间的块的一致性并不是一种安全的方法。

    我重新编写了XSL代码,以匹配.XX字符串的整个前端,这似乎可靠地工作。

  3. 查看代码和评论,如果有任何不清楚或无法解决的话,请告诉我。