Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

XSLT transformation of multiple "flat" XML files into tree-like XML. Continuation – 2 xml files of objects and 1 with hierarchy

This is a continuation of the previous question.
XSLT transformation of multiple "flat" XML files into tree-like XML

I tried to build xml from more object files and got a problem.

what we have:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

file 1.xml is $doc1 -main objects

<?xml version="1.0" encoding="utf-8"?>
<ADDRESSOBJECTS>
  <OBJECT OBJECTID="105037985" NAME="OLDER OTHER PARENT" LEVEL="2" ISACTIVE="0" />
  <OBJECT OBJECTID="105037985" NAME="OTHER PARENT" LEVEL="2" ISACTIVE="1" />
  <OBJECT OBJECTID="1171388" NAME="OLDER PARENT" LEVEL="2" ISACTIVE="0" />
  <OBJECT OBJECTID="1171388" NAME="PARENT" LEVEL="2" ISACTIVE="1" />
  <OBJECT OBJECTID="1171421" NAME="CHILD 1" LEVEL="6" ISACTIVE="1" />
  <OBJECT OBJECTID="1171502" NAME="OLDER CHILD 2" LEVEL="6" ISACTIVE="0" />
  <OBJECT OBJECTID="1171502" NAME="CHILD 2" LEVEL="6" ISACTIVE="1" />
  <OBJECT OBJECTID="11715021" NAME="CHILD 21" LEVEL="7" ISACTIVE="1" />
  <OBJECT OBJECTID="1171612" NAME="CHILD 3" LEVEL="4" ISACTIVE="1" />
  <OBJECT OBJECTID="1171731" NAME="CHILD 4" LEVEL="4" ISACTIVE="1" />
  <OBJECT OBJECTID="1171761" NAME="CHILD 5" LEVEL="6" ISACTIVE="1" />
</ADDRESSOBJECTS>

file 2.xml is $doc2 – object hierarchy

<?xml version="1.0" encoding="utf-8"?>
<ITEMS>
  <ITEM OBJECTID="1170420" PARENTOBJID="1171380" ISACTIVE="1" />
  <ITEM OBJECTID="1170423" PARENTOBJID="1171380" ISACTIVE="1" />
  <ITEM OBJECTID="1171421" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171421" PARENTOBJID="1171388" ISACTIVE="0" />
  <ITEM OBJECTID="1171502" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171612" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171731" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171761" PARENTOBJID="1171388" ISACTIVE="1" />

  <ITEM OBJECTID="11715021" PARENTOBJID="1171502" ISACTIVE="1" />

  <ITEM OBJECTID="90000001" PARENTOBJID="11715021" ISACTIVE="1" />
  <ITEM OBJECTID="90000002" PARENTOBJID="11715021" ISACTIVE="1" />
  <ITEM OBJECTID="90000003" PARENTOBJID="1171761" ISACTIVE="1" />
  <ITEM OBJECTID="90000004" PARENTOBJID="1171761" ISACTIVE="1" />
  <ITEM OBJECTID="90000005" PARENTOBJID="1171421" ISACTIVE="1" />
</ITEMS>

file 3.xml is $doc3 – yet another objects are simple entities

<?xml version="1.0" encoding="utf-8"?>
<SUBOBJECTS>
  <SUBOBJECT OBJECTID="90000001" SUBOBJECTNUM="1" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000002" SUBOBJECTNUM="2" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000003" SUBOBJECTNUM="3" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000004" SUBOBJECTNUM="4" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000005" SUBOBJECTNUM="5" SUBOBJECTTYPE="9" ISACTIVE="1" />
</SUBOBJECTS>

and 4th file – 4.xml is $doc4 – parameters of simple entities

<?xml version="1.0" encoding="utf-8"?>
<PARAMS>
  <PARAM OBJECTID="90000001" CHANGEIDEND="0" TYPE="IND" VALUE="412784" UPDATEDATE="2019-07-10" STARTDATE="2012-07-26" ENDDATE="2079-06-06" />
  <PARAM OBJECTID="90000001" CHANGEIDEND="50098691" TYPE="OKTMO" VALUE="63649101" UPDATEDATE="2019-07-10" STARTDATE="2012-07-26" ENDDATE="2016-06-30" />
  <PARAM OBJECTID="90000001" CHANGEIDEND="0" TYPE="OKTMO" VALUE="63649101126" UPDATEDATE="2019-12-08" STARTDATE="2016-06-30" ENDDATE="2079-06-06" />
</PARAMS>

I hope I did not get confused and the hierarchy was given correctly in the examples. The output should be like this:

<ROOT OBJECTID="105037985" NAME="OTHER PARENT" LEVEL="2" ISACTIVE="1" />
<ROOT OBJECTID="1171388" NAME="PARENT" LEVEL="2" ISACTIVE="1" />
  <ELEMENT OBJECTID="1171421" NAME="CHILD 1" LEVEL="6" ISACTIVE="1">
    <SIMPLEENTITY OBJECTID="90000005"  NAME="5" LEVEL="10" ISACTIVE="1" />
  </ELEMENT>
  <ELEMENT OBJECTID="1171502" NAME="CHILD 2" LEVEL="6" ISACTIVE="1">
    <SUBELEMENT OBJECTID="11715021" NAME="CHILD 21" LEVEL="7" ISACTIVE="1">
         <SIMPLEENTITY OBJECTID="90000001" NAME="1" LEVEL="10" ISACTIVE="1" />
         <SIMPLEENTITY OBJECTID="90000002" NAME="2" LEVEL="10" ISACTIVE="1" />
    </SUBELEMENT>
  </ELEMENT>
  <ELEMENT OBJECTID="1171612" NAME="CHILD 3" LEVEL="4" ISACTIVE="1" />
  <ELEMENT OBJECTID="1171731" NAME="CHILD 4" LEVEL="4" ISACTIVE="1" />
  <ELEMENT OBJECTID="1171761" NAME="CHILD 5" LEVEL="6" ISACTIVE="1">
    <SIMPLEENTITY OBJECTID="90000003" NAME="3" LEVEL="10" ISACTIVE="1" />
    <SIMPLEENTITY OBJECTID="90000004" NAME="4" LEVEL="10" ISACTIVE="1" />
  </ELEMENT>
</ROOT>

That is, SUBOBJECT aka SIMPLEENTITY can belong to both OBJECT[@LEVEL = (7,8)] and OBJECT[@LEVEL = 6]

The resulting XSL file looks like this (files 2 – 4 are included in the XSL for convenience):

<?xml version="1.0" encoding="utf-8"?>
<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">

<!--
file 1.xml is $doc1 -main objects

file 2.xml is $doc2 - object hierarchy
file 3.xml is $doc3 - yet another objects are simple entities
file 4.xml is $doc4 - parameters of simple entities

PS: $doc2, $doc3, $doc4 see below at the end
-->

  <xsl:output indent="yes"/>
  
  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:key name="child" match="ITEM[@ISACTIVE=1]" use="@PARENTOBJID"/>
  
  <xsl:key name="object" match="OBJECT[@ISACTIVE=1]" use="@OBJECTID"/>
  
  <xsl:key name="subobject" match="SUBOBJECT[@ISACTIVE=1]" use="@OBJECTID"/>

  <xsl:key name="param" match="PARAM[@CHANGEIDEND=0]" use="@OBJECTID"/>
  
  <xsl:variable name="doc1" select="/"/>
  
  <xsl:template match="/">
    <xsl:apply-templates select="ADDRESSOBJECTS/OBJECT[@LEVEL = 2 and @ISACTIVE=1]"/>
  </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[@LEVEL = (4,5)]">
    <ELEMENT>
      <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('object', @OBJECTID, $doc1)"/>
    </ELEMENT>
  </xsl:template>

  <xsl:template match="OBJECT[@LEVEL = 6]">
    <ELEMENT>
      <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('object', @OBJECTID, $doc1)"/>
      <!-- xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('subobject', @OBJECTID, $doc3)"/ -->
    </ELEMENT>
  </xsl:template>

  <xsl:template match="OBJECT[@LEVEL = (7,8)]">
    <SUBELEMENT>
      <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('object', @OBJECTID, $doc1)"/>
      <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('subobject', @OBJECTID, $doc3)"/>
    </SUBELEMENT>
  </xsl:template>

  <xsl:template match="SUBOBJECT">
    <SIMPLEENTITY>
      <xsl:attribute name="OBJECTID" select="@OBJECTID"/>
      <xsl:attribute name="NAME" select="@SUBOBJECTNUM"/>
      <xsl:attribute name="LEVEL" select="10"/>
      <xsl:attribute name="ISACTIVE" select="@ISACTIVE"/>
      <xsl:apply-templates select="@TYPE|@VALUE, key('subobject', @OBJECTID, $doc3)/key('param', @OBJECTID, $doc4)"/>
    </SIMPLEENTITY>
  </xsl:template>

  <xsl:template match="PARAM">
      <xsl:attribute name="{@TYPE}" select="@VALUE"/>
  </xsl:template>

  <xsl:param name="doc2">
<ITEMS>
  <ITEM OBJECTID="1170420" PARENTOBJID="1171380" ISACTIVE="1" />
  <ITEM OBJECTID="1170423" PARENTOBJID="1171380" ISACTIVE="1" />
  <ITEM OBJECTID="1171421" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171421" PARENTOBJID="1171388" ISACTIVE="0" />
  <ITEM OBJECTID="1171502" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171612" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171731" PARENTOBJID="1171388" ISACTIVE="1" />
  <ITEM OBJECTID="1171761" PARENTOBJID="1171388" ISACTIVE="1" />

  <ITEM OBJECTID="11715021" PARENTOBJID="1171502" ISACTIVE="1" />

  <ITEM OBJECTID="90000001" PARENTOBJID="11715021" ISACTIVE="1" />
  <ITEM OBJECTID="90000002" PARENTOBJID="11715021" ISACTIVE="1" />
  <ITEM OBJECTID="90000003" PARENTOBJID="1171761" ISACTIVE="1" />
  <ITEM OBJECTID="90000004" PARENTOBJID="1171761" ISACTIVE="1" />
  <ITEM OBJECTID="90000005" PARENTOBJID="1171421" ISACTIVE="1" />
</ITEMS>
  </xsl:param>
  <xsl:param name="doc3">
<SUBOBJECTS>
  <SUBOBJECT OBJECTID="90000001" SUBOBJECTNUM="1" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000002" SUBOBJECTNUM="02" SUBOBJECTTYPE="9" ISACTIVE="0" />
  <SUBOBJECT OBJECTID="90000002" SUBOBJECTNUM="2" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000003" SUBOBJECTNUM="3" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000004" SUBOBJECTNUM="4" SUBOBJECTTYPE="9" ISACTIVE="1" />
  <SUBOBJECT OBJECTID="90000005" SUBOBJECTNUM="5" SUBOBJECTTYPE="9" ISACTIVE="1" />
</SUBOBJECTS>
  </xsl:param>
  <xsl:param name="doc4">
<PARAMS>
  <PARAM OBJECTID="90000001" CHANGEIDEND="0" TYPE="IND" VALUE="412784" UPDATEDATE="2019-07-10" STARTDATE="2012-07-26" ENDDATE="2079-06-06" />
  <PARAM OBJECTID="90000001" CHANGEIDEND="50098691" TYPE="OKTMO" VALUE="63649101" UPDATEDATE="2019-07-10" STARTDATE="2012-07-26" ENDDATE="2016-06-30" />
  <PARAM OBJECTID="90000001" CHANGEIDEND="0" TYPE="OKTMO" VALUE="63649101126" UPDATEDATE="2019-12-08" STARTDATE="2016-06-30" ENDDATE="2079-06-06" />
</PARAMS>
  </xsl:param>

</xsl:stylesheet>

Everything seems to be working. But… The problem is to map SIMPLEENTITY to OBJECT[@LEVEL = 6]
If I include the line

   <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('subobject', @OBJECTID, $doc3)"/>

into the match="OBJECT[@LEVEL = 6]" template (51st line), then an error occurs:

Error at char 2 in expression in xsl:apply-templates/@select on line
51 column 108 of tt4.xsl: XTDE0410 An attribute node (OBJECTID)
cannot be created after a child of the containing element. Most
recent element start tag was output at line 63 of module tt4.xsl In
template rule with match="OBJECT[@LEVEL eq 6]" on line 48 of tt4.xsl
invoked by xsl:apply-templates at file:/home/sergey/Projects/xslt/tt4.xsl#38 In template rule with
match="OBJECT[@LEVEL eq 2]" on line 36 of tt4.xsl
invoked by xsl:apply-templates at file:/home/sergey/Projects/xslt/tt4.xsl#33 In template rule with
match="/" on line 32 of tt4.xsl An attribute node (OBJECTID) cannot be
created after a child of the containing element. Most recent element
start tag was output at line 63 of module tt4.xsl

I can’t figure out how to fix it 🙁

>Solution :

I think you simply want

  <xsl:template match="OBJECT[@LEVEL = 6]">
    <ELEMENT>
      <xsl:apply-templates select="@*, key('child', @OBJECTID, $doc2)/key('object', @OBJECTID, $doc1), key('child', @OBJECTID, $doc2)/key('subobject', @OBJECTID, $doc3)"/>
    </ELEMENT>
  </xsl:template>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading