2

I am trying to get MSXML6 XSLT import (or include) functionality working for my app. I have looked everywhere for a solution to this and have found a number of references to people for whom it seems to be working so I'm thinking I must be doing something wrong but I can't figure out what it is.

Book.xml

<Book>
    <Title>A Christmas Carol</Title>
    <Author>Charles Dickens</Author>
    <Binding>Hardcover</Binding>
    <Price>14.99</Price>
    <Comment/>
</Book>

DefaultRules.xsl

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:include href="/Temp/Book.xsl"/>
    <xsl:output method="text" indent="no"/>

    <xsl:template match="/">
        <xsl:apply-templates select="Book"/>
    </xsl:template>
</xsl:stylesheet>

Book.xsl

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="Book">
        <xsl:text>Title: </xsl:text>
        <xsl:apply-templates select="Title"/>
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="Author"/>
        <xsl:text> - $</xsl:text>
        <xsl:apply-templates select="Price"/>
    </xsl:template>
</xsl:stylesheet>

Proto.cpp

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);

    variant_t vResult;
    LPCTSTR output = NULL;
    MSXML2::IXMLDOMDocumentPtr pXml(__uuidof(MSXML2::DOMDocument60));
    MSXML2::IXMLDOMDocumentPtr pXslt(__uuidof(MSXML2::FreeThreadedDOMDocument60));
    MSXML2::IXSLTemplatePtr pTemplate(__uuidof(MSXML2::XSLTemplate60));
    MSXML2::IXSLProcessorPtr pProcessor;
    IStream *pOutStream;
    // load xml file with data and xsl file to transform
    // xml -> html
    pXml->async = false;
    pXml->load(_bstr_t("Book.xml"));
    pXslt->resolveExternals = true;
    pXslt->async = false;
    pXslt->load(_bstr_t("/Temp/DefaultRules.xsl"));
    pTemplate->stylesheet = pXslt;
    pProcessor = pTemplate->createProcessor();

    CreateStreamOnHGlobal(NULL, TRUE, &pOutStream);
    pProcessor->put_output(_variant_t(pOutStream));
    pProcessor->put_input(_variant_t((IUnknown*)pXml));
    pProcessor->transform();
    //get results of transformation and print them to stdout
    HGLOBAL hg = NULL;
    pOutStream->Write((void const*)"\0\0", 2, 0);
    GetHGlobalFromStream(pOutStream, &hg);
    output = (LPCTSTR)GlobalLock(hg);
    wprintf(L"%s", output);
    GlobalUnlock(hg);

    //release before CoUninitialize()
    pXml.Release();
    pXslt.Release();
    pTemplate.Release();
    pProcessor.Release();

    CoUninitialize();
    getchar();
    return 0;
}

The (correct) output I get from an XML tool (EditiX) is

Title: A Christmas Carol
Charles Dickens - $14.99

The (incorrect) output that I get from MSXML6 transform is

A Christmas CarolCharles DickensHardcover14.99

It looks like the file specified for inclusion isn't being imported/included so I'm just getting the default template results although I don't get any error messages. As you can see, I am setting the resolveExternals flag to true (new for MSXML6). I have also tried loading it from the current folder as well as a specific folder (the above code uses /Temp). I have also tried using both xsl:import and xsl:include. The solution always works in the XML tool but always gives the incorrect result using the above code and MSXML6. I am using Windows 10 if that could be related.

Bill S
  • 23
  • 4
  • Shouldn't the location be a URL (e.g. absolute `` or relative ``)? Or where do you want to load the XSLT module from? – Martin Honnen Jan 11 '16 at 18:51
  • I tried changing the href to be in URI format but it made no difference. I had already tried including the file from the current folder (i.e. ) but that doesn't work for me either. I have verified that the outer file (e.g. DefaultRules.xsl) IS getting loaded. If I attempt to include an xsl:call-template in it for a named template in the other file, I get an error message that the named template doesn't exist. – Bill S Jan 11 '16 at 21:41
  • What happens if you run the following in place of DefaultRules.xsl? http://pastebin.com/uwcHLXeq (try to find out what it thinks the current working directory is). – Dan Field Jan 11 '16 at 22:01
  • I don't use MSXML with C++ but when I try to write similar code with JScript and execute it with Windows Script Host (e.g. `cscript file.js`) on Windows 10 then it work fine. What's more, if the `xsl:include` references a non-existing file then I get a clear error message (e.g. "Error compiling the included or imported styleshet '/Temp/file.xsl'") instead of the wrong output. Only when `xslt.resolveExternals = true;` is not set then I get the wrong output you have shown. So I can't currently explain the result you get – Martin Honnen Jan 12 '16 at 10:45

1 Answers1

3

I think the problem is that you have set pXslt->resolveExternals = true;, you need to use pXslt->resolveExternals = VARIANT_TRUE;.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110