Skip to content

Instantly share code, notes, and snippets.

@jonforums
Created July 11, 2011 13:50
Show Gist options
  • Save jonforums/1075878 to your computer and use it in GitHub Desktop.
Save jonforums/1075878 to your computer and use it in GitHub Desktop.
XML validation with external DTD in .NET experiment
/*
* Author: Jon Maken
* Creation Date: 5/24/2004 1:03 PM
* Version: 0.95, 7/2/2004
* License: Modified BSD License (3-clause)
*/
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
namespace XCheck
{
/// <summary>
/// XCheck exit codes
/// </summary>
enum ExitCode
{
OK = 0, // XML is well-formed and valid
CmdLineErr = -1, // incorrect command line invocation
IOErr = -2, // I/O error
XmlParseErr = -3, // XML parsing error
ValErr = -4, // Validation error
}
/// <summary>
/// <para>Console app that checks if the specified XML file is well formed.
/// If the XML file has an embedded DTD/XSD schema reference or a DTD/XSD
/// schema location is given on the command line, the XML file will be
/// validated against the specified schema.</para>
/// <para>NOTE: only a DTD or an XSD location can be provided as a command
/// line argument; both can not be specified.
/// </para>
/// <example>Example command line invocations:
/// Well-formed? <c>xcheck test.xml</c>
/// Well-formed and valid (DTD)? <c>xcheck test.xml -d valid.dtd rootElement</c>
/// Well-formed and valid (XSD)? <c>xcheck test.xml -s valid.xsd</c>
/// </example>
/// <returns>simple error message to StdErr and integer exit code.</returns>
/// </summary>
class XCheck
{
private static bool valErrFlag = false;
private static string usage =
"\nFAILED. Usage = xcheck XML_file [-d DTD_file XML_root | -s XSD_file]";
/*******************************************************************************
* MAIN METHOD
*/
public static int Main(string[] args)
{
string xmlFile; // XML file to check
string dtdFile; // DTD file that validates XML file
string xsdFile; // XSD file that validates XML file
string xmlRoot; // root element of XML file
FileStream xmlStream = null; // XML file as a FileStream object
XmlValidatingReader reader;
XmlParserContext context;
// check command line arguments and react accordingly
switch (args.Length)
{
case 1:
case 3:
case 4:
GetArgs(args, out xmlFile, out xmlRoot,
out dtdFile, out xsdFile);
break;
default:
Console.Error.WriteLine(usage);
return (int) ExitCode.CmdLineErr;
}
// if DTD argument, build up an XmlParserContext in order to
// validate an XML fragment then create an XmlValidatingReader
// using a Stream (xmlFile), XmlNodeType (Document) and the
// XmlParserContext
if (dtdFile != String.Empty && xmlRoot != String.Empty)
{
InitParserContext(xmlRoot, dtdFile, out context);
try
{
xmlStream = new FileStream(xmlFile, FileMode.Open);
}
catch (Exception)
{
Console.Error.WriteLine("\tFAILED. I/O error occurred reading XML file");
Environment.Exit((int) ExitCode.IOErr);
}
reader = new XmlValidatingReader(xmlStream,
XmlNodeType.Document, context);
reader.ValidationType = ValidationType.Auto;
reader.ValidationEventHandler +=
new ValidationEventHandler(ValidationCallback);
}
// else set up a validating reader that automagically recognizes internal
// DTD's or XSD schemas and validates accordingly. Hook up a
// validation event handler so that all validation errors and warnings
// are captured, not just the first one which would throw an XMLException
else
{
reader = new XmlValidatingReader(
new XmlTextReader(xmlFile));
reader.ValidationType = ValidationType.Auto;
reader.ValidationEventHandler +=
new ValidationEventHandler(ValidationCallback);
}
// if XSD schema argument, add the specified schema to the reader's
// XmlSchemaCollection using the namespace defined in the
// targetNamespace attribute of the schema by passing null as 1st
// arg to Add()
if (xsdFile != String.Empty)
{
reader.Schemas.Add(null, xsdFile);
}
// try parsing XML files, catch appropriate exceptions, print
// appropriate msgs, and always close the reader. IOException
// will handle any file opening issues.
try
{
while (reader.Read());
}
catch (System.IO.IOException)
{
Console.Error.WriteLine("\tFAILED. I/O error occurred");
return (int) ExitCode.IOErr;
}
catch (XmlException e)
{
Console.Error.WriteLine(e.Message);
return (int) ExitCode.XmlParseErr;
}
finally
{
if (xmlStream != null)
xmlStream.Close();
reader.Close();
}
// Exit with failure if any validation errors occur, otherwise exit
// with success code. Since a validation event handler is defined
// validation will continue until all errors are discovered
if (!valErrFlag)
{
return (int) ExitCode.OK;
}
else
{
return (int) ExitCode.ValErr;
}
}
/*******************************************************************************
* METHODS
*/
// write validation warning and error messages to StdErr
private static void ValidationCallback(object sender, ValidationEventArgs e)
{
if (e.Severity == XmlSeverityType.Warning)
{
Console.Error.WriteLine("VALIDATION WARNING: " + e.Message);
}
else
{
valErrFlag = true;
Console.Error.WriteLine("VALIDATION ERROR: " + e.Message);
}
}
// parse command line and check that specified files exist
private static void GetArgs(string[] args, out string xmlFile,
out string xmlRoot, out string dtdFile,
out string xsdFile)
{
int argIndex;
// XML file must be the first command line argument
if (File.Exists(args[0]))
{
xmlFile = args[0];
}
else
{
xmlFile = String.Empty;
}
// check and parse DTD file name
argIndex = Array.IndexOf(args, "-d");
if (argIndex > 0)
{
// ensure # of args is consisent with a DTD
// and the XML root element
if (args.Length == 4)
{
dtdFile = args[argIndex + 1];
xmlRoot = args[argIndex + 2];
if (!File.Exists(dtdFile))
dtdFile = String.Empty;
}
else
{
dtdFile = xmlRoot = null;
Console.Error.WriteLine(usage);
Environment.Exit((int) ExitCode.CmdLineErr);
}
}
else
{
dtdFile = String.Empty;
xmlRoot = String.Empty;
}
// check and parse XSD file name.
argIndex = Array.IndexOf(args, "-s");
if (argIndex > 0)
{
// ensure # of args consistent
if (args.Length == 3)
{
xsdFile = args[argIndex + 1];
if (!File.Exists(xsdFile))
xsdFile = String.Empty;
}
else
{
xsdFile = null;
Console.Error.WriteLine(usage);
Environment.Exit((int) ExitCode.CmdLineErr);
}
}
else
{
xsdFile = String.Empty;
}
}
// build context for parser in order to validate with the specified DTD
private static void InitParserContext(string xmlRoot, string dtdFile,
out XmlParserContext context)
{
context = new XmlParserContext(null, // XmlNameTable
null, // XmlNamespaceManager
xmlRoot, // docTypeName
String.Empty, // pubId
dtdFile, // sysId
String.Empty, // internalSubset
String.Empty, // baseURI
String.Empty, // xmlLang
XmlSpace.Default,// xmlSpace
Encoding.UTF8); // Encoding
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment