The most performant way to validate XML against XSD - c#

I get a string variable with XML in it and have a XSD file. I have to validate the XML in the string against the XSD file and know there is more than one way (XmlDocument, XmlReader, ... ?).
After the validation I just have to store the XML, so I don't need it in an XDocument or XmlDocument.
What's the way to go if I want the fastest performance?

Others have already mentioned the XmlReader class for doing the validation, and I wont elaborate further into that.
Your question does not specify much context. Will you be doing this validation repeatedly for several xml documents, or just once? I'm reading a scenario where you are just validating a lot of xml documents (from a third party system?) and storing them for future use.
My contribution to the performance hunt would be to use a compiled XmlSchemaSet which would be thread safe, so several threads can reuse it without needing to parse the xsd document again.
var xmlSchema = XmlSchema.Read(stream, null);
var xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.Add(xmlSchema);
xmlSchemaSet.Compile();
CachedSchemas.Add(name, xmlSchemaSet);

I would go for the XmlReader with XmlReaderSettings because does not need to load the complete XML in memory. It will be more efficient for big XML files.

I think the fastest way is to use an XmlReader that validates the document as it is being read. This allows you to validate the document in only one pass: http://msdn.microsoft.com/en-us/library/hdf992b8.aspx

Use an XmlReader configured to perform validation, with the source being a TextReader.
You can manually specify the XSD the XmlReader is to use if you don't want to rely on declarations in the input document (with XmlReaderSettings.Schemas property)
A start (just assumes XSD-instance declarations in the input document) would be:
var settings = new XmlReaderSettings {
ConformanceLevel = ConformanceLevel.Document,
ValidationType = ValidationType.Schema,
ValidationFlags = XmlSchemaValidationFlags.ProcessSchemaLocation |
XmlSchemaValidationFlags.ProcessInlineSchema,
};
int warnings = 0;
int errors = 0;
settings.ValidationEventHandler += (obj, ea) => {
if (args.Severity == XmlSeverityType.Warning) {
++warnings;
} else {
++errors;
}
};
XmlReader xvr = XmlReader.Create(new StringReader(inputDocInString), settings);
try {
while (xvr.Read()) {
// do nothing
}
if (0 != errors) {
Console.WriteLine("\nFailed to load XML, {0} error(s) and {1} warning(s).", errors, warnings);
} else if (0 != warnings) {
Console.WriteLine("\nLoaded XML with {0} warning(s).", warnings);
} else {
System.Console.WriteLine("Loaded XML OK");
}
Console.WriteLine("\nSchemas loaded durring validation:");
ListSchemas(xvr.Schemas, 1);
} catch (System.Xml.Schema.XmlSchemaException e) {
System.Console.Error.WriteLine("Failed to read XML: {0}", e.Message);
} catch (System.Xml.XmlException e) {
System.Console.Error.WriteLine("XML Error: {0}", e.Message);
} catch (System.IO.IOException e) {
System.Console.Error.WriteLine("IO error: {0}", e.Message);
}

Related

OutOfMemoryException when save XmlDocument from XmlReader.ReadInnerXml

I am using XmlReader.ReadInnerXml() to load part of an XML file and save it as an XmlDocument. I ran into OutOfMemoryException when the innerXml part was over 2 GB (an estimate). What is the best way to handle this error? Is there a better way to create a large xml from XmlReader? Can I save the content without loading into memory?
using (XmlReader xmlRdr = XmlReader.Create(file))
{
xmlRdr.MoveToContent();
while (xmlRdr.Read())
{
//when read to XmlNodeType.Element and xmlRdr.Name meets certain criteria
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
try
{
xmlDoc.LoadXml(xmlRdr.ReadInnerXml());
//get a few data from within the innerXml and eventually use XmlWritter to save the file
}
catch(Exception e)
{
string content = $"{e.GetType()} {e.Message} {NewLine} {objId}";
//send content to log file and email
}
}
}
As said in one of the comments maybe try using StreamReader and StreamWriter
This tutorial might help

Using Multiple XSD files Stored as Embedded Resources to Validate XML File

I have a complex schema structure, with multiple schema using the tag. The issue I was having was accessing the schema files that were stored in sub folders (something like /xsd/Person/personcommon.xsd), as the file structure doesn't exist when added as an embedded resource. I have written a .dll to validate the XML file and then de-serialize the file into objects. These objects are called by another application. After searching around I came up with the following to help validate the XML:
public bool ValidateXML(string xmlFilePath)
{
// Set the namespace followed by the folder
string prefix = "MyApp.xsd";
// Variable to determine if file is valid
bool isValid = true;
// Assembly object to find the embedded resources
Assembly myAssembly = Assembly.GetExecutingAssembly();
// Get all the xsd resoruces
var resourceNames = myAssembly.GetManifestResourceNames().Where(name => name.StartsWith(prefix));
try
{
// Load the XML
XDocument xmlDoc = XDocument.Load(xmlFilePath);
// Create new schema set
XmlSchemaSet schemas = new XmlSchemaSet();
// Iterate through all the resources and add only the xsd's
foreach (var name in resourceNames)
{
using (Stream schemaStream = myAssembly.GetManifestResourceStream(name))
{
using (XmlReader schemaReader = XmlReader.Create(schemaStream))
{
schemas.Add(null, schemaReader);
}
}
}
// Call to the validate method
xmlDoc.Validate(schemas, (o, e) => { this.ErrorMessage = string.Format("File failure: There was an error validating the XML document: {0} ", e.Message); isValid = false; }, true);
return isValid;
}
catch (Exception ex)
{
isValid = false;
this.ErrorMessage = string.Format("File failure: There was an error validating the XML document: {0}", ex.ToString());
return isValid;
}
}
The issue I am now having is in my unit tests. Debuging through the code I see exceptions are thrown when schemas.Add() is called, it can't find the referenced XSD files. The weird thing is that it still validates the XML file passed in correctly.
Is this expected behavior and is the code I have written valid / is there a better way I can access these XSD files.

XDocument.Parse Success or Failure?

I am using
XDocument doc = XDocument.Parse(somestring);
But how do I validate if the string somestring is a well formed XML. Is Try Catch the only way to do this?
Is Try Catch the only way to do this?
There is no TryParse method for XDocument, so try-catch is probably the best bet. Also consider validating your XML against a schema since it will not only check if the XML is well-formed, but also checks for constraints.
You may see: Validation Against XML Schema (XSD) with the XmlValidatingReader
If you only need to check whether the document is well-formed, the fastest way is to use XmlReader as follows:
var isWellFormedXml = true;
try
{
using (var reader = XmlReader.Create(stream)) // can be a mem stream for string validation
{
while (reader.Read()) {}
}
}
catch
{
isWellFormedXml = false;
}
This way you don't spend memory for XDocument DOM. BTW, XDocument.Parse() uses XmlReader for processing XML, so the exceptions are the same, if you need to analyse them.

determine if xml file contains data - c#

How do i know if my XML file has data besides the name space info:
Some of the files contain this:
<?xml version="1.0" encoding="UTF-8"?>
And if i encounter such a file, i want to place the file in an error directory
You could use the XmlReader to avoid the overhead of XmlDocument. In your case, you will receive an exception because the root element is missing.
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
using (StringReader strReader = new StringReader(xml))
{
//You can replace the StringReader object with the path of your xml file.
//In that case, do not forget to remove the "using" lines above.
using (XmlReader reader = XmlReader.Create(strReader))
{
try
{
while (reader.Read())
{
}
}
catch (XmlException ex)
{
//Catch xml exception
//in your case: root element is missing
}
}
}
You can add a condition in the while(reader.Read()) loop after you checked the first nodes to avoid to read the entire xml file since you just want to check if the root element is missing.
I think the only way is to catch an exception when you try and load it, like this:
try
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(Server.MapPath("XMLFile.xml"));
}
catch (System.Xml.XmlException xmlEx)
{
if (xmlEx.Message.Contains("Root element is missing"))
{
// Xml file is empty
}
}
Yes, there is some overhead, but you should be performing sanity checks like this anyway. You should never trust input and the only way to reliably verify it is XML is to treat it like XML and see what .NET says about it!
XmlDocument xDoc = new XmlDocument();
if (xDoc.ChildNodes.Count == 0)
{ // xml document is empty }
if (xDoc.ChildNodes.Count == 1)
{ // in xml document is only declaration node. (if you are shure that declaration is allways at the begining }
if (xDoc.ChildNodes.Count > 1)
{ // there is declaration + n nodes (usually this count is 2; declaration + root node) }
Haven't tried this...but should work.
try
{
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
}
catch (XmlException exc)
{
//invalid file
}
EDIT: Based on feedback comments
For large XML documents see Thomas's answer. This approach can have performance issues.
But, if it is a valid xml and the program wants to process it then this approach seems better.
If you aren't worried about validity, just check to see if there is anything after the first ?>. I'm not entirely sure of the C# syntax (it's been too long since I used it), but read the file, look for the first instance of ?>, and see if there is anything after that index.
However, if you want to use the XML later or you want to process the XML later, you should consider PK's answer and load the XML into an XmlDocument object. But if you have large XML documents that you don't need to process, then a solution more like mine, reading the file as text, might have less overhead.
You could check if the xml document has a node (the root node) and check it that node has inner text or other children.
As long as you aren't concerned with the validity of the XML document, and only want to ensure that it has a tag other than the declaration, you could use simple text processing:
var regEx = new RegEx("<[A-Za-z]");
bool foundTags = false;
string curLine = "";
using (var reader = new StreamReader(fileName)) {
while (!reader.EndOfStream) {
curLine = reader.ReadLine();
if (regEx.Match(curLine)) {
foundTags = true;
break;
}
}
}
if (!foundTags) {
// file is bad, copy.
}
Keep in mind that there's a million other reasons that the file may be invalid, and the code above would validate a file consisting only of "<a". If your intent is to validate that the XML document is capable of being read, you should use the XmlDocument approach.

How to do streaming read of a large XML file in C# 3.5

How can you do a streaming read on a large XML file that contains a xs:sequence just below root element, without loading the whole file into a XDocument instance in memory?
Going with a SAX-style element parser and the XmlTextReader class created with XmlReader.Create would be a good idea, yes. Here's a slightly-modified code example from CodeGuru:
void ParseURL(string strUrl)
{
try
{
using (var reader = XmlReader.Create(strUrl))
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
var attributes = new Hashtable();
var strURI = reader.NamespaceURI;
var strName = reader.Name;
if (reader.HasAttributes)
{
for (int i = 0; i < reader.AttributeCount; i++)
{
reader.MoveToAttribute(i);
attributes.Add(reader.Name,reader.Value);
}
}
StartElement(strURI,strName,strName,attributes);
break;
//
//you can handle other cases here
//
//case XmlNodeType.EndElement:
// Todo
//case XmlNodeType.Text:
// Todo
default:
break;
}
}
}
catch (XmlException e)
{
Console.WriteLine("error occured: " + e.Message);
}
}
}
}
I can't add a comment, since I just signed up but the code sample posted by Hirvox and currently selected as the answer has a bug in it. It should not have the new statement when using the static Create method.
Current:
using (var reader = new XmlReader.Create(strUrl))
Fixed:
using (var reader = XmlReader.Create(strUrl))
I think it's not possible if you want to use object model (i.e. XElement\XDocument) to query XML. Obviously, you can't build XML objects tree without reading enough data. However you can use XmlReader class.
The XmlReader class reads XML data
from a stream or file. It provides
non-cached, forward-only, read-only
access to XML data.
Heres is a howto: http://support.microsoft.com/kb/301228/en-us Just remember that you should not use XmlTextReader but instead XmlReader in conjunction with XmlReader.Create
I'm confused by the mention of the "xs:sequence" - this is a XML Schema element.
Are you trying to open a large XML Schema file? Are you open a large XML file that is based on that schema? Or are you trying to open a large XML file and validate it at the same time?
None of these situations should provide you with a problem using the standard XmlReader (or XmlValidatingReader).
Reading XML with XMLReader: http://msdn.microsoft.com/en-us/library/9d83k261(VS.80).aspx
That code sample tries to turn XmlReader style code into SAX style code - if you're writing code from scratch I'd just use XmlReader as it was intended - Pull not Push.

Categories

Resources