views:

506

answers:

3

Hi, i have the following function i wrote to create an XML file using Xerces 3.0.1, if i call this function with a filePath of "foo.xml" or "../foo.xml" it works great, but if i pass in "c:/foo.xml" then i get an exception on this line

XMLFormatTarget *formatTarget = new LocalFileFormatTarget(targetPath);

can someone explain why my code works for relative paths, but not absolute paths please? many thanks.

const int ABSOLUTE_PATH_FILENAME_PREFIX_SIZE = 9;

void OutputXML(xercesc::DOMDocument* pmyDOMDocument, std::string filePath)
{
    //Return the first registered implementation that has the desired features. In this case, we are after a DOM implementation that has the LS feature... or Load/Save.
    DOMImplementation *implementation = DOMImplementationRegistry::getDOMImplementation(L"LS");

    // Create a DOMLSSerializer which is used to serialize a DOM tree into an XML document.
    DOMLSSerializer *serializer = ((DOMImplementationLS*)implementation)->createLSSerializer();

    // Make the output more human readable by inserting line feeds.
    if (serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true))
        serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);

    // The end-of-line sequence of characters to be used in the XML being written out. 
    serializer->setNewLine(XMLString::transcode("\r\n")); 

    // Convert the path into Xerces compatible XMLCh*.
    XMLCh *tempFilePath = XMLString::transcode(filePath.c_str());

    // Calculate the length of the string.
    const int pathLen = XMLString::stringLen(tempFilePath);

    // Allocate memory for a Xerces string sufficent to hold the path.
    XMLCh *targetPath = (XMLCh*)XMLPlatformUtils::fgMemoryManager->allocate((pathLen + ABSOLUTE_PATH_FILENAME_PREFIX_SIZE) * sizeof(XMLCh));

    // Fixes a platform dependent absolute path filename to standard URI form.
    XMLString::fixURI(tempFilePath, targetPath);

    // Specify the target for the XML output.
    XMLFormatTarget *formatTarget = new LocalFileFormatTarget(targetPath);
    //XMLFormatTarget *myFormTarget = new StdOutFormatTarget();

    // Create a new empty output destination object.
    DOMLSOutput *output = ((DOMImplementationLS*)implementation)->createLSOutput();

    // Set the stream to our target.
    output->setByteStream(formatTarget);

    // Write the serialized output to the destination.
    serializer->write(pmyDOMDocument, output);

    // Cleanup.
    serializer->release();
    XMLString::release(&tempFilePath);
    delete formatTarget;
    output->release();
}
+1  A: 

It looks like your file path is incorrect. It should be file:///C:/. See the following for more information:

http://en.wikipedia.org/wiki/File_URI_scheme

UPDATE: The following code works for me with Visual Studio 2008. It's a quick hack using your code along with some sample that comes with Xerces.

#include <windows.h>
#include <iostream>
#include <string>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <xercesc/framework/XMLFormatter.hpp>
#include <xercesc/framework/LocalFileFormatTarget.hpp>
#include <xercesc/dom/DOMDocument.hpp>
#include <xercesc/dom/DOMImplementation.hpp>
#include <xercesc/dom/DOMImplementationRegistry.hpp>
#include <xercesc/dom/DOMLSSerializer.hpp>
#include <xercesc/dom/DOMLSOutput.hpp>


using namespace xercesc;
using namespace std;

void OutputXML(xercesc::DOMDocument* pmyDOMDocument, std::string filePath);

class XStr
{
public :
    XStr(const char* const toTranscode)
    {
        // Call the private transcoding method
        fUnicodeForm = XMLString::transcode(toTranscode);
    }

    ~XStr()
    {
        XMLString::release(&fUnicodeForm);
    }

    const XMLCh* unicodeForm() const
    {
        return fUnicodeForm;
    }

private :
    XMLCh*   fUnicodeForm;
};

#define X(str) XStr(str).unicodeForm()

int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        XMLPlatformUtils::Initialize();
    }
    catch(const XMLException& e)
    {
        char* message = XMLString::transcode(e.getMessage());
        cout << "Error Message: " << message << "\n";
        XMLString::release(&message);
        return 1;
    }

    int errorCode = 0;
    {

        DOMImplementation* impl =  DOMImplementationRegistry::getDOMImplementation(X("Core"));

        if (impl != NULL)
        {
            try
            {
                DOMDocument* doc = impl->createDocument(
                               0,                    // root element namespace URI.
                               X("company"),         // root element name
                               0);                   // document type object (DTD).

                DOMElement* rootElem = doc->getDocumentElement();

                DOMElement*  prodElem = doc->createElement(X("product"));
                rootElem->appendChild(prodElem);

                DOMText*    prodDataVal = doc->createTextNode(X("Xerces-C"));
                prodElem->appendChild(prodDataVal);

                DOMElement*  catElem = doc->createElement(X("category"));
                rootElem->appendChild(catElem);

                catElem->setAttribute(X("idea"), X("great"));

                DOMText*    catDataVal = doc->createTextNode(X("XML Parsing Tools"));
                catElem->appendChild(catDataVal);

                DOMElement*  devByElem = doc->createElement(X("developedBy"));
                rootElem->appendChild(devByElem);

                DOMText*    devByDataVal = doc->createTextNode(X("Apache Software Foundation"));
                devByElem->appendChild(devByDataVal);

                OutputXML(doc, "C:/Foo.xml");

                doc->release();
            }
            catch (const OutOfMemoryException&)
            {
                XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl;
                errorCode = 5;
            }
            catch (const DOMException& e)
            {
                XERCES_STD_QUALIFIER cerr << "DOMException code is:  " << e.code << XERCES_STD_QUALIFIER endl;
                errorCode = 2;
            }
            catch(const XMLException& e)
            {
                char* message = XMLString::transcode(e.getMessage());
                cout << "Error Message: " << message << endl;
                XMLString::release(&message);
                return 1;
            }
            catch (...)
            {
                XERCES_STD_QUALIFIER cerr << "An error occurred creating the document" << XERCES_STD_QUALIFIER endl;
                errorCode = 3;
            }
       }  // (inpl != NULL)
       else
       {
           XERCES_STD_QUALIFIER cerr << "Requested implementation is not supported" << XERCES_STD_QUALIFIER endl;
           errorCode = 4;
       }
    }

    XMLPlatformUtils::Terminate();

    return errorCode;
}

void OutputXML(xercesc::DOMDocument* pmyDOMDocument, std::string filePath) 
{ 
    //Return the first registered implementation that has the desired features. In this case, we are after a DOM implementation that has the LS feature... or Load/Save. 
    DOMImplementation *implementation = DOMImplementationRegistry::getDOMImplementation(L"LS"); 

    // Create a DOMLSSerializer which is used to serialize a DOM tree into an XML document. 
    DOMLSSerializer *serializer = ((DOMImplementationLS*)implementation)->createLSSerializer(); 

    // Make the output more human readable by inserting line feeds. 
    if (serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true)) 
        serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true); 

    // The end-of-line sequence of characters to be used in the XML being written out.  
    serializer->setNewLine(XMLString::transcode("\r\n"));  

    // Convert the path into Xerces compatible XMLCh*. 
    XMLCh *tempFilePath = XMLString::transcode(filePath.c_str()); 

    // Specify the target for the XML output. 
    XMLFormatTarget *formatTarget = new LocalFileFormatTarget(tempFilePath); 

    // Create a new empty output destination object. 
    DOMLSOutput *output = ((DOMImplementationLS*)implementation)->createLSOutput(); 

    // Set the stream to our target. 
    output->setByteStream(formatTarget); 

    // Write the serialized output to the destination. 
    serializer->write(pmyDOMDocument, output); 

    // Cleanup. 
    serializer->release(); 
    XMLString::release(&tempFilePath); 
    delete formatTarget; 
    output->release(); 
} 
Garett
Thanks for the reply Garett, i call XMLString::fixURI to get the path into that format.
Jon
Here is the string that gets passed to LocalFileFormatTarget: "file:///c:/foo.xml"
Jon
What error message are you getting? I'm assuming you've wrapped this in a try/catch.
Garett
unable to open file 'file:///c:/foo.xml'
Jon
Have you tried using GetLastError() along with FormatMessage to get the windows error description? I tried it with you code and I got "The filename, directory name, or volume label syntax is incorrect"
Garett
Hi Garett, i tried your code above under visual studio 2008, and i still get Error Message: unable to open file 'C:/Foo.xml'. just to confirm, this code is working for you?
Jon
Yes, it is working for me. The error you are getting is the xerces error. To know what the windows error is, you will need to use dthe GetLastError and FormatMessage Windows API functions. http://stackoverflow.com/questions/455434/how-should-i-use-formatmessage-properly-in-c. Also, as Jon indicated if you are linking against the debug build of the library you should be able to step into the function.
Garett
+1  A: 

From looking at the source the filename gets passed through to WindowsFileMgr::fileOpen in WindowsFileMgr.cpp, which doesn't appear to be expecting a URI.

So, have you tried not converting the filename to a URI, e.g. just use:

c:\foo.xml

(might need to escape the backslash)

?

jon hanson
Interesting, I tried what you suggested, it works fine for relative paths, but i get the same exception for absolute paths."unable to open file 'c:\foo.xml'"simplified my code though, thankyou!
Jon
Are you linking to a debug build of xerces? You should be able to step into the function i mention above add see the call to the Windows CreateFileW function.
jon hanson
+1  A: 

Are you using Windows Vista? perhaps you don't have the necessary permissions? See this question: http://stackoverflow.com/questions/3091484/exception-in-two-line-xerces-program

Gungho