Created nested xml from flat xml

2019-09-04 15:45发布

问题:

Okay I am well and truly stuck.

I'm trying to recreate a hierarchy from a flat xml file and I don't know how to proceed. The xml file I'm working from looks a bit like this:

<table name="ecatalogue">
  <tuple>
    <atom name="irn">2470</atom>
    <atom name="EADUnitID"></atom>
    <atom name="EADUnitTitle"></atom>
    <atom name="EADLevelAttribute"></atom>
    <tuple name="AssParentObjectRef">
    </tuple>
  </tuple>

  <tuple>
    <atom name="irn">5416</atom>
    <atom name="EADUnitID"></atom>
    <atom name="EADUnitTitle"></atom>
    <tuple name="AssParentObjectRef">
      <atom name="irn">2470</atom>
      <atom name="EADUnitTitle"></atom>
    </tuple>
  </tuple>

  <tuple>
    <atom name="irn">7</atom>
    <atom name="EADUnitID"></atom>
    <atom name="EADUnitTitle"></atom>
    <tuple name="AssParentObjectRef">
      <atom name="irn">2470</atom>
      <atom name="EADUnitTitle"></atom>
    </tuple>
  </tuple>

  <tuple>
    <atom name="irn">8</atom>
    <atom name="ObjectType"></atom>
    <atom name="EADLevelAttribute"></atom>
    <atom name="EADUnitID"></atom>
    <atom name="EADUnitTitle"></atom>
    <tuple name="AssParentObjectRef">
      <atom name="EADUnitTitle"></atom>
      <atom name="irn">7</atom>
    </tuple>
  </tuple>
</table>

What I would like to achieve is a file which looks something like this:

<table name="ecatalogue">
    <collection>
       <tuple>
         <atom name="irn">2470</atom>
         <atom name="EADUnitID"></atom>
         <atom name="EADUnitTitle"></atom>
         <atom name="EADLevelAttribute"></atom>
         <tuple name="children">
            <tuple> 
                <atom name="irn">5416</atom>
                <atom name="EADUnitID"></atom>
                <atom name="EADUnitTitle"></atom>
                <atom name="EADLevelAttribute"></atom>
                <tuple name="children"></tuple>
            </tuple>
            <tuple> 
                <atom name="irn">7</atom>
                <atom name="EADUnitID"></atom>
                <atom name="EADUnitTitle"></atom>
                <atom name="EADLevelAttribute"></atom>
                <tuple name="children">
                    <tuple> 
                        <atom name="irn">8</atom>
                        <atom name="EADUnitID"></atom>
                        <atom name="EADUnitTitle"></atom>
                        <atom name="EADLevelAttribute"></atom>
                        <tuple name="children"></tuple>
                    </tuple>
                </tuple>
            </tuple>
        </tuple>
      </tuple>
    </collection>

The challenges here are that there can be any number of top level items and any number of child records. Also, they can extend to multiple levels - I don't know how deep the child records extend to. The only way of finding out the relationship between the records is by

<tuple name="AssParentObjectRef">

which gives the immediate parent record and is empty if a record has no parent. If I am to use XSLT, I am limited to XSLT 1.0.

Given its in xml I thought an XSLT might be the way forward, but I know next to nothing about XSLT and my attempts so far have resulted in nothing. I have read up on the Muenchian Method and thought this might help, below is an example of what I have tried so far (which is probably a fair indication of how much of a novice I am at XSLT :-) ):

<?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:key name="parent-irn" match="tuple[@name='AssParentObjectRef']" use="atom[@name='irn']" />

  <xsl:template match="/table/tuple">
    <xsl:copy>
      <xsl:apply-templates select="*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/table/tuple">
    <xsl:if test="generate-id() = generate-id(key('parent-irn', atom[@name='irn'])[1])">
      <collection>
        <xsl:attribute name="title">
          <xsl:value-of select="tuple[@name='AssParentObjectRef']"/>
        </xsl:attribute>
        <xsl:for-each select="key('parent-irn', atom[@name='irn'])">
          <tuple>
            <xsl:copy-of select="atom[@name='EADUnitTitle']" />
            <xsl:copy-of select="atom[@name='EADUnitID']" />
            <xsl:copy-of select="atom[@name='EADLevelAttribute']" />
          </tuple>
        </xsl:for-each>
      </collection>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet> 

Am I on the right track with this? Is XSLT even the right tool for this job?

Edit Added missing 'tuple' tag from desired output.

回答1:

You should try it this way:

XSLT 1.0

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

The result here is slightly different from the one you posted:

<?xml version="1.0" encoding="UTF-8"?>
<table name="ecatalogue">
   <collection>
      <tuple>
         <atom name="irn">2470</atom>
         <atom name="EADUnitID"/>
         <atom name="EADUnitTitle"/>
         <atom name="EADLevelAttribute"/>
         <tuple name="children">
            <tuple>
               <atom name="irn">5416</atom>
               <atom name="EADUnitID"/>
               <atom name="EADUnitTitle"/>
            </tuple>
            <tuple>
               <atom name="irn">7</atom>
               <atom name="EADUnitID"/>
               <atom name="EADUnitTitle"/>
               <tuple name="children">
                  <tuple>
                     <atom name="irn">8</atom>
                     <atom name="ObjectType"/>
                     <atom name="EADLevelAttribute"/>
                     <atom name="EADUnitID"/>
                     <atom name="EADUnitTitle"/>
                  </tuple>
               </tuple>
            </tuple>
         </tuple>
      </tuple>
   </collection>
</table>

but I am not sure what the correct result is because your example is ambiguous.



标签: xml xslt