XSL - Subnodes afsplitsen naar aparte bestanden

Uit De Vliegende Brigade
(Doorverwezen vanaf XSL - Subnodes afsplitsen)
Ga naar: navigatie, zoeken

Subnodes afsplitsen heeft betrekking op flattening XML files, oftewel lineair maken van XML-data. Het meest aansprekende voorbeeld voor mij: Een orderdocument dat bestaat uit een orderkopje en een variabel aantal orderregels.

In dit artikel worden orderkopje en orderregels in twee aparte tabellen opgeslagen. In dit geval is er (geloof ik) maar één subnode per hoofdnode. Als dit er meer zijn, zou je bv. een tussentabel kunnen genereren - Da's waarschijnlijk niet zo'n grote stap.

Afkomstig van [1]:

Hoe het werkt

Er worden twee entiteiten afgesplitst, die meerdere keren voorkomen in het bronbestand, en niet uniek identificeerbaar zijn:

  • Correspondent wordt bv. niet afgesplitst, want daarvan is er maar eentje per record
  • Assignor en Document worden wél afgesplitst en van id voorzien, want daarvan zijn er twee per assignment-record.

Bronbestand parser.py

#!/usr/bin/python

################################################################
# Import
################################################################
#
import os
import lxml.etree as ET

################################################################
# Split off "Assignor"
################################################################
#
# dom = Original XML file
##########################################
#
dom = ET.parse('Assignment.xml')

# Load Assignor-XSLT file
###########################################
#
xslt = ET.parse('Assignor.xsl')

# transform to new file "newdom"
###########################################
#
transform = ET.XSLT(xslt)
newdom = transform(dom)    

# Write "newdom" to file "Output-Assignor.xml"
############################################################
#
xmlfile = open(os.path.join('', 'Output-Assignor.xml'),'wb')
xmlfile.write(newdom)
xmlfile.close()    

################################################################
# Split off "Document"
################################################################
#
xslt = ET.parse('Document.xsl')    
transform = ET.XSLT(xslt)
newdom = transform(dom)

xmlfile = open(os.path.join('', 'Output-Document.xml'),'wb')
xmlfile.write(newdom)
xmlfile.close()

Bronbestand Assignment.xml

<?xml version="1.0" encoding="UTF-8"?>
<assignment>
    <assignment-record>
        <reel-no>28879</reel-no>
        <frame-no>97</frame-no>
        <last-update-date><date>20120903</date></last-update-date>
        <recorded-date><date>20120830</date></recorded-date>
        <page-count>4</page-count>
        <correspondent>
            <name>LEE, HONG, DEGERMAN, KANG & WAIMEY</name>
            <address-1>660 S. FIGUEROA ST., 23RD FL.</address-1>
            <address-2>LOS ANGELES, CA 90017</address-2>
        </correspondent>
        <conveyance-text>ASSIGNMENT OF ASSIGNORS INTEREST (SEE DOCUMENT FOR DETAILS).</conveyance-text>
    </assignment-record>
    <assignors>
        <assignor>
            <name>WOO, SUNGHO</name>
            <execution-date><date>20120806</date></execution-date>
        </assignor>
            <assignor>
                <name>CHOI, JAEYOUNG</name>
                <execution-date><date>20120806</date></execution-date>
        </assignor>
    </assignors>
    <docproperties>
        <property>
            <document-id>
                <country>US</country>
                <doc-number>13277056</doc-number>
                <kind>X0</kind>
                <date>20111019</date>
            </document-id>
            <document-id>
                <country>US</country>
                <doc-number>20120213136</doc-number>
                <kind>A1</kind>
                <date>20120823</date>
            </document-id>
            <title lang="en">SYSTEMS AND METHODS FOR CONTROLLING SENSOR DEVICES IN MOBILE DEVICES</title>
     </property>
    </docproperties>
</assignment>

Bronbestand Assignor.xsl

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

  <xsl:template match="/assignment">
    <xsl:copy>      
      <xsl:apply-templates select="descendant::assignor"/>
    </xsl:copy>
  </xsl:template>  

  <xsl:template match="assignor">
    <xsl:copy>
      <assign_id>
          <xsl:value-of select="ancestor::assignment/assignment-record/reel-no"/>
      </assign_id>
      <xsl:copy-of select="name"/>
      <xsl:copy-of select="execution-date/date"/>        
    </xsl:copy>
  </xsl:template>

</xsl:transform>

Bronbestand Document.xsl

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

  <xsl:template match="/assignment">
    <xsl:copy>
      <xsl:apply-templates select="descendant::document-id"/>
    </xsl:copy>
  </xsl:template>  

  <xsl:template match="document-id">
    <xsl:copy>
      <assign_id>
          <xsl:value-of select="ancestor::assignment/assignment-record/reel-no"/>
      </assign_id>
      <xsl:copy-of select="*"/>        
    </xsl:copy>
  </xsl:template>

</xsl:transform>

Output-bestand Output-Assignor.xml

<?xml version="1.0" encoding="UTF-8"?>
<assignment>
  <assignor>
    <assign_id>28879</assign_id>
    <name>WOO, SUNGHO</name>
    <date>20120806</date>
  </assignor>
  <assignor>
    <assign_id>28879</assign_id>
    <name>CHOI, JAEYOUNG</name>
    <date>20120806</date>
  </assignor>
</assignment>

Output-bestand Output-Document.xml

<?xml version="1.0" encoding="UTF-8"?>
<assignment>
  <document-id>
    <assign_id>28879</assign_id>
    <country>US</country>
    <doc-number>13277056</doc-number>
    <kind>X0</kind>
    <date>20111019</date>
  </document-id>
  <document-id>
    <assign_id>28879</assign_id>
    <country>US</country>
    <doc-number>20120213136</doc-number>
    <kind>A1</kind>
    <date>20120823</date>
  </document-id>
</assignment>

Assignor.xsl - Uitgelegd

Met pseudo-code:

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


  #################################################################################
  # Copy all assignor-entries
  #################################################################################
  #
  # Select all "assignment" records
  ###################################
  #
  <xsl:template match="/assignment">

    # Copy all assignor-entries
    #########################################################
    #
    <xsl:copy>      
      <xsl:apply-templates select="descendant::assignor"/>
    </xsl:copy>

  # Finish
  ####################################    
  </xsl:template>  


  #################################################################################
  # Add id to these assignor-entries 
  #################################################################################
  #
  # Select all "assignor" records
  ###############################
  #
  # This are probably all the newly copied records, since I can't imagine that
  # it otherwise could copy data data is nested in other data
  #
  <xsl:template match="assignor">
    <xsl:copy>

      # Create tag "assign_id"
      ########################
      #
      # You can tell this is a tag and not a command, as it doesn't include "xsl:"
      #
      <assign_id>

          # Copy value of reel-no, two levels above
          #########################################
          #
          # "value-of" happens within this copy-routine. It copies the value
          # of the given tag, two levels up
          #
          <xsl:value-of select="ancestor::assignment/assignment-record/reel-no"/>

      # Close "assign_id" tag
      #######################
      #
      </assign_id>

      # ???
      ############################
      #
      <xsl:copy-of select="name"/>

      # Change "<execution-date><date>" to "<date>"
      #############################################
      #
      <xsl:copy-of select="execution-date/date"/>        
    </xsl:copy>
  </xsl:template>

</xsl:transform>

Hoe verder met importeren?

  • Uit bestand Assignment.xml, importeer je entiteiten assignment-record
  • Uit bestand Output-Assignor.xml, importeer je entiteiten assignor
  • Uit bestand Output-Document.xml, importeer je entiteiten document.

Verwijder subnodes uit hoofdbestand

Het voorbeeld hiervoor, is precies wat ik zoek, maar dan graag dat de afgesplitste gedeeltes worden verwijderd uit het hoofdbestand: Dat houdt het overzichtelijk. Ik weet namelijk niet vantevoren wat ik allemaal ga tegenkomen aan structuren.

Simpel:

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

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

<xsl:template match="assignor|document-id" />

</xsl:stylesheet>

Bronnen