Including an XML file in an XML/XSL file
Asked Answered
W

6

24

So currently I'm doing some XML-> XSLT-> (HTML5/CSS3) work. Right now I have a menu.xml file, and I'd like to include it in either the XSL file or the XML page. I've done lots of searching, but I'm unable to find a straightforward answer.

So, how do I include an XML file in to another XML file or in to a XSL file?

Edit: By include, I mean referencing/loading it from another file, not copy and pasting it or simply embedding it.

Wyoming answered 16/1, 2011 at 1:46 Comment(3)
By 'include' it, do you mean that you want to copy/paste the contents of menu.xml into another file and host it in there without problems, or do you mean that you want some way to remotely reference menu.xml from the host file, or do you mean that you want your web server to copy the contents of menu.xml into the host file before serving it, as though you had copy/pasted the contents yourself?Eugenieeugenio
Ah, I kind of meant it like referencing it from the file, not including it. Damn my programming speak. I'm too use to C.Wyoming
Good question, +1. See my answer for explanation and two complete code examples that answer your questions. :)Quass
Q
37

I. Here is how any XML document or fragment can be embedded in an XSLT stylesheet and used during the transformation:

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

 <my:menu>
   <menu>
     <choice>A</choice>
     <choice>B</choice>
     <choice>C</choice>
   </menu>
 </my:menu>

 <xsl:template match="/">
  <xsl:copy-of select="document('')/*/my:menu/*"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on any XML document (not used in this example), the wanted result (just copying the XML) is produced:

<menu xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">
   <choice>A</choice>
   <choice>B</choice>
   <choice>C</choice>
</menu>

Remember: Any XML can be embedded into an XSLT stylesheet, provided it is wrapped into a namespaced element (the namespace not the XSLT namespace) and this wrapping element is at the global level (a child of the <xsl:stylesheet> (top) element).

II. Accessing the XML menu file that resides in a separate XML file:

To do this we have to change only slightly the previous example:

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


 <xsl:template match="/">
  <xsl:copy-of select="document('menu.XML')/*"/>
 </xsl:template>
</xsl:stylesheet>

If the menu XML file is in the 'menu.XML' file (in the same directory as the XSLT stylesheet file, then this transformation produces exactly the same result as the previous:

<menu>
   <choice>A</choice>
   <choice>B</choice>
   <choice>C</choice>
</menu>

Do note: In both cases we are using the standard XSLT function document()

Typically, one defines a global-level variable, whose value is the result of calling the document() function. Then this variable and its contents is accessed via XPath expressions during the transformation.

Quass answered 16/1, 2011 at 3:21 Comment(4)
Is it possible to apply templates on the included (menu.xml) document as well, just like any other element?Battleplane
@lysergic-acid: Yes, why would you think this isn't possible? Just try <xsl:apply-templates select="document('menu.XML')/*"/> or whatever you like to see that this works as it is supposed to.Quass
Add the attribute exclude-result-prefixes="my" to the xsl:stylesheet element in the first example to remove the namespace declaration from the output.Stomachache
@Burkart, the exclude-result-prefixes attribute only affects literal-result-elements -- not copied ones. One can write the code in a way to prevent copying of namespace nodes, but this would make the current code rather unreadable and is not directly related to the current question. I have provided answers to questions asking how to transform an XML document to exclude namespaces -- just search for such questions and see how this is done.Quass
R
6

So, how do I include an XML file in to another XML file or in to a XSL file?

You can use an external entity to reference the menu.xml file and include the content into either an XML file or the XSLT (or both).

By include, I mean referencing/loading it from another file, not copy and pasting it or simply embedding it.

By using external entities, you can reference/load the menu.xml content from external files and do not have to duplicate the XML content.

For instance, if you wanted the menu.xml content included in your XSLT, you would declare the external entity in your XSLT like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stylesheet [
     <!ENTITY menu SYSTEM "./menu.xml">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

and could then reference it like you would any other entity:

&menu;

When the XSLT parsed, the entity reference will be expanded and the XML content of the menu.xml will be included as part of the XSLT document as if you had copy/pasted into the spot where the entity reference was.

Rooseveltroost answered 16/1, 2011 at 15:17 Comment(3)
It's not a bad answer, but this is not "referencing/loading" but replacement in the surface syntax rarer than in the logical abstraction.Macron
wrapping &menu; in a local object would allow all xpaths within the menu xml to be used within xslt. ex: <externalmenu> &menu; </externalmenu> and then <xsl:for-each select="document('')/*/externalmenu/xpath-in-menu-xml">Brevity
This appears to work in Safari, but has no effect on Firefox.Homogeneity
C
1

I have a blog post on this. You use document() to get the other XML and pass around the content using parameters.

Crystallo answered 24/9, 2013 at 10:32 Comment(2)
Perhaps you could show your work here as it applies to the question asked?Dumah
@vhs, no need. I did it in my answer. 9 years later but hey.Locarno
B
1

I was unable to get the code to work that people provided. However I did get a solution after trying several things. Name an xml file clGroup.xml (first two letters don't matter, but I am finding "Group" is required. This xml file will reference the xml you want to parse.

clGroup xml:

 <?xml version='1.0' encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="calendarGroup.xslt" ?>
<groups>
  <groupRef>caGroup.xml</groupRef>
</groups>

Now name a file caGroup.xml and put in the following code:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<topics xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <topic>
        <topicstopic>Calendar</topicstopic>
        <topicsubmitter>Calendar</topicsubmitter>
        <creatoremail>[email protected]</creatoremail>
    </topic>
    <topic>
        <name>Week1</name>
        <sunday/>
        <monday/>
        <tuesday>Test01 Young Men Weekly</tuesday>
        <wednesday>02 Elders Home Teaching Message Birthday</wednesday>
        <thursday>03 </thursday>
        <friday>04 </friday>
        <saturday>05  #Young Men Weekly</saturday>
    </topic>
    <topic>
        <name>Week2</name>
        <sunday>06 Fast Sunday</sunday>
        <monday>07 FHE</monday>
        <tuesday>08 Young Men Tonight #Young Men Weekly going to Bishops storehouse 6PM to 7PM Young Men Weekly</tuesday>
        <wednesday>09 </wednesday>
        <thursday>10 Scout Round Table at 7 PM </thursday>
        <friday>11 </friday>
        <saturday>12 </saturday>
    </topic>
  </topics>

Now here is my XSLT file calendarGroup.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="html" 
            version="1.0" 
            encoding="UTF-8" 
            indent="yes"/>

<xsl:template match="groups/groupRef">
  <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Table</title>
      <meta charset="utf-8" />
    </head>
    <body>
      <table border="1">
        <tr>
          <th>Sunday</th>
          <th>Monday</th>
          <th>Tuesday</th>
          <th>Wednesday</th>
          <th>Thursday</th>
          <th>Friday</th>
          <th>Saturday</th>
        </tr>         
        <xsl:for-each select="document(.)//topic">
          <xsl:if test="string(name) != ''">
          <tr>
            <td>
              <xsl:value-of select="sunday"/>
            </td>
            <td>
              <xsl:value-of select="monday"/>
            </td>
            <td>
              <xsl:value-of select="tuesday"/>
            </td>
            <td>
              <xsl:value-of select="wednesday"/>
            </td>
            <td>
              <xsl:value-of select="thursday"/>
            </td>
            <td>
              <xsl:value-of select="friday"/>
            </td>
            <td>
              <xsl:value-of select="saturday"/>
            </td>
          </tr>
          </xsl:if>
        </xsl:for-each>
      </table>
    </body>
  </html>
</xsl:template>
</xsl:stylesheet>

The benefit of setting up my code this way is I can easily create the xml from an excel file and not half to put in a declaration statement in the newly created file. You do need to have the format "xxGroup" for this to work together. You can add more groups to the clGroup file and they will parse in the XSLT allowing you to include more xml in the same XSLT parser. Make sure they are all in the same directory.

Bataan answered 4/12, 2016 at 21:2 Comment(2)
There is no magic in the string "Group" - you can use any names you like.Meletius
Thanks! It is weird because when I change the names out of the wording it doesn't work for some reason for me. The nice thing I see is you can add several XML files to the grouping and it will parse them.Bataan
L
1

This works in xslt versions 1-3. I've been using this for over 20 years.

in the xslt file declare variable

<xsl:variable name="myexternaldata" select="document('/home/username/Documents/path/myxmlfile.xml')"/>

or if the file is in the same path, just

<xsl:variable name="myexternaldata" select="document('myxmlfile.xml')"/>

in the xslt file use variable with $variablename

<xsl:value-of select="$myexternaldata/rootelementname/child/etc/@myattribute"/>

I really can't think of anything simpler or more straightforward than this. Be sure to include the root element name in your path unless you're doing //elementname or something like that. The $myexternaldata is NOT the root element. It's the document which of course contains the root.

  • no need to copy the data
  • data and code are kept separate
  • no need to refer to one xml from another, which is confusing and overly coupled
  • this loads the data, which is necesary, but doesn't embed it inside the stylesheet. I'm not sure what the implications of that are, but it seems superflous
Locarno answered 27/9, 2022 at 13:26 Comment(0)
S
0

If simply embedding the file inside the XML file is not a solution then adding a url field that the interpreting program reads should be added- you're basically asking for the equivalent of an include(menu.xml) or require(xml), but in an XML file.

So since you are writing the program that is intepretting the file you can add an <externalMenuTagThatYouDecideToAddToTheBaseXmlFile> tag or whatever you want to call it that you will read and insert menu.xml's root note the place of the <externalMenuTagThatYouDecideToAddToTheBaseXmlFile>.

Good luck and may your programs always compile.

Sophistic answered 16/1, 2011 at 2:20 Comment(1)
I'm not really writing an interpreter, I'm writing a website. >_>Wyoming

© 2022 - 2024 — McMap. All rights reserved.