Home > Mobile >  XSLT transformation of multiple "flat" XML files into tree-like XML
XSLT transformation of multiple "flat" XML files into tree-like XML

Time:08-13

I use saxon 11he

file 1.xml describes all objects:

<?xml version="1.0" encoding="utf-8"?>
<ADDRESSOBJECTS>
  <OBJECT OBJECTID="105037985" NAME="OTHER PARENT" LEVEL="2" STARTDATE="2022-07-20" ENDDATE="2079-06-06" ISACTIVE="1" />
  <OBJECT OBJECTID="1171388" NAME="PARENT" LEVEL="2" STARTDATE="1900-01-01" ENDDATE="2079-06-06" ISACTIVE="1" />
  <OBJECT OBJECTID="1171421" NAME="CHILD 1" LEVEL="6" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <OBJECT OBJECTID="1171502" NAME="CHILD 2" LEVEL="6" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <OBJECT OBJECTID="1171612" NAME="CHILD 3" LEVEL="4" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <OBJECT OBJECTID="1171731" NAME="CHILD 4" LEVEL="4" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <OBJECT OBJECTID="1171761" NAME="CHILD 5" LEVEL="6" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
</ADDRESSOBJECTS>

file 2.xml describes the hierarchical dependency:

<?xml version="1.0" encoding="utf-8"?>
<ITEMS>
  <ITEM OBJECTID="1170420" PARENTOBJID="1171380" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <ITEM OBJECTID="1170423" PARENTOBJID="1171380" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <ITEM OBJECTID="1171421" PARENTOBJID="1171388" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <ITEM OBJECTID="1171421" PARENTOBJID="1171388" STARTDATE="2001-01-01" ENDDATE="2004-12-26" ISACTIVE="0" />
  <ITEM OBJECTID="1171502" PARENTOBJID="1171388" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <ITEM OBJECTID="1171612" PARENTOBJID="1171388" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <ITEM OBJECTID="1171731" PARENTOBJID="1171388" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
  <ITEM OBJECTID="1171761" PARENTOBJID="1171388" STARTDATE="2004-12-27" ENDDATE="2079-06-06" ISACTIVE="1" />
</ITEMS>

I need to build a tree from file 1 objects by hierarchy from file 2

I'm stuck even at the first level, there are 4-5 levels in total, and there are two hierarchy files, there is another independent hierarchy (but it doesn't matter) First, how to return to the context of file 1 in for-each? (see below xsl file) Secondly, how to select a node in for-each by the 2nd file from the 1st file and work with its attributes? Because, I need to copy a lot more attributes and pull them in the way that I have, this is every new search and, accordingly, an increase execution time. Thirdly, it is necessary to sort by @NAME (from the 1st file), although it is not necessary, but interesting.

I made some crutches, I assume that everything is done differently:

<xsl:transform
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="2.0"
>
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>


<xsl:variable name="objectid">
  <xsl:value-of
          select="//OBJECT[@LEVEL='2' and @ISACTIVE='1' and starts-with(@NAME, 'PARENT')]/@OBJECTID"/>
</xsl:variable>

<xsl:template match="/">
<ROOT OBJECTID="{$objectid}">
  <xsl:for-each select="document('2.xml')//ITEM[@PARENTOBJID=$objectid and @ISACTIVE='1']">
    <xsl:variable name="childid">
      <xsl:value-of select="@OBJECTID"/>
    </xsl:variable>
    <xsl:variable name="childname">
      <xsl:value-of
              select="document('1.xml')//OBJECT[@OBJECTID=$childid and @ISACTIVE='1']/@NAME"/>
    </xsl:variable>
    <xsl:variable name="childlevel">
      <xsl:value-of
              select="document('1.xml')//OBJECT[@OBJECTID=$childid and @ISACTIVE='1']/@LEVEL"/>
    </xsl:variable>
     <ELEMENT OBJECTID="{$childid}" NAME="{$childname}" LEVEL="{$childlevel}" />
  </xsl:for-each>
</ROOT>
</xsl:template>

</xsl:transform>

output:

<?xml version="1.0" encoding="UTF-8"?>
<ROOT OBJECTID="1171388">
   <ELEMENT OBJECTID="1171421" NAME="CHILD 1" LEVEL="6"/>
   <ELEMENT OBJECTID="1171502" NAME="CHILD 2" LEVEL="6"/>
   <ELEMENT OBJECTID="1171612" NAME="CHILD 3" LEVEL="4"/>
   <ELEMENT OBJECTID="1171731" NAME="CHILD 4" LEVEL="4"/>
   <ELEMENT OBJECTID="1171761" NAME="CHILD 5" LEVEL="6"/>
</ROOT>

PS: In general, i need to get something like this structure.

<REGION>
   <DISTRICT>
     <MUNICIPALDISTRICT>
       <LOCALITY>
         <DRIVEWAY>
           <HOUSE>
           </HOUSE>
         </DRIVEWAY>
       </LOCALITY>
     </MUNICIPALDISTRICT>
   </DISTRICT>
</REGION>

the element of each nested level is called by its own name

PPS: I hope I've made my confused thoughts clear.

CodePudding user response:

Use a key e.g. <xsl:key name="child" match="ITEM" use="@PARENTOBJID"/> and then you can follow e.g. key('child', @OBJECTID, doc('2.xml')) to find element children of the currently processed ITEM.

Trying to use that it seems you need a second key to look back in the main document:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">
  
  <xsl:output indent="yes"/>
  
  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:key name="child" match="ITEM" use="@PARENTOBJID"/>
  
  <xsl:key name="object" match="OBJECT" use="@OBJECTID"/>
  
  <xsl:variable name="doc1" select="/"/>
  
  <xsl:template match="/">
    <xsl:apply-templates select="ADDRESSOBJECTS/OBJECT[@LEVEL = 2]"/>
  </xsl:template>
  
  <xsl:template match="OBJECT[@LEVEL = 2]">
    <ROOT>
      <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('object', @OBJECTID, $doc1)"/>
    </ROOT>
  </xsl:template>
  
  <xsl:template match="OBJECT">
    <ELEMENT>
      <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('object', @OBJECTID, $doc1)"/>
    </ELEMENT>
  </xsl:template>
  
  <xsl:param name="doc2" select="doc('2.xml')"/>

</xsl:stylesheet>

Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')} &input=<?xml version="1.0" encoding="utf-8"?> &input-type=XML" rel="nofollow noreferrer">Online sample.

I guess you want to filter out some attributes with an empty template.

  • Related