1

I have this .XML file:

<?xml version='1.0' encoding='utf-8'?>
<document id="random_id_doc" class"doc">
  <properties>
    <randomProperty name="name"><![CDATA[Name]]></randomProperty>
  </properties>
  <children>
    <child id="random_id1" path="C:\random_id1.xml" class="image"/>
    <child id="random_id2" path="C:\random_id2.xml" class="section"/>
  </children>
</document>

And I need to extract the values from this XML to show it as HTML and also I need to extract the information of each children as well.

Each children has a class, so I have to separate them depending on the class to which they belong. Let's say that for this example there are only two classes:

  • image
  • section

The structure of a child is the following:

<?xml version="1.0" encoding="UTF-8"?>
<object id="empty-section" class="section">
  <properties>
    <randomProperty name="name"><![CDATA[Name]]></randomProperty>
  </properties>
  <children>
    <child id="random_id3" path="C:\random_id3.xml" class="image"/>
  </children>
  <traces />
</object>

As you can see they can also have children.

As far as I know, I tried to use document() XSLT function to get the document. I have also tried to call from the main template other templates:

<xsl:stylesheet
        version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:exslt="http://exslt.org/common"
        xmlns:msxsl="urn:schemas-microsoft-com:xslt"
        xmlns:proteus="https://proteus.us.es"
        exclude-result-prefixes="exslt msxsl">
    <xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>

    <!-- Object templates -->
    <xsl:include href="objects/section.xslt" />
    <xsl:include href="objects/image.xslt" />
    
    <xsl:template match="/">
        <html>
            <head>
                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" integrity="sha512-wnea99uKIC3TJF7v4eKk4Y+lMz2Mklv18+r4na2Gn1abDRPPOeef95xTzdwGD9e6zXJBteMIhZ1+68QC5byJZw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
            </head>
            <body class="bg-white h-full">
                <div class="mx-auto max-w-[210mm] w-full md:w-1/2 p-10 print:m-0 print:w-full">
                    <xsl:apply-templates />    
                </div>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="document">
        <!-- some Code for title -->
        
        <!--Some code for document properties -->

        <!-- Document objects -->
        <xsl:for-each select="children/child">              
            <xsl:call-template name="child" />
        </xsl:for-each>

    </xsl:template>
    
    <xsl:template name="child">
        <xsl:call-template name="object"/>
        <xsl:for-each select="children/child">
            <xsl:call-template name="child"/>
        </xsl:for-each>
    </xsl:template>

    
    <xsl:template name="object">
        <xsl:variable name="class" select="@class"/>
        <xsl:choose>
            <xsl:when test="$class = 'section'">
                <xsl:call-template name="section"/>
            </xsl:when>
            <xsl:when test="$class = 'image'">
                <xsl:call-template name="image"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="section"/>
            </xsl:otherwise>
        </xsl:choose>
        
    </xsl:template>


</xsl:stylesheet>

And let's for for example section.xslt I have tried several things but this one was the last one:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:proteus="https://proteus.us.es" exclude-result-prefixes="exslt msxsl">
  <xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>

<xsl:template name="section" mode="call-template">
  <xsl:variable name="path" select="@path"/>
  <xsl:value-of select="document($path)/object/@id" disable-output-escaping="yes"/>
</xsl:template>
</xsl:stylesheet>

So basically it doesn't show anything. As far as I know the template is called properly because I tried to show the id <xsl:value-of select="@id" disable-output-escaping="yes"/> and it worked. So I think the problem it's in the document function but I'm not sure at all.

WhoKnowsMe
  • 498
  • 2
  • 13
  • 1
    May be due to security restrictions? – WhoKnowsMe Mar 12 '23 at 20:59
  • 1
    Note that according to the specs, document() expects a URI, not a Windows filename. However, some implementations are tolerant and will accept a filename. – Michael Kay Mar 12 '23 at 21:00
  • Are you sure you want `disable-output-escaping='yes'`, by the way? What's the reason for that? Because that's really only for highly unusual situations and is generally frowned on as a hack. Looking at your example, I don't think it would have any effect, but potentially, if the `name` element in your included file had a special character such as `&` or `<` in it, for instance, then it could make your output file not well-formed. – Conal Tuohy Mar 13 '23 at 03:30
  • With default security settings in both .NET's XslCompiledTransform as well as with MSXML 6, I think, the `document` function is disabled. So depending on your used XSLT processor you need to make sure you enable the `document` function e.g. `var xsltProcessor = new XslCompiledTransform(); xsltProcessor.Load("sheet.xsl", new XsltSettings(true, false), new XmlUrlResolver());`. – Martin Honnen Mar 13 '23 at 06:36
  • @ConalTuohy Thanks for your comment, you are right. To tell you the truth, I am developing on a legacy system and did not really pay attention to `disable-output-escaping` attribute. – WhoKnowsMe Mar 13 '23 at 09:50

2 Answers2

0

Two problems.

The first is going to be a problem with some XSLT processors and not others. According to the specs, document() expects a URI, not a Windows filename. However, some implementations are tolerant and will accept a filename. So you may be lucky; or you may not.

Secondly, your templates are confused about what context they are expecting. When you write

<xsl:for-each select="children/child">
    <xsl:call-template name="child"/>
</xsl:for-each>

The named template "child" will be called with a "child" element as the context node. But the template itself does

<xsl:for-each select="children/child">

which isn't going to work if the context node is a "child" element.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Thanks you so much for your response I didn't thought about it. Anyway, I have tried to called directly the section template instead of the child template and It doesn't works neither. I supposed I'm not lucky and Doesn't support Windows filename, right? – WhoKnowsMe Mar 12 '23 at 21:14
0

You may need to transform the @path value into a URI to satisfy your XSLT processor's document function. My advice is to read the documentation for your specific processor, and make and experiment with a stub stylesheet until you have worked out a form of URI that works, e.g.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="/">
      <xsl:copy-of select="document('file:///C:/random_id3.xml')"/>
      <!--<xsl:copy-of select="document('C:\random_id3.xml')"/>-->
   </xsl:template>
</xsl:stylesheet>

This Wikipedia page may also be helpful: https://en.wikipedia.org/wiki/File_URI_scheme

Once you are able to load a document, you'll be in a position to transform your @path into the correct form for the document function (if that's needed).

Conal Tuohy
  • 2,561
  • 1
  • 8
  • 15
  • I have tried already with `file:///` as well as I check already [this other post](https://stackoverflow.com/questions/50038958/encode-for-uri-and-absolute-windows-paths) and unfortunaly it didn't work. – WhoKnowsMe Mar 13 '23 at 09:41
  • Does my suggested stylesheet produce an error? What happens? – Conal Tuohy Mar 13 '23 at 11:07
  • My advice would be to try `document('test.xml')` and see if you can load an XML document from the same folder as the stylesheet. If you can load a document from the stylesheet's directory then you can move on to more complicated URIs such as either `test-sub-folder/test.xml` or `test-sub-folder\test.xml` and see what works. – Conal Tuohy Mar 13 '23 at 11:12
  • I have already tried that before making the post and neither worked :(. At the end I have just changed the structure of the XML I pass to my xslt function in python in order to have all the data before so I don't need to use the document function. – WhoKnowsMe Mar 13 '23 at 17:00