I have a Web Method (within a SOAP Web Service) with a signature of:
public msgResponse myWebMethod([XmlAnyElement] XmlElement msgRequest)
I chose to use the XmlElement parameter after reading that it would allow me to perform my own XSD validation on the parameter. The problem is that the parameter can be quite large (up to 80Mb of XML) so calling XmlElement.OuterXML() as suggested in the link isn't a very practical method.
Is there another way to validate the XmlElement object against an XSD?
More generally, is this an inappropriate approach for implementing a web service expecting large amounts of XML? I've come across some hints at using SoapExtensions for gaining access to the input stream directly but am not sure this is the correct approach for my situation.
Note: Unfortunately, I'm chained to an existing WSDL and XSD that I have no power to alter which is why I went with a non-WCF implementation in the first place.
Here's a quick example. Just pass your XmlElement to this method:
private static void TheAnswer(IXPathNavigable inputElement)
{
var schemas = new XmlSchemaSet();
schemas.Add("http://foo.org/importvalidator.xsd",
#"..\..\validator.xsd");
var settings = new XmlReaderSettings
{
Schemas = schemas,
ValidationFlags =
XmlSchemaValidationFlags.
ProcessIdentityConstraints |
XmlSchemaValidationFlags.
ReportValidationWarnings,
ValidationType = ValidationType.Schema
};
settings.ValidationEventHandler +=
(sender, e) =>
Console.WriteLine("{0}: {1}", e.Severity, e.Message);
using (
XmlReader documentReader =
inputElement.CreateNavigator().ReadSubtree())
{
using (
XmlReader validatingReader = XmlReader.Create(
documentReader, settings))
{
while (validatingReader.Read())
{
}
}
}
}
Related
I have a c# script that validates an XML document against an XSD document, as follows:
static bool IsValidXml(string xmlFilePath, string xsdFilePath)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(null, xsdFilePath);
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Compile();
try
{
XmlReader xmlRead = XmlReader.Create(xmlFilePath, settings);
while (xmlRead.Read())
{ };
xmlRead.Close();
}
catch (Exception e)
{
return false;
}
return true;
}
I've compiled this after looking at a number of MSDN articles and questions here where this is the solution. It does correctly validate that the XSD is formed well (returns false if I mess with the file) and checks that the XML is formed well (also returns false when messed with).
I've also tried the following, but it does the exact same thing:
static bool IsValidXml(string xmlFilePath, string xsdFilePath)
{
XDocument xdoc = XDocument.Load(xmlFilePath);
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null, xsdFilePath);
try
{
xdoc.Validate(schemas, null);
}
catch (XmlSchemaValidationException e)
{
return false;
}
return true;
}
I've even pulled a completely random XSD off the internet and thrown it into both scripts, and it still validates on both. What am I missing here?
Using .NET 3.5 within an SSIS job.
In .NET you have to check yourself if the validator actually matches a schema component; if it doesn't, there is no exception thrown, and so your code will not work as you expect.
A match means one or both of the following:
there is one global element in your schema set with a qualified name that is the same as your XML document element's qualified name.
the document element has an xsi:type attribute, that is a qualified name pointing to a global type in your schema set.
In streaming mode, you can do this check easily. This pseudo-kind-of-code should give you an idea (error handling not shown, etc.):
using (XmlReader reader = XmlReader.Create(xmlfile, settings))
{
reader.MoveToContent();
var qn = new XmlQualifiedName(reader.LocalName, reader.NamespaceURI);
// element test: schemas.GlobalElements.ContainsKey(qn);
// check if there's an xsi:type attribute: reader["type", XmlSchema.InstanceNamespace] != null;
// if exists, resolve the value of the xsi:type attribute to an XmlQualifiedName
// type test: schemas.GlobalTypes.ContainsKey(qn);
// if all good, keep reading; otherwise, break here after setting your error flag, etc.
}
You might also consider the XmlNode.SchemaInfo which represents the post schema validation infoset that has been assigned to a node as a result of schema validation. I would test different conditions and see how it works for your scenario. The first method is recommended to reduce the attack surface in DoS attacks, as it is the fastest way to detect completely bogus payloads.
public void WriteXmlLog(string logType, string logFlag, string logModule, string logLocation, string logText, string logStackTrace)
{
Mutex objMutex = new Mutex(false, #"Global\MySharedLog");
objMutex.WaitOne();
try
{
if(!File.Exists(_logFilePath))
{
File.WriteAllText(_logFilePath, "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\r\n <AppXmlLogWritter></AppXmlLogWritter>");
}
string currentDateTime = DateTime.Now.ToString("yyyyMMddHHmmss");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(_logFilePath);
XmlElement newelement = xmlDoc.CreateElement("LogData");
XmlElement xmlLogID = xmlDoc.CreateElement("LogID");
XmlElement xmlLogDateTime = xmlDoc.CreateElement("LogDateTime");
XmlElement xmlLogType = xmlDoc.CreateElement("LogType");
XmlElement xmlLogFlag = xmlDoc.CreateElement("LogFlag");
XmlElement xmlLogApplication = xmlDoc.CreateElement("LogApplication");
XmlElement xmlLogModule = xmlDoc.CreateElement("LogModule");
XmlElement xmlLogLocation = xmlDoc.CreateElement("LogLocation");
XmlElement xmlLogText = xmlDoc.CreateElement("LogText");
XmlElement xmlLogStackTrace = xmlDoc.CreateElement("LogStackTrace");
xmlLogID.InnerText = _logIDPrefix + currentDateTime + randomNumber;
xmlLogDateTime.InnerText = currentDateTime;
xmlLogType.InnerText = ((LogTypes)Convert.ToInt32(logType)).ToString();
xmlLogFlag.InnerText = logFlag;
xmlLogApplication.InnerText = _logApplication;
xmlLogModule.InnerText = logModule;
xmlLogLocation.InnerText = logLocation;
xmlLogText.InnerText = logText;
xmlLogStackTrace.InnerText = logStackTrace;
newelement.AppendChild(xmlLogID);
newelement.AppendChild(xmlLogDateTime);
newelement.AppendChild(xmlLogType);
newelement.AppendChild(xmlLogFlag);
newelement.AppendChild(xmlLogApplication);
newelement.AppendChild(xmlLogModule);
newelement.AppendChild(xmlLogLocation);
newelement.AppendChild(xmlLogText);
xmlDoc.DocumentElement.AppendChild(newelement);
xmlDoc.Save(_logFilePath);
}
finally
{
objMutex.ReleaseMutex();
}
}
I am writing logs in xml file of several different applications.
see below code i m using Mutex class for locking purpose Means when two thread comes at a time mutex.waitone() method wouldn't release second thread if first thread doing task.
Is it possible without using Mutex class i have to write log in xml file of different aplications
Mutex is a standard technique to synchronize access to a shared resource across multiple processes. If it was concurrent access within the same process you could have used more lightweight classes such as ReaderWriterLockSlim. But since you need to support cross process synchronizations Mutexes is the way to go.
Or maybe start asking yourself whether writing logs to a text file is appropriate in this case. Have you considered logging to the EventLog which will handle concurrency for you? Have you considered logging to a shared database which also will handle the concurrency for you?
By the way have you considered using a logging library to perform your logging instead of manually doing it with some XmlDocument? .NET already has built-in tracing capabilities. Maybe you should explore them before rolling such custom solutions.
Many questions you should probably be asking yourself now.
I am writing an series of web interfaces to some data. I have WebMethods to return the data in DataSet and XmlDataDocument format (The XmlDataDocument removes all the schema overhead.)
[WebMethod]
public XmlDataDocument Search_XML( string query ) {
return new XmlDataDocument( Search_DataSet( query ) );
}
[WebMethod]
public DataSet Search_DataSet( string query ) {
DataSet result = new DataSet( "SearchResults" );
//... Populate DataSet here
return result;
}
I have also created a function that accepts an XSL formatting string and returns the formated results, allowing the client to format an HTML response they can inject right into their webpage:
public string Search_XSL( string query, string xsl ) {
string result = "";
XmlDataDocument resultxml = Search_XML( query );
XslCompiledTransform transform = new XslCompiledTransform();
using ( StringReader xslstringreader = new StringReader( xsl ) ) {
using ( XmlReader xslxmlreader = XmlReader.Create( xslstringreader ) ) {
using ( MemoryStream transformedmemorystream = new MemoryStream() ) {
using ( StreamWriter transformedstreamwriter = new StreamWriter( transformedmemorystream ) ) {
try {
transform.Load( xslxmlreader );
transform.Transform( resultxml, null, transformedstreamwriter );
transformedstreamwriter.Flush();
transformedmemorystream.Position = 0;
using ( StreamReader transformedreader = new StreamReader( transformedmemorystream ) ) {
result = transformedreader.ReadToEnd();
}
}
catch ( Exception ex ) {
result = ex.InnerException.ToString();
}
}
}
}
}
return result;
}
My question is, how do I implement a WebMethod-like interface for this Search_XSL() function so that I can return the resulting string exactly as the function does, without the XML encoding the WebMethod puts around it? Would that be a new Web Form? How do I implement a Web Form with no actual HTML, just accepting form parameters? Not sure where to start here.
Edit: It looks like a "Generic Handler" .ashx file is the way to go. Is this the right approach?
If you need an HTTP endpoint that processes an HttpContext and returns a custom response, then using IHttpHandler via a Generic Web handler (*.ashx) would be the correct approach to take.
You would read the values from the request query string and then process the request. Your generic handler would use the HttpContext.Response to set the content type of the output stream to text/html and would write the resulting HTML you wish to inject.
WCF is the way to go in .net. You can configure your methods to return json or any number of of other type of serializations. While a generic handler could work there is much better support for wcf. Check out this question for more info.
WebMethods use SOAP, which means you can't remove the SOAP envelope at the service layer. Your original idea is right, here's what you need to finish:
protected void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "text/html";
Response.Write("Hello world");
}
protected override void Render(HtmlTextWriter writer)
{
}
It's a quick and dirty solution, but it works.
It would be fantastic if you could help me rid of these warnings below.
I have not been able to find a good document. Since the warnings are concentrated in just the private void ValidateConfiguration( XmlNode section ) section, hopefully this is not terribly hard to answer, if you have encountered this before.
Thanks!
'System.Configuration.ConfigurationException.ConfigurationException(string)' is obsolete: 'This class is obsolete, to create a new exception create a System.Configuration!System.Configuration.ConfigurationErrorsException'
'System.Xml.XmlValidatingReader' is obsolete: 'Use XmlReader created by XmlReader.Create() method using appropriate XmlReaderSettings instead. http://go.microsoft.com/fwlink/?linkid=14202'
private void ValidateConfiguration( XmlNode section )
{
// throw if there is no configuration node.
if( null == section )
{
throw new ConfigurationException("The configuration section passed within the ... class was null ... there must be a configuration file defined.", section );
}
//Validate the document using a schema
XmlValidatingReader vreader = new XmlValidatingReader( new XmlTextReader( new StringReader( section.OuterXml ) ) );
// open stream on Resources; the XSD is set as an "embedded resource" so Resource can open a stream on it
using (Stream xsdFile = XYZ.GetStream("ABC.xsd"))
using (StreamReader sr = new StreamReader(xsdFile))
{
vreader.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
vreader.Schemas.Add(XmlSchema.Read(new XmlTextReader(sr), null));
vreader.ValidationType = ValidationType.Schema;
// Validate the document
while (vreader.Read()) { }
if (!_isValidDocument)
{
_schemaErrors = _sb.ToString();
throw new ConfigurationException("XML Document not valid");
}
}
}
// Does not cause warnings.
private void ValidationCallBack( object sender, ValidationEventArgs args )
{
// check what KIND of problem the schema validation reader has;
// on FX 1.0, it gives a warning for "<xs:any...skip" sections. Don't worry about those, only set validation false
// for real errors
if( args.Severity == XmlSeverityType.Error )
{
_isValidDocument = false;
_sb.Append( args.Message + Environment.NewLine );
}
}
Replace
throw new ConfigurationException(....)
with
throw new ConfigurationErrorsException(....)
Replace XmlValidatingReader vreader = new XmlValidatingReader(...)
with
var vreader = XmlReader.Create(new StringReader(section.OuterXml),
new XmlReaderSettings
{
ValidationType = ValidationType.Schema
});
Basically, it's telling you to use the XmlReaderSettings instead of the XmlValidatingReader, which was deprecated.
Personally I'm not going to do the conversion, I think that you actually doing that will be good for your coding development, so here is some resources:
Look at the overloads of the XmlReader.Create() method, specifically this one.
Then have a look at the different properties associated with the XmlReaderSettings class: http://msdn.microsoft.com/en-us/library/system.xml.xmlreadersettings_members.aspx
Give it a try, see what happens and if your still having problems, ask another question :)
HTH
Im trying to validate an XML file using a .DTD but it gives me the following error.
'ENTITY' is an unexpected token. The expected token is 'DOCTYPE'. Line 538, position 3.
public static void Validate(string xmlFilename, string schemaFilename)
{
XmlTextReader r = new XmlTextReader(xmlFilename);
XmlValidatingReader validator = new XmlValidatingReader(r);
validator.ValidationType = ValidationType.Schema;
XmlSchemaCollection schemas = new XmlSchemaCollection();
schemas.Add(null, schemaFilename);
validator.ValidationEventHandler += new ValidationEventHandler(ValidationEventHandler);
try
{
while (validator.Read())
{ }
}
catch (XmlException err)
{
Console.WriteLine(err.Message);
}
finally
{
validator.Close();
}
}
The DTD im using to validate = http://www.editeur.org/onix/2.1/reference/onix-international.dtd
I hope someone can help me thanks!
I realise this is a really old question, but for anyone else struggling with this problem, here's what I did.
I gave up trying to validate with the DTD.
Instead, I ended up using the onix 2.1 xsd available at http://www.editeur.org/15/Previous-Releases/#R%202.1%20Downloads. I had to set the default namespace:
var nt = new NameTable();
var ns = new XmlNamespaceManager(nt);
ns.AddNamespace(string.Empty, "http://www.editeur.org/onix/2.1/reference");
var context = new XmlParserContext(null, ns, null, XmlSpace.None);
and then when loading the xml, turn off DTD parsing (this is using .NET4)
var settings = XmlReaderSettings
{
ValidationType = System.Xml.ValidationType.Schema,
DtdProcessing = DtdProcessing.Ignore
}
using(var reader = XmlReader.Create("path to xml file", settings)) { ... }
Edit:
Just noticed: your validation type is also set wrong. Try setting it to ValidationType.DTD instead of Schema.
ValidationType at MSDN
--
The error means exactly as it states- the DTD that is referenced is not well formed, as DOCTYPE should be present before any other declarations in a DTD.
Document Type Definition (Wikipedia)
Introduction to DTD (w3schools)
You might be able to get around this by downloading a local copy, modifying it to add in the expected root element yourself, and then referencing your edited version in your source.