3

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();
}
dangerousdave
  • 6,331
  • 8
  • 45
  • 62
  • +1 because I have the same problem. I don't think the answer you have chosen as correct solves the problem though as I do have the necessary permissions (same file location for both relative and absolute paths, relative works, absolute doesn't). How did you resolve your issue??? – Jacob Sep 29 '10 at 02:12
  • I'm pretty sure it was a permissions problem for me, if i remember correctly i was trying to write to the root of the C drive under Vista, and it wouldn't let me. – dangerousdave Sep 30 '10 at 18:09
  • Thanks Jon. I think you must have been right. I fixed a few other issues in my xerces wrapper and now it works with both relative and absolute. And with both '/' and '\\' directory separators :) – Jacob Oct 03 '10 at 22:37

4 Answers4

4

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
  • 16,632
  • 5
  • 55
  • 63
  • Thanks for the reply Garett, i call XMLString::fixURI to get the path into that format. – dangerousdave May 24 '10 at 13:50
  • Here is the string that gets passed to LocalFileFormatTarget: "file:///c:/foo.xml" – dangerousdave May 24 '10 at 14:00
  • What error message are you getting? I'm assuming you've wrapped this in a try/catch. – Garett May 24 '10 at 14:00
  • unable to open file 'file:///c:/foo.xml' – dangerousdave May 24 '10 at 14:23
  • 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 May 24 '10 at 20:25
  • 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? – dangerousdave May 26 '10 at 10:04
  • 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 May 26 '10 at 11:47
1

Running the above code might give the error - 'DOMDocument' : ambiguous symbol, in which case replace DOMDocument by xercesc::DOMDocument as there is also a DOMDocument class defined in msxml.h header that leads to ambiguity.

Vigo
  • 707
  • 4
  • 11
  • 29
1

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
  • 8,722
  • 2
  • 37
  • 61
  • 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! – dangerousdave May 24 '10 at 17:12
  • 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 May 25 '10 at 07:51
1

Are you using Windows Vista? perhaps you don't have the necessary permissions? See this question: Exception in two line Xerces program

Community
  • 1
  • 1
Gungho
  • 42
  • 1
  • 6