@echo off SET SoapValidator=%TMP%\SoapValidator if exist "%SoapValidator%.exe" goto :Doit echo #if NEVER >"%SoapValidator%.cs.h" copy "%SoapValidator%.cs.h" + "%0" "%SoapValidator%.cs" %WINDIR%\Microsoft.NET\Framework\v2.0.50727\csc.exe /out:"%SoapValidator%.exe" /r:System.Xml.dll "%SoapValidator%.cs" :Doit "%SoapValidator%.exe" %* exit /B 0 #endif /****************************************************************************** * * SoapValidator v 1.0 * Copyright (C) 2011 Ivan Krivyakov, http://www.ikriv.com * Distributed under Apache License * http://www.apache.org/licenses/LICENSE-2.0.html * * Validates SOAP message against WSDL, or any other XML document against a schema * Usage: SoapValidator soapfile wsdl1 xsd1 xsd2 * *******************************************************************************/ // C#, .Net 2.0 or later using System; using System.IO; using System.Xml; using System.Xml.Schema; namespace SoapValidator { class Program { static int Main(string[] args) { return new Program().Run(args); } class SchemaReadException : ApplicationException { public SchemaReadException(XmlReader reader, string path, Exception inner) : base(GetXmlErrorMessage(reader, path, inner), inner) { } } private int Run(string[] args) { try { if (IsUsageWanted(args)) { Usage(); return 1; } string inputPath = GetInputPath(args); using (XmlReader xmlReader = XmlReader.Create(GetInputStream(inputPath), GetXmlReaderSettings(args))) { try { while (xmlReader.Read()) { } } catch (Exception ex) { Console.WriteLine(GetXmlErrorMessage(xmlReader, inputPath, ex)); return 1; } } Console.WriteLine("{0} is valid XML", inputPath); return 0; } catch (Exception ex) { Console.WriteLine("Error: {0}\r\n{1}", ex.GetType().Name, ex.Message); return 2; } } private static bool IsUsageWanted(string[] args) { return args.Length == 0 || args[0] == "/?"; } private static void Usage() { Console.WriteLine("Usage: SoapValidator message wsdl xsd1 xsd2 ..."); Console.WriteLine(" SoapValidator - wsdl xsd1 xsd2 ..."); Console.WriteLine(); Console.WriteLine("The second form reands standard input"); } private static string GetXmlErrorMessage(XmlReader xmlReader, string filePath, Exception ex) { return String.Format("{0}{1}: {2}: {3}", filePath, GetLineInfo(xmlReader as IXmlLineInfo), ex.GetType().Name, ex.Message ); } private static string GetLineInfo( IXmlLineInfo info ) { if (info == null) return String.Empty; if (!info.HasLineInfo()) return String.Empty; return String.Format("({0},{1})", info.LineNumber, info.LinePosition); } private XmlReaderSettings GetXmlReaderSettings(string[] args) { XmlReaderSettings settings = new XmlReaderSettings(); settings.ValidationType = ValidationType.Schema; settings.ValidationFlags = XmlSchemaValidationFlags.ProcessSchemaLocation | XmlSchemaValidationFlags.ProcessInlineSchema | XmlSchemaValidationFlags.ReportValidationWarnings; AddSchemas(settings.Schemas, args); return settings; } private string GetInputPath(string[] args) { if (args == null) throw new ArgumentNullException(); if (args.Length == 0) return null; return args[0]; } private TextReader GetInputStream(string path) { if (path == null || path == "-") return System.Console.In; return new StreamReader(path); } private void AddSchemas(XmlSchemaSet schemas, string[] args) { if (args == null) throw new ArgumentNullException(); AddSoapSchema(schemas); for (int i = 1; i < args.Length; ++i) { AddSchema(schemas, args[i]); } } private void AddSoapSchema(XmlSchemaSet schemas) { const string SoapSchemaUri = "http://schemas.xmlsoap.org/soap/envelope/"; XmlDocument soapSchema = new XmlDocument(); soapSchema.LoadXml(SoapSchemaXml); schemas.Add(SoapSchemaUri, new XmlNodeReader(soapSchema)); } private void AddSchema(XmlSchemaSet schemas, string location) { if (IsUrl(location)) { schemas.Add(null, location); } else if (IsWsdl(location)) { AddWsdl(schemas, location); } else { schemas.Add(GetSchemaFromFile(location)); } } private static bool IsUrl(string location) { return location.Contains("://"); } private static bool IsWsdl(string location) { return location.ToLower().EndsWith(".wsdl"); } private XmlSchema GetSchemaFromFile(string path) { using (XmlReader reader = XmlReader.Create(path)) { try { ValidationEventHandler onValidationEvent = delegate(object sender, ValidationEventArgs args) { OnValidationEvent(path, args); }; return XmlSchema.Read(reader, onValidationEvent); } catch (Exception ex) { throw new SchemaReadException(reader, path, ex); } } } private void OnValidationEvent(string file, ValidationEventArgs args) { Console.WriteLine("{0}: {1} {2}", file, args.Severity.ToString().ToUpper(), args.Message); } private void AddWsdl(XmlSchemaSet schemas, string location) { XmlDocument wsdlDoc = new XmlDocument(); wsdlDoc.Load(location); XmlNamespaceManager namespaces = new XmlNamespaceManager(wsdlDoc.NameTable); namespaces.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema"); XmlNodeList schemaNodes = wsdlDoc.SelectNodes("//xsd:schema", namespaces); foreach (XmlNode node in schemaNodes) { using (XmlReader reader = new XmlNodeReader(node)) { try { ValidationEventHandler onValidationEvent = delegate(object sender, ValidationEventArgs args) { OnValidationEvent(location, args); }; schemas.Add(XmlSchema.Read(reader, onValidationEvent)); } catch (Exception ex) { throw new SchemaReadException(reader, location, ex); } } } } const string SoapSchemaXml = @" Prose in the spec does not specify that attributes are allowed on the Body element 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification Fault reporting structure "; } };