Exporter un document Calc en XML

Encore une fois, les choses ne sont pas si simples qu'il n'y paraît...

Ici le besoin consiste à exporter un classeur OpenOffice Calc en XML, selon une structure personnalisée. Le logiciel offre déjà la possibilité d'exporter sous forme d'XML à la sauce Micro$oft : autant dire que le résultat n'est pas glorieux :

Le document Calc :

Le XML produit (Fichier > Enregistrer sous... > Microsoft Excel 2003 XML) :

<?xml version="1.0" encoding="utf-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:x2="http://schemas.microsoft.com/office/excel/2003/xml" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:x="urn:schemas-microsoft-com:office:excel">
  <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
    <Colors>
      <Color>
        <Index>3</Index>
        <RGB>#c0c0c0</RGB>
      </Color>
      <Color>
        <Index>4</Index>
        <RGB>#ff0000</RGB>
      </Color>
    </Colors>
  </OfficeDocumentSettings>
  <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
    <WindowHeight>9000</WindowHeight>
    <WindowWidth>13860</WindowWidth>
    <WindowTopX>240</WindowTopX>
    <WindowTopY>75</WindowTopY>
    <ProtectStructure>False</ProtectStructure>
    <ProtectWindows>False</ProtectWindows>
  </ExcelWorkbook>
  <Styles>
    <Style ss:ID="Default" ss:Name="Default" />
    <Style ss:ID="Result" ss:Name="Result">
      <Font ss:Bold="1" ss:Italic="1" ss:Underline="Single" />
    </Style>
    <Style ss:ID="Result2" ss:Name="Result2">
      <Font ss:Bold="1" ss:Italic="1" ss:Underline="Single" />
      <NumberFormat ss:Format="Euro Currency" />
    </Style>
    <Style ss:ID="Heading" ss:Name="Heading">
      <Alignment ss:Horizontal="Center" />
      <Font ss:Bold="1" ss:Italic="1" ss:Size="16" />
    </Style>
    <Style ss:ID="Heading1" ss:Name="Heading1">
      <Alignment ss:Horizontal="Center" ss:Rotate="90" />
      <Font ss:Bold="1" ss:Italic="1" ss:Size="16" />
    </Style>
    <Style ss:ID="co1" />
    <Style ss:ID="ta1" />
    <Style ss:ID="ta_extref" />
  </Styles>
  <ss:Worksheet ss:Name="Exemple1">
    <Table ss:StyleID="ta1">
      <Column ss:Span="1" ss:Width="64.2614" />
      <Row ss:Height="12.8409">
        <Cell>
          <Data ss:Type="String">Donnée 1</Data>
        </Cell>
        <Cell>
          <Data ss:Type="String">Donnée 1.1</Data>
        </Cell>
      </Row>
      <Row ss:Height="12.8409">
        <Cell>
          <Data ss:Type="String">Donnée 2</Data>
        </Cell>
        <Cell>
          <Data ss:Type="String">Donnée 1.2</Data>
        </Cell>
      </Row>
    </Table>
    <x:WorksheetOptions />
  </ss:Worksheet>
  <ss:Worksheet ss:Name="Exemple2">
    <Table ss:StyleID="ta1">
      <Column ss:Span="1" ss:Width="64.2614" />
      <Row ss:Height="12.8409">
        <Cell>
          <Data ss:Type="String">Feuille 2 test</Data>
        </Cell>
        <Cell>
          <Data ss:Type="String">Autre valeur</Data>
        </Cell>
      </Row>
    </Table>
    <x:WorksheetOptions />
  </ss:Worksheet>
</Workbook>

Pas très lisible tout ça...

Avant de continuer, il faut savoir que les fichiers Calc (.ods) ne sont en fait que des fichiers zip contenant des XML. La structure de base du classeur, tel que lu par OpenOffice est la suivante :

  • content.xml
  • meta.xml
  • mimetype.xml
  • settings.xml
  • styles.xml

Tous ces fichiers décrivent le contenu, la présentation et les métas informations constituant le classeur. Le plus intéressant est le premier. En voici un extrait :

  <office:body>
    <office:spreadsheet>
      <table:table table:name="Exemple1" table:style-name="ta1" table:print="false">
        <office:forms form:automatic-focus="false" form:apply-design-mode="false" />
        <table:table-column table:style-name="co1" table:number-columns-repeated="2" table:default-cell-style-name="Default" />
        <table:table-row table:style-name="ro1">
          <table:table-cell office:value-type="string">
            <text:p>Donnée 1</text:p>
          </table:table-cell>
          <table:table-cell office:value-type="string">
            <text:p>Donnée 1.1</text:p>
          </table:table-cell>
        </table:table-row>
        <table:table-row table:style-name="ro1">
          <table:table-cell office:value-type="string">
            <text:p>Donnée 2</text:p>
          </table:table-cell>
          <table:table-cell office:value-type="string">
            <text:p>Donnée 1.2</text:p>
          </table:table-cell>
        </table:table-row>
      </table:table>
      <table:table table:name="Exemple2" table:style-name="ta1" table:print="false">
        <table:table-column table:style-name="co1" table:number-columns-repeated="2" table:default-cell-style-name="Default" />
        <table:table-row table:style-name="ro1">
          <table:table-cell office:value-type="string">
            <text:p>Feuille 2 test</text:p>
          </table:table-cell>
          <table:table-cell office:value-type="string">
            <text:p>Autre valeur</text:p>
          </table:table-cell>
        </table:table-row>
      </table:table>
    </office:spreadsheet>
  </office:body>

On retrouve ici les informations contenues dans les cellules de chaque feuille.

Ainsi, puisque la structure des documents Calc est basée sur du XML, si l'on désire l'exporter sous un autre format, il faudra passer par... XSLT!

Admettons que nous voulions exporter le document présenté plus haut sous la forme :

<mapping>
 <Exemple1>
  <data col1="Donnée 1" col2="Donnée 1.1" />
  <data col1="Donnée 2" col2="Donnée 1.2" />
 </Exemple1>
 <Exemple2>
 ...
</mapping>

Il faudra donc trouver la feuille de transformation XSL convenable. Par exemple, celle-ci (le code est perfectible mais est présenté à titre d'exemple) :

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
 xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
 exclude-result-prefixes="office table">
 
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>
 
<!-- To avoid annoying header data -->
<xsl:template match="office:meta" />
<xsl:template match="office:settings" />
<xsl:template match="office:styles" />
<xsl:template match="office:master-styles" />
<xsl:template match="office:automatic-styles" />
<xsl:template match="office:font-face-decls" />
<xsl:template match="office:scripts" />
<xsl:template match="office:font-face-decls" />
 
<!-- parse document -->
<xsl:template match="office:spreadsheet">
 <xsl:element name="mapping">
  <xsl:for-each select="table:table">
   <!-- take only sheets that have real data (multiple columns) -->
   <xsl:if test="table:table-column[@table:number-columns-repeated]">
    <xsl:variable name="tablename" select="@table:name" />
    <xsl:element name="{$tablename}">
     <xsl:for-each select="table:table-row">
      <xsl:element name="item">
       <xsl:for-each select="table:table-cell">
        <xsl:variable name="attrNameV1" select="'col1'" />
        <xsl:variable name="attrNameV3" select="'col2'" />
        <xsl:if test="position() = 1">
         <xsl:attribute name="{$attrNameV1}">
          <xsl:value-of select="text:p" />
         </xsl:attribute>
        </xsl:if>
        <xsl:if test="position() = 2">
         <xsl:attribute name="{$attrNameV3}">
          <xsl:value-of select="text:p" />
         </xsl:attribute>
        </xsl:if>
       </xsl:for-each>
      </xsl:element>
     </xsl:for-each>
    </xsl:element>
   </xsl:if>
  </xsl:for-each>
 </xsl:element>
</xsl:template>
</xsl:stylesheet>

En détails, voici comment elle fonctionne :

  • Les namespaces déclarés correspondent à ceux attendus par OpenOffice pour présenter ses propres balises.
  • Le premier bloc de templates supprime le traitement de tout ce qui est méta-infos, styles et autres scripts éventuellement contenus dans le classeur.
  • L'élement table:table décrit chaque feuille du classeur
  • Le test xsl:if permet de ne pas traiter les feuilles ne contenant aucune donnée (chaque feuille contenant par défault toujours au moins une cellule vide)
  • Enfin, on parcour chaque ligne (table:table-row) et chaque cellule (table:table-cell) pour en extraire le contenu (text:p)

Pour rendre cet export disponible dans OpenOffice, il faut le configurer. Pour ce faire : Outils > Paramétrages du filtre XML... > Nouveau... Et renseigner le chemin vers notre XSLT dans la section "XSLT pour export"

Notre export sera finalement réalisable depuis le menu Fichier > Exporter...

Pourquoi faire simple quand on peut faire compliqué!


Fichier(s) joint(s) :

0 commentaires: