wordpress hit counter
Use XSLT to transform XML to Open XML - OpenXML Developer - Blog - OpenXML Developer
Goodbye and Hello

OpenXmlDeveloper.org is Shutting Down

There is a time for all good things to come to an end, and the time has come to shut down OpenXmlDeveloper.org.

Screen-casts and blog posts: Content on OpenXmlDeveloper.org will be moving to EricWhite.com.

Forums: We are moving the forums to EricWhite.com and StackOverflow.com. Please do not post in the forums on OpenXmlDeveloper.org. Instead, please post in the forums at EricWhite.com or at StackOverflow.com.

Please see this blog post for more information about my plans moving forward.  Cheers, Eric

Use XSLT to transform XML to Open XML

Use XSLT to transform XML to Open XML

Rate This
  • Comments 18

by Bryce Telford

SO WHAT IS IT?

I set out to create an OpenXML Document from an xml file using XSLT and the Open XML SDK 2.0. While it is possible to create a fully fledged Open XML document from XSLT and XML, a few shortcuts make the process a lot easier and faster.

Included in the source are some sample documents. In this case I’m using:

1.     hl7.xml – The Data – an XML based HL7 Medical File as the data source

2.     template.docx – The Template – An existing document to model the output on

3.     transform.xslt – The Transformation – The XSLT specifies how the data will fit into the template.  

Although I’ve used an xml file on the file system for my data, any well formed data source that XSLT can interact with could work with only minor modifications to the application.

HOW DO YOU USE IT?

 To run the application using the provided sample data and template, run the following from the source directory

bin\Debug\OOXMLSpike.exe ..\..\SampleFiles\hl7.xml ..\..\SampleFiles\template.docx ..\..\SampleFiles\transform.xslt ..\..\SampleFiles\output.docx

This will produce an output.docx file in the SampleFiles directory.

HOW'S IT DONE?

Here we’ll step through a simple example from start to finish illustrating the different skills and techniques required.

The process I followed to create my XSLT and then use that to create the Open XML document involved the following steps

1.     Design the output document (template)

2.     Remove document.xml from the template

3.     Create an XSLT using the document.xml as the base.

4.     Run the console application with the following parameters, <datasource>.xml <template>.docx<transform>.xslt <outputFilename>.docx

 

Step 1: Design the Output Document (template)

You can create a document using your chosen Open XML Document programme, in this case I used Microsoft Word and created various areas to be populated. An example is the title section of the document. Once all the required formatting, static content, layout and other preferences have been set on this template you can save and close the document and proceed to the next step.

 

Step 2: Remove the document.xml from the Open XML Document

Locate your file created above, copy it and change the extension to .zip.

Open the zip file, open the word folder and copy the document.xml to your working directory

Step 3: Create an XSLT using the document.xml as the base

Rename document.xml to transform.xslt (or any name of your choosing). Open this in your desired xslt editor, I used Microsoft Visual Studio 2008 for this. Convert the xml file into an xslt by following these steps:

1.     Remove the <?xml version="1.0" encoding="UTF-8" standalone="yes"?> tag

2.     Surround the entire text (<w:document>) with

<xsl:stylesheet

     version="1.0"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xmlns:xs="http://www.w3.org/2001/XMLSchema"

     xmlns:n2="urn:hl7-org:v3"

     exclude-result-prefixes="n2 xs xsi xsl"

     >

     <xsl:output method="xml" encoding="UTF-8" indent="yes"/>

     <xsl:template match="/">

----EXISTING DOCUMENT---

      </xsl:template>

</xsl:stylesheet>

3.     Now modify the sections you want to replace with data with the expected XSLT mark-up, I’ll use the title example here:

Before:

<w:p w:rsidR="00720332" w:rsidRPr="006078F7" w:rsidRDefault="00B308B8" w:rsidP="004A3019">

      <w:pPr>

            <w:jc w:val="center"/>

            <w:rPr>

                  <w:color w:val="FF0000"/>

                  <w:sz w:val="36"/>

                  <w:szCs w:val="36"/>

            </w:rPr>

      </w:pPr>

      <w:r>

            <w:rPr>

                  <w:b/>

                  <w:color w:val="FF0000"/>

                  <w:sz w:val="36"/>

                  <w:szCs w:val="36"/>

            </w:rPr>

            <w:t>Title</w:t>

      </w:r>

</w:p>

After:

<w:p w:rsidR="00720332" w:rsidRPr="006078F7" w:rsidRDefault="00971BFB" w:rsidP="004A3019">

      <w:pPr>

            <w:jc w:val="center"/>

            <w:rPr>

                  <w:color w:val="FF0000"/>

                  <w:sz w:val="36"/>

                  <w:szCs w:val="36"/>

            </w:rPr>

      </w:pPr>

      <w:r w:rsidRPr="006078F7">

            <w:rPr>

                  <w:b/>

                  <w:color w:val="FF0000"/>

                  <w:sz w:val="36"/>

                  <w:szCs w:val="36"/>

            </w:rPr>

            <w:t>

<xsl:value-of select="string($var1_instance/n2:ClinicalDocument/n2:title)"/>

            </w:t>

      </w:r>

</w:p>

 

Step 4: Run the Console Application

You can now run the console application with the new XSLT, this will produce the output file specified.

 

CONSOLE APPLICATION

This is a fairly simple C# application which runs the xslt against the data source, creates a new file from the template supplied and then calls the Open XML SDK to replace the body of the document with the processed xml. This is then written to the file system.

This could easily be repackaged into a larger application and used to process xml generated from a custom data source.

DIFFERENT OPERATIONS

In the sample application I’ve used various xsl transformations to create the desired display of data. These include:

·         xsl:value-of (Singular)

·         xsl:for-each (Looping)

·         xsl:if (Conditional)

Combining these with the markup already generated it is easy to dynamically populate various Word constructs including but not limited to:

·         Word Art

·         Text Boxes

·         Headings

·         Tables

·         Styles

Word Art Injection

The highlighted blocks are the only sections that have changed from the original Word markup. Here we retrieve the Oraganisation name from the data, assign it to a variable and then use that variable to populate the string section of the WordArt. The string section stores the text displayed by the WordArt.

<w:pict>

      <v:shapetype id="_x0000_t144" coordsize="21600,21600" o:spt="144" adj="11796480" path="al10800,10800,10800,10800@2@14e">

            <v:formulas>

                  <v:f eqn="val #1"/>

                  <v:f eqn="val #0"/>

                  <v:f eqn="sum 0 0 #0"/>

                  <v:f eqn="sumangle #0 0 180"/>

                  <v:f eqn="sumangle #0 0 90"/>

                  <v:f eqn="prod @4 2 1"/>

                  <v:f eqn="sumangle #0 90 0"/>

                  <v:f eqn="prod @6 2 1"/>

                  <v:f eqn="abs #0"/>

                  <v:f eqn="sumangle @8 0 90"/>

                  <v:f eqn="if @9 @7 @5"/>

                  <v:f eqn="sumangle @10 0 360"/>

                  <v:f eqn="if @10 @11 @10"/>

                  <v:f eqn="sumangle @12 0 360"/>

                  <v:f eqn="if @12 @13 @12"/>

                  <v:f eqn="sum 0 0 @14"/>

                  <v:f eqn="val 10800"/>

                  <v:f eqn="cos 10800 #0"/>

                  <v:f eqn="sin 10800 #0"/>

                  <v:f eqn="sum @17 10800 0"/>

                  <v:f eqn="sum @18 10800 0"/>

                  <v:f eqn="sum 10800 0 @17"/>

                  <v:f eqn="if @9 0 21600"/>

                  <v:f eqn="sum 10800 0 @18"/>

            </v:formulas>

            <v:path textpathok="t" o:connecttype="custom" o:connectlocs="10800,@22;@19,@20;@21,@20"/>

            <v:textpath on="t" style="v-text-kern:t" fitpath="t"/>

            <v:handles>

                  <v:h position="@16,#0" polar="10800,10800"/>

            </v:handles>

            <o:lock v:ext="edit" text="t" shapetype="t"/>

      </v:shapetype>

      <v:shape id="_x0000_s1026" type="#_x0000_t144" style="position:absolute;margin-left:0;margin-top:.85pt;width:440.25pt;height:65.25pt;z-index:251660288;mso-position-horizontal:center" o:borderbottomcolor="this" fillcolor="black">

            <v:shadow color="#868686"/>

            <xsl:variable name="organisationName" select="string(n2:ClinicalDocument/n2:custodian/n2:assignedCustodian/n2:representedCustodianOrganization/n2:name)"/>

            <v:textpath style="font-family:&quot;Arial Black&quot;" fitshape="t" trim="t" string="{$organisationName}"/>

      </v:shape>

</w:pict>

Repeating Data

Below is a section of repeating data used in one of the text boxes. It iterates through each ‘item’ in the list node (xsl:for-each). For each ‘item’ it will print out the static formatting and also the value of that item in the text area.

<xsl:for-each select="n2:ClinicalDocument/n2:component/n2:StructuredBody/n2:component[4]/n2:section/n2:text/n2:list/n2:item">

      <w:p w:rsidR="00E05032" w:rsidRPr="00B741A8" w:rsidRDefault="00E05032" w:rsidP="00E05032">

            <w:pPr>

                  <w:pStyle w:val="ListParagraph"/>

                  <w:numPr>

                        <w:ilvl w:val="0"/>

                        <w:numId w:val="5"/>

                  </w:numPr>

            </w:pPr>

            <w:r>

                  <w:rPr>

                        <w:noProof/>

                  </w:rPr>

                  <w:t>

                        <xsl:value-of select="string(.)"/>

                  </w:t>

            </w:r>

      </w:p>

</xsl:for-each>

 

Attachment: OOXMLXSLTTransform.zip
Page 1 of 2 (18 items) 12