I have encountered a strange bug while loading a file into IXMLDOMDocument2
. The method load
returns False
, if the file name contains %
and sets parseError
to:
The system cannot locate the resource specified.
The file exists and when remove %
from its name, it works just fine.
Is there any way to load XML file with %
in its name?
The documentation for IXMLDOMDocument.load()
says that the xmlSource
source parameter accepts one of the following values:
IStream
ISequentialStream
IPersistStream
SAFEARRAY
of bytesIXMLDOMDocument
instanceYou are trying to pass a file name, which the method treats as a URL (the first option in the list above). Character %
in URLs is reserved for percent-encoding. The IXMLDOMDocument.load()
method is trying to decode the URL from the string you passed as an argument, which may result in a slightly different file name. The tricky part is that this doesn't happen when the percent character isn't followed by a pair of hexadecimal digits, meaning that: Test%.xml
will be decoded as Test%.xml
, but Test%21.xml
will be decoded as Test!.xml
, or Test%4A.xml
will be decoded as TestJ.xml
.
You should be careful what you pass to the IXMLDOMDocument.load()
method. You have several options.
This is easy. You can use TNetEncoding.URL
from the System.NetEncoding
unit:
var FileName := 'C:\test%21.xml';
var Doc := CoDOMDocument.Create;
Doc.load(TNetEncoding.URL.Encode(FileName));
IStream
as an argument:Open a file using TFileStream
(or TFile.OpenRead()
from the System.IOUtils
unit) and wrap it in a TStreamAdapter
from the System.CLasses
unit, which implements the IStream
interface declared in the Winapi.ActiveX
unit.
var FileName := 'C:\test%21.xml';
var Stream: IStream := TStreamAdapter.Create(TFile.OpenRead(FileName), soOwned);
var Doc := CoDOMDocument.Create;
Doc.load(Stream);
loadXML()
method instead:There is an IXMLDOMDOcument.loadXML()
method that allows you to load XML content from a string. You can use TFile.ReadAllText()
(unit System.IOUtils
) to read the content of the file and pass it to the loadXML()
method.
var FileName := 'C:\test%21.xml';
var Doc := CoDOMDocument.Create;
Doc.loadXML(TFile.ReadAllText(FileName));
I don't recommend loading XML using this option, because it loads the whole content of the file into memory.