Validate XML with XSD in .NET and native MSXML – big difference in string maxLength validation with newlines (samples in C# and Delphi)
Posted by jpluimers on 2010/01/19
Recently, I had an issue while validating XML with XSD: validation in .NET using the built in classes in the System.XML namespace, and validation in native Windows using the COM objects exposed by MSXML version 6 (which incidentally ships with the .NET 3.0 framework).
Some documents validating OK in .NET did not validate well with MSXML.
I’ll show my findings below, and try to explain the difference I found, together with my conclusions.
The main conclusion is that MSXML version 6 has a bug, but I wonder why I can’t find much more information on it.
Since there is not so much ready to use for validating XML by XSD in .NET and native, I’ll include complete source code of command-line validations applications for both platforms.
.NET source code is in C#.
Native source code is in Delphi.
So lets get started with the XSD:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xs:element name="content"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:maxLength value="25"/> </xs:restriction> </xs:simpleType> </xs:element> </xs:schema>
An XML document like the one directly below will validate OK with the XSD, both in .NET and with MSXML version 6:
<?xml version="1.0" encoding="UTF-8"?> <content>text</content>
The odd thing is, this XML file validates OK in .NET, but not in MSXML version 6:
<?xml version="1.0" encoding="UTF-8"?> <content>123456789 123456789 12345</content>
Even stranger is that both .NET and MSXML agree on the length of the content element in the above XML file: 25 characters, just like with this XML:
<?xml version="1.0" encoding="UTF-8"?> <content>1234567890123456789012345</content>
Just look at these two dumps of the above XML files from .NET:
NodeType=Element, NodeName=content NodeType=Text, NodeName= Length=25 LF-Count=2 CR-Count=0 123456789 123456789 12345 NodeType=Element, NodeName=content NodeType=Text, NodeName= Length=25 LF-Count=0 CR-Count=0 1234567890123456789012345
and from MSXML:
NodeType=1, NodeName=content NodeType=3, NodeName=#text NodeValue-Length=25, LF-Count=2, CR-Count=0 123456789 123456789 12345 NodeType=1, NodeName=content NodeType=3, NodeName=#text NodeValue-Length=25, LF-Count=0, CR-Count=0 1234567890123456789012345
So: for the XML, both .NET and MSXML agree on the length of content (both cases 25), and for the first case an LF count of 2, and a CR count of 0 (zero).
Even though the XML files here store their line endings as CRLF pairs (see the dumps below), they should be treated as one character.
Both the Microsoft documentation (end of the page) and the W3C documentation (section 2.11) state this.
000000: 3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D 22 31 <?xml version="1
000010: 2E 30 22 20 65 6E 63 6F 64 69 6E 67 3D 22 55 54 .0" encoding="UT
000020: 46 2D 38 22 3F 3E 0D 0A 3C 63 6F 6E 74 65 6E 74 F-8"?>..<content
000030: 3E 31 32 33 34 35 36 37 38 39 0D 0A 31 32 33 34 >123456789..1234
000040: 35 36 37 38 39 0D 0A 31 32 33 34 35 3C 2F 63 6F 56789..12345</co
000050: 6E 74 65 6E 74 3E 0D 0A 0D 0A ntent>….
000000: 3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D 22 31 <?xml version="1
000010: 2E 30 22 20 65 6E 63 6F 64 69 6E 67 3D 22 55 54 .0" encoding="UT
000020: 46 2D 38 22 3F 3E 0D 0A 3C 63 6F 6E 74 65 6E 74 F-8"?>..<content
000030: 3E 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 >123456789012345
000040: 36 37 38 39 30 31 32 33 34 35 3C 2F 63 6F 6E 74 6789012345</cont
000050: 65 6E 74 3E 0D 0A 0D 0A ent>….
So, I tried once more with this file which is LF separated:
000000: 3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D 22 31 <?xml version="1
000010: 2E 30 22 20 65 6E 63 6F 64 69 6E 67 3D 22 55 54 .0" encoding="UT
000020: 46 2D 38 22 3F 3E 0A 3C 63 6F 6E 74 65 6E 74 3E F-8"?>.<content>
000030: 31 32 33 34 35 36 37 38 39 0A 31 32 33 34 35 36 123456789.123456
000040: 37 38 39 0A 31 32 33 34 35 3C 2F 63 6F 6E 74 65 789.12345</conte
000050: 6E 74 3E nt>
And now both .NET and MSXML version 6 have the same result: this file is valid in both cases.
Conclusion
From the above, I have a couple of conclusions.
- For XSD validation, MSXML does not adhere to the W3C standard with respect to CR LF end-of-line handling, but the .NET implementation does.
- Contrary to what a lot of people believe, the .NET XML implementation is not just a shell around MSXML.
- When validating XML with XSD using MSXML, you have to make sure you do not use CR LF end-of-lines. Normalize them to LF line endings (or CR line endings) before putting them through MSXML.
Source code
Since there is not so much sample XML with XSD validation sample source available in Delphi nor in C#/.NET, here are some samples.
For both environments, there are two samples:
- one for validating an XML document with one or more XSD documents,
- and one to dump one or more XML documents.
The C# samples are based on XMLReader, XmlReaderSettings and XmlSchema objects. They accommodate for includes in the XSD files (back when I wrote the original code, I had some nasty includes from relative directories).
The Delphi samples are based on MSXML, and use IXMLDOMDocument and IXMLDOMSchemaCollection. I did not add special code for includes in XSD files here, as the XSD files were all in once piece.
C# sample code
The C# sample code needs these assemblies
- – mscorlib.dll
- – System.dll
- – System.Xml.dll
C# console apps
using System;
namespace bo.Xml
{
class ValidateXmlWithXsd
{
static void Main(string[] args)
{
if (2 != args.Length)
{
Console.WriteLine("use two parameters: XmlFile and XsdFile");
}
else
{
logic instance = new logic();
try
{
instance.Run(
args[0], args[1]
);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
class logic
{
public void Run(string xmlFileName, string xsdFileName)
{
bo.Xml.XmlValidator validator = new bo.Xml.XmlValidator();
validator.XmlReadEventHandler += new bo.Xml.XmlReadEventHandler(validator_XmlReadEventHandler);
validator.XmlValidationEventHandler += new bo.Xml.XmlValidationEventHandler(validator.ValidationResultEventHandler);
validator.ValidateXml(xmlFileName, xsdFileName);
string result = validator.ToString();
if (string.IsNullOrEmpty(result))
{
result = "OK.";
}
Console.WriteLine(result);
}
void validator_XmlReadEventHandler(object sender, bo.Xml.XmlReadEventArgs e)
{
//if (reader.NodeType == XmlNodeType.Text)
// Console.WriteLine(reader.Value);
}
}
}
using System;
namespace bo.Xml
{
class DumpXml
{
static void Main(string[] args)
{
if (1 > args.Length)
{
Console.WriteLine("use parameters: [XmlFile]...");
}
else
{
logic instance = new logic();
try
{
instance.Run(args);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
class logic
{
public void Run(string[] xmlFileNames)
{
bo.Xml.XmlDumper dumper = new bo.Xml.XmlDumper();
dumper.DumpXml(xmlFileNames);
string result = dumper.ToString();
if (!string.IsNullOrEmpty(result))
Console.WriteLine(result);
}
}
}
C# supporting classes
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
namespace bo.Xml
{
public class XmlDumper
{
public void DumpXml(string[] xmlFileNames)
{
foreach (string xmlFileName in xmlFileNames)
{
DumpXml(xmlFileName);
}
}
public void DumpXml(string xmlFileName)
{
using (FileStream xmlFile = File.OpenRead(xmlFileName))
{
using (XmlReader reader = XmlReader.Create(xmlFile))
{
while (reader.Read())
{
string currentNodeAsString = GetCurrentNodeAsString(reader);
if (!string.IsNullOrEmpty(currentNodeAsString))
{
if (null == toString)
toString = new StringBuilder();
toString.AppendLine(currentNodeAsString);
}
}
}
}
}
private StringBuilder toString = null;
public override string ToString()
{
return (null == toString) ? null : toString.ToString();
}
private int charCount(char compareValue, string value)
{
int result = 0;
foreach (System.Char item in value)
{
if (item == compareValue)
result++;
}
return result;
}
public string GetCurrentNodeAsString(XmlReader reader)
{
StringBuilder result = new StringBuilder();
switch (reader.NodeType)
{
case XmlNodeType.Element:
case XmlNodeType.Text:
AppendNodeTypeAndName(reader, result);
break;
default:
break;
}
switch (reader.NodeType)
{
case XmlNodeType.Text:
string value = reader.Value;
int lfCount = charCount('\n', value);
int crCount = charCount('\r', value);
result.AppendFormat(" Length={0} LF-Count={1} CR-Count={2}", value.Length, lfCount, crCount);
AppendValueOnNewLine(reader, result);
break;
case XmlNodeType.CDATA:
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.Comment:
case XmlNodeType.DocumentType:
AppendValueOnNewLine(reader, result);
break;
default:
break;
}
return result.ToString();
}
private static void AppendNodeTypeAndName(XmlReader reader, StringBuilder result)
{
result.AppendFormat("NodeType={0}, NodeName={1}", reader.NodeType, reader.Name);
}
private static void AppendValueOnNewLine(XmlReader reader, StringBuilder result)
{
result.AppendLine();
result.Append(reader.Value);
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
namespace bo.Xml
{
public class XmlReadEventArgs : EventArgs
{
public XmlReadEventArgs(XmlReader reader)
: base()
{
this.reader = reader;
}
private XmlReader reader;
public XmlReader Reader
{
get { return reader; }
}
}
public class XmlValidationEventArgs : EventArgs
{
public XmlValidationEventArgs(XmlReader reader, System.Xml.Schema.ValidationEventArgs e)
: base()
{
this.e = e;
this.reader = reader;
}
private System.Xml.Schema.ValidationEventArgs e;
public System.Xml.Schema.ValidationEventArgs E
{
get { return e; }
}
private XmlReader reader;
public XmlReader Reader
{
get { return reader; }
}
}
public delegate void XmlReadEventHandler(Object sender, XmlReadEventArgs e);
public delegate void XmlValidationEventHandler(Object sender, XmlValidationEventArgs e);
public class XmlValidator
{
public event XmlReadEventHandler XmlReadEventHandler;
public event XmlValidationEventHandler XmlValidationEventHandler;
public void ValidateXml(string xmlFileName, string xsdFileName)
{
ValidateXml(xmlFileName, new string[] { xsdFileName });
}
public void ValidateXml(string xmlFileName, string[] xsdFileNames)
{
List<FileStream> xsdFileStreamsToDispose = new List<FileStream>();
try
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
foreach (string xsdFileName in xsdFileNames)
{
// resolve the relative SchemaLocation of the included Xsd's using the
// base path of the xsdFileName
// see http://www.hanselman.com/blog/ValidatingXMLSchemasThatHaveBeenXsimportedAndHaveSchemaLocation.aspx
// Refactoring idee: in de toekomst oplossen met een resolver (die recursief werkt)
// see http://www.hanselman.com/blog/ValidatingThatXmlSchemasAndTheirImportsAreValidAndAllGoodWithAnXmlResolver.aspx
FileStream xsdFileStream = File.OpenRead(xsdFileName);
xsdFileStreamsToDispose.Add(xsdFileStream);
XmlSchema schema = XmlSchema.Read(xsdFileStream, null);
string baseDirectory = Path.GetDirectoryName(xsdFileName);
foreach (XmlSchemaInclude include in schema.Includes)
{
// if include.SchemaLocation is an absolute path, Path.Combine returns that,
// otherwise it appends the relative path to the baseDirectory
include.SchemaLocation = Path.Combine(baseDirectory, include.SchemaLocation);
}
settings.Schemas.Add(schema);
}
settings.ValidationEventHandler +=
new ValidationEventHandler(reader_ValidationEventHandler);
using (FileStream xmlFile = File.OpenRead(xmlFileName))
{
using (reader = XmlReader.Create(xmlFile, settings))
{
while (reader.Read())
{
currentLine = GetCurrentNodeAsString(reader);
OnReadEventHandler();
}
}
}
}
finally
{
foreach (IDisposable fileStream in xsdFileStreamsToDispose)
{
fileStream.Dispose();
}
}
}
protected void OnReadEventHandler()
{
if (null != XmlReadEventHandler)
{
XmlReadEventHandler(this, new XmlReadEventArgs(reader));
}
}
private XmlReader reader;
private string currentLine;
private void reader_ValidationEventHandler(object sender, ValidationEventArgs e)
{
if (null != XmlValidationEventHandler)
{
XmlValidationEventHandler(this, new XmlValidationEventArgs(reader, e));
}
}
private StringBuilder toString = null;
public override string ToString()
{
return (null == toString) ? null : toString.ToString();
}
public void ValidationResultEventHandler(object sender, XmlValidationEventArgs e)
{
if (null == toString)
{
toString = new StringBuilder();
}
toString.AppendFormat("{0}: {1}", e.E.Severity.ToString(), e.E.Message);
toString.AppendLine();
if (!string.IsNullOrEmpty(currentLine))
toString.AppendLine(currentLine);
string line = GetCurrentNodeAsString(e.Reader);
if (!string.IsNullOrEmpty(line))
toString.AppendLine(line);
}
public string GetCurrentNodeAsString(XmlReader reader)
{
string result = "";
switch (reader.NodeType)
{
case XmlNodeType.Element:
result = string.Format("<{0}>", reader.Name);
break;
case XmlNodeType.Text:
result = reader.Value;
break;
case XmlNodeType.CDATA:
result = string.Format("<![CDATA[{0}]]>", reader.Value);
break;
case XmlNodeType.ProcessingInstruction:
result = string.Format("<?{0} {1}?>", reader.Name, reader.Value);
break;
case XmlNodeType.Comment:
result = string.Format("<!--{0}-->", reader.Value);
break;
case XmlNodeType.XmlDeclaration:
result = string.Format("<?{0} {1}?>", reader.Name, reader.Value);
break;
case XmlNodeType.Document:
break;
case XmlNodeType.DocumentType:
result = string.Format("<!DOCTYPE {0} [{1}]", reader.Name, reader.Value);
break;
case XmlNodeType.EntityReference:
toString.Append(reader.Name);
break;
case XmlNodeType.EndElement:
result = string.Format("</{0}>", reader.Name);
break;
default:
break;
}
return result;
}
}
}
Delphi sample code
The Delphi sample code needs MSXML6.DLL (usually found in c:\WINDOWS\system32\msxml6.dll) imported as MSXML2_TLB.pas (in the Delphi IDE, choose the menu item “Components”, followed by “Import Component”, in the dialog choose “Import ActiveX component”, then select the right DLL, and import it into your project).
The code has been tested with Delphi 2007.
Delphi console apps
program DumpXml;
{$APPTYPE CONSOLE}
uses
SysUtils,
MSXML2_TLB in 'MSXML2_TLB.pas',
XmlDumperUnit in 'XmlDumperUnit.pas';
var
Index: Integer;
begin
try
if ParamCount < 1 then
Writeln('use parameters: [XmlFile]...')
else
for Index := 1 to ParamCount do
with TXmlDumper.Create() do
try
Dump(ParamStr(Index));
Writeln(DumpResult);
finally
Free;
end;
except
on E: Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
program ValidateXmlWithXsd;
{$APPTYPE CONSOLE}
uses
SysUtils,
XmlValidatorUnit in 'XmlValidatorUnit.pas',
MSXML2_TLB in 'MSXML2_TLB.pas',
XmlDumperUnit in 'XmlDumperUnit.pas';
begin
try
if ParamCount <> 2 then
begin
Writeln('use two parameters: XmlFile and XsdFile');
end
else
begin
with TXmlValidator.Create do
try
if ValidateXml(ParamStr(1), ParamStr(2)) then
Writeln('OK')
else
begin
Writeln(ValidationResult);
end;
finally
Free;
end;
with TXmlDumper.Create() do
try
Dump(ParamStr(1));
Writeln(DumpResult);
finally
Free;
end;
end;
except
on E: Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Delphi supporting classes
unit XmlDumperUnit;
interface
uses
Classes, MSXML2_TLB, ActiveX;
type
TXmlDumper = class(TObject)
strict private
FDumpStrings: TStrings;
strict protected
function GetHaveDump: Boolean; virtual;
function GetDumpResult: string; virtual;
function GetDumpStrings: TStrings; virtual;
procedure DumpNode(const Node: IXMLDOMNode); virtual;
property HaveDump: Boolean read GetHaveDump;
property DumpStrings: TStrings read GetDumpStrings;
public
destructor Destroy; override;
function Dump(const XmlFileName: string): Boolean; overload; virtual;
function Dump(const XmlFileNames: array of string): Boolean; overload; virtual;
property DumpResult: string read GetDumpResult;
end;
implementation
uses
SysUtils, Variants, ComObj;
destructor TXmlDumper.Destroy;
begin
inherited;
FreeAndNil(FDumpStrings);
end;
function TXmlDumper.GetDumpResult: string;
begin
if HaveDump then
Result := DumpStrings.Text
else
Result := NullAsStringValue;
end;
function TXmlDumper.Dump(const XmlFileName: string): Boolean;
var
MultipleErrorMessages: Boolean;
XmlDocument: IXMLDOMDocument3;
node: IXMLDOMNode;
begin
XmlDocument := CoFreeThreadedDOMDocument60.Create();
if not Assigned(XmlDocument) then
RaiseLastOSError();
if not XmlDocument.load(XmlFileName) then
RaiseLastOSError();
node := XmlDocument.documentElement;
DumpNode(node);
Result := not HaveDump;
end;
function CharCount(const S: string; const CharToCount: Char): Integer;
var
Ch: Char;
begin
Result := 0;
for Ch in S do
begin
if Ch = CharToCount then
Inc(Result);
end;
end;
function TXmlDumper.Dump(const XmlFileNames: array of string): Boolean;
var
XmlFileName: string;
begin
for XmlFileName in XmlFileNames do
Dump(XmlFileName);
Result := not HaveDump;
end;
procedure TXmlDumper.DumpNode(const Node: IXMLDOMNode);
var
attribute: IXMLDOMNode;
attributes: IXMLDOMNamedNodeMap;
childNode: IXMLDOMNode;
childNodes: IXMLDOMNodeList;
HaveNodeValue: Boolean;
NodeType: DOMNodeType;
NodeValue: OleVariant;
NodeValueLength: Integer;
NodeValueString: string;
LfCount: Integer;
CrCount: Integer;
begin
if not Assigned(Node) then
Exit;
NodeType := Node.NodeType;
NodeValue := Node.nodeValue;
HaveNodeValue := not VarIsNull(NodeValue);
DumpStrings.Add(Format('NodeType=%d, NodeName=%s', [NodeType, Node.nodeName]));
if HaveNodeValue then
begin
NodeValueString := NodeValue;
NodeValueLength := Length(NodeValueString);
LfCount := CharCount(NodeValueString, #10);
CrCount := CharCount(NodeValueString, #13);
DumpStrings.Add(Format('NodeValue-Length=%d, LF-Count=%d, CR-Count=%d', [NodeValueLength, LfCount, CrCount]));
DumpStrings.Add(NodeValueString);
end;
attributes := Node.attributes;
if Assigned(attributes) then
begin
attribute := attributes.nextNode;
if Assigned(attribute) then
repeat
DumpNode(attribute);
attribute := attributes.nextNode;
until not Assigned(attribute);
end;
childNodes := Node.childNodes;
if Assigned(childNodes) then
begin
childNode := childNodes.nextNode;
if Assigned(childNode) then
repeat
DumpNode(childNode);
childNode := childNodes.nextNode;
until not Assigned(childNode);
end;
end;
function TXmlDumper.GetHaveDump: Boolean;
begin
Result := Assigned(FDumpStrings)
end;
function TXmlDumper.GetDumpStrings: TStrings;
begin
if not HaveDump then
FDumpStrings := TStringList.Create();
Result := FDumpStrings;
end;
initialization
// http://chrisbensen.blogspot.com/2007/06/delphi-tips-and-tricks_26.html
if Assigned(ComObj.CoInitializeEx) then
ComObj.CoInitializeEx(nil, COINIT_MULTITHREADED)
else
CoInitialize(nil);
finalization
CoUninitialize;
end.
unit XmlValidatorUnit;
interface
uses
MSXML2_TLB, Classes;
type
/// loosely based on http://msdn.microsoft.com/en-us/library/ms765386(VS.85).aspx
/// and http://www.nonhostile.com/howto-validate-xml-xsd-in-vb6.asp
TXmlValidator = class(TObject)
strict private
FValidationResultStrings: TStrings;
strict protected
function GetHaveValidationResult: Boolean; virtual;
function GetValidationResult: string; virtual;
function GetValidationResultStrings: TStrings; virtual;
procedure ProcessParseError(const parseError: IXMLDOMParseError); virtual;
procedure ProcessParseError2(const parseError2: IXMLDOMParseError2); virtual;
property HaveValidationResult: Boolean read GetHaveValidationResult;
property ValidationResultStrings: TStrings read GetValidationResultStrings;
public
destructor Destroy; override;
function ValidateXml(const XmlFileName, XsdFileName: string): Boolean; overload;
function ValidateXml(const XmlFileName: string; const XsdFileNames: array of string): Boolean; overload;
property ValidationResult: string read GetValidationResult;
end;
implementation
uses
Variants,
SysUtils, ActiveX, ComObj;
destructor TXmlValidator.Destroy;
begin
inherited;
FreeAndNil(FValidationResultStrings);
end;
function TXmlValidator.GetHaveValidationResult: Boolean;
begin
Result := Assigned(FValidationResultStrings)
end;
function TXmlValidator.GetValidationResult: string;
begin
if HaveValidationResult then
Result := ValidationResultStrings.Text
else
Result := NullAsStringValue;
end;
function TXmlValidator.GetValidationResultStrings: TStrings;
begin
if not HaveValidationResult then
FValidationResultStrings := TStringList.Create();
Result := FValidationResultStrings;
end;
procedure TXmlValidator.ProcessParseError(const parseError: IXMLDOMParseError);
var
Reason: WideString;
begin
Reason := parseError.reason;
// parseError.errorCode;
// parseError.url;
if Reason <> NullAsStringValue then
ValidationResultStrings.Add(Reason);
// parseError.srcText;
end;
procedure TXmlValidator.ProcessParseError2(const parseError2: IXMLDOMParseError2);
var
ParseErrorCollection: IXMLDOMParseErrorCollection;
item: IXMLDOMParseError2;
begin
ProcessParseError(parseError2);
ParseErrorCollection := parseError2.allErrors;
if Assigned(ParseErrorCollection) then
begin
item := ParseErrorCollection.Next;
if Assigned(item) then
repeat
ProcessParseError(item);
item := ParseErrorCollection.Next;
until not Assigned(item);
end;
end;
function TXmlValidator.ValidateXml(const XmlFileName, XsdFileName: string): Boolean;
begin
Result := ValidateXml(XmlFileName, [XsdFileName]);
end;
function TXmlValidator.ValidateXml(const XmlFileName: string; const XsdFileNames: array of string): Boolean;
var
MultipleErrorMessages: Boolean;
XmlDocument: IXMLDOMDocument3;
XsdDocument: IXMLDOMDocument3;
SchemaCollection: IXMLDOMSchemaCollection2;
targetNamespaceNode: IXMLDOMNode;
namespaceURI: string;
XsdFileName: string;
parseError: IXMLDOMParseError;
parseError2: IXMLDOMParseError2;
begin
XmlDocument := CoFreeThreadedDOMDocument60.Create();
if not Assigned(XmlDocument) then
RaiseLastOSError();
if not XmlDocument.load(XmlFileName) then
RaiseLastOSError();
SchemaCollection := CoXMLSchemaCache60.Create();
if not Assigned(SchemaCollection) then
RaiseLastOSError();
for XsdFileName in XsdFileNames do
begin
XsdDocument := CoFreeThreadedDOMDocument60.Create();
if not Assigned(XsdDocument) then
RaiseLastOSError();
if not XsdDocument.load(XsdFileName) then
RaiseLastOSError();
targetNamespaceNode := XsdDocument.documentElement.attributes.getNamedItem('targetNamespace');
if Assigned(targetNamespaceNode) then
namespaceURI := targetNamespaceNode.nodeValue
else
namespaceURI := NullAsStringValue;
SchemaCollection.Add(namespaceURI, XsdDocument);
end;
XmlDocument.schemas := SchemaCollection;
parseError := XmlDocument.validate();
if Supports(parseError, IXMLDOMParseError2, parseError2) then
ProcessParseError2(parseError2)
else
ProcessParseError(parseError);
Result := not HaveValidationResult;
end;
initialization
// http://chrisbensen.blogspot.com/2007/06/delphi-tips-and-tricks_26.html
if Assigned(ComObj.CoInitializeEx) then
ComObj.CoInitializeEx(nil, COINIT_MULTITHREADED)
else
CoInitialize(nil);
finalization
CoUninitialize;
end.
Let me know with these samples
Have fun with the sources and let me know.
–jeroen






François said
Thanks Jeroen! Very interesting.
Quite a coincidence with the latest coding Horror: http://www.codinghorror.com/blog/archives/001319.html
jpluimers said
Indeed. Ain’t all these line-ending issues a great déjà-vu :-)
–jeroen
Blaz said
Thank you for the code!
Components4Developers
http://www.components4developers.com
The best SOA, ESB and EAI components for the best developers.
jpluimers said
Thanks!
It still is odd for me to see the SOA acronym, in Dutch it usually has nothing to do with computing.
–jeroen