Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

This has been driving me mad all day, thank goodness it's hometime.

 

Basically I have a routine that validates an XML document, here is the code:

 

Private Function ValidateXML(ByRef xmlDoc As XmlDocument, ByVal streamReader As StreamReader) As Boolean
       Dim buffer() As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(xmlDoc.InnerXml)
       Dim memstrm As New System.IO.MemoryStream(buffer)
       Dim xmlReader As New XmlTextReader(memstrm)
       memstrm.Close()
       Dim xmlVal As New XmlValidatingReader(xmlReader)
       xmlVal.Schemas.Add("urn:pleaseWork", Server.MapPath("XSD/AddEmployee.xsd"))
       xmlVal.ValidationType = ValidationType.Schema
       AddHandler xmlVal.ValidationEventHandler, AddressOf SchemaValidationEventHandler
       Try
           Dim isValid As Boolean = True
           While xmlVal.Read
               Dim strr As String = xmlVal.Value
           End While
           Return True
       Catch ex As Exception
           Return False
       End Try

   End Function

   Private Sub SchemaValidationEventHandler(ByVal sender As Object, ByVal e As System.Xml.Schema.ValidationEventArgs)
       If e.Severity = XmlSeverityType.Error Then
           Dim poo As String = String.Empty
           _errorCount += 1
       ElseIf e.Severity = XmlSeverityType.Warning Then
           Dim notAsPoo As String = String.Empty
           _errorCount += 1
       End If
   End Sub

 

Here is the XSD (AddEmployee.XSD):

 

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="AddEmployee" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:pleaseWork" elementFormDefault="qualified" targetNamespace="urn:pleaseWork">
   <xs:element name="Employees">
     <xs:complexType>
       <xs:sequence>
         <xs:element name="Employee">
           <xs:complexType>
             <xs:sequence>
               <xs:element name="ID" type="xs:int" minOccurs="1"/>
               <xs:element name="Name" type="xs:string" minOccurs="1"/>
               <xs:element name="ActivityID" type="xs:int" minOccurs="1"/>
             </xs:sequence>
           </xs:complexType>
         </xs:element>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
</xs:schema>

 

Finally here is my XML document;

 

<?xml version="1.0" standalone="yes"?>
<Employees>
 <Employee>
   <Name>Dave Jones</Name>
   <ActivityID>9</ActivityID>
 </Employee>
 <Employee>
   <ID>111</ID>
   <Name>Wayne Wallice</Name>
   <ActivityID>1</ActivityID>
 </Employee>
 <Employee>
   <ID>1111</ID>
   <Name>Justin Davies</Name>
   <ActivityID>2</ActivityID>
 </Employee>
 <Employee>
   <ID>11111</ID>
   <Name>Matthew Jones</Name>
   <ActivityID>0</ActivityID>
 </Employee>
 <Employee>
   <ID>111111</ID>
   <Name>Steve Pear</Name>
   <ActivityID>2</ActivityID>
 </Employee>
</Employees>

 

If you look at the XSD file you'll see that the element ID is supposed to mandatory in that I have set minoccurs to 1. However when I run the code this doesn't seem to be flagged as an error. Also I get 20 errors like Could not find schema information for the element Employees etc.

 

Can someone show me what stupid mistake I have made as I am totally stumped!

 

Thanks, Dave.

Posted

You have to associate the Xml document with the schema.

 

The code you use to add a schema to a validating reader is saying "If you find this namespace, use this contract to verify it"...but the validator never found the namespace.

 

Add the namespace to the root node:

 

<Employees xmlns="urn:pleaseWork">

 

Fyi, you may just want to change the namespace to pleaseWork or something along the lines of http://*/pleaseWork.

 

Also, you should take out the standalone attribute in the xml header...the standalone attribute is designed specifically to tell the xml reader that nothing outside of this document will affect how it is processed...ie, there is no dtd to validate against. Although most readers will validate anyway, they shouldn't.

 

 

Also, the XmlValidatingReader instance is redundant, as the XmlDocument has a validate function that performs exactly the same procedure.

Posted

I have taken your advice and come up with this solution which works:

 

Private Function ValidXML(ByVal xmlDoc As XmlDocument, ByVal requestID As Int32) As Boolean
       Dim buffer() As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(xmlDoc.InnerXml)
       Dim memstrm As New System.IO.MemoryStream(buffer)
       Dim xmlReader As New XmlTextReader(memstrm)
       Dim xmlVal As New XmlValidatingReader(xmlReader)
       Try
           Select Case requestID
               Case requestType.insert
                   xmlVal.Schemas.Add(Nothing, Server.MapPath("XSD/AddEmployee.xsd"))
               Case requestType.update
                   xmlVal.Schemas.Add(Nothing, Server.MapPath("XSD/AddEmployee.xsd"))
               Case requestType.delete
                   xmlVal.Schemas.Add(Nothing, Server.MapPath("XSD/AddEmployee.xsd"))
           End Select
           xmlVal.ValidationType = ValidationType.Schema
           AddHandler xmlVal.ValidationEventHandler, AddressOf SchemaValidationEventHandler
           While xmlVal.Read
               'Any invalid XML nonsense will cause SchemaValidationEventHandler to be called.
           End While
           If _isValid Then
               Return True
           Else
               Return False
           End If
       Finally
           memstrm.Close()
       End Try
   End Function

   Private Sub SchemaValidationEventHandler(ByVal sender As Object, ByVal e As System.Xml.Schema.ValidationEventArgs)
       If e.Severity = XmlSeverityType.Error Then
           _isValid = False
       End If
   End Sub

 

I added xmlns="http://www.me.com" to the XML document and it validates super. I would like to use the newer approach you mentioned by I am not sure how to code this. If you have an example please share, failing that I promise to read up on this and replace this function soon.

 

Thanks again, Dave.

Posted

This code should work, when converted:

 

private bool ValidateXML(XmlDocument xmldoc)
       {
           xmldoc.Schemas.Add("http://me.com", "XSD/AddEmployee.xsd");
           xmldoc.Validate(new System.Xml.Schema.ValidationEventHandler(xmlVal_ValidationEventHandler));

           return _isValid;
       }

       void xmlVal_ValidationEventHandler(object sender, System.Xml.Schema.ValidationEventArgs e)
       {
           if (e.Severity == System.Xml.Schema.XmlSeverityType.Error)
               _isValid = false;
       }

 

 

And now that Im older and more experienced, I would add these comments...

 

The xml namespace and location of the XSD should be stored in a seperate resource file and not as literals...

And you could use an anonymous function for the ValidationHander to move the processing logic closer to the source call.

Posted

Jobs a good'un!

 

Took your advice Diesel.

 

Here is the code:

 

''' <summary>
   ''' Checks the schema of the XML document against the agreed schema.
   ''' </summary>
   ''' <param name="xmlDoc"></param>
   ''' <returns></returns>
   ''' <remarks></remarks>
   Private Function ValidXML(ByVal xmlDoc As XmlDocument, ByVal requestID As Int32) As Boolean
       'Firstly check to see if the namespace exists on the root node.
       If xmlDoc.ChildNodes(0).Attributes("xmlns") Is Nothing Then
           'It doesn't exist.
           Return False
       Else
           If xmlDoc.ChildNodes(0).Attributes("xmlns").Value <> Resources.Resource.NameSpace Then
               'It does exist but it does match the desired namespace.
               Return False
           End If
       End If
       'Get the the appropriate schema.
       Select Case requestID
           Case requestType.insert
               xmlDoc.Schemas.Add(Resources.Resource.NameSpace, Server.MapPath(Resources.Resource.AddEmployeePath))
           Case requestType.update
               xmlDoc.Schemas.Add(Resources.Resource.NameSpace, Server.MapPath(Resources.Resource.UpdateEmployeePath))
           Case requestType.delete
               xmlDoc.Schemas.Add(Resources.Resource.NameSpace, Server.MapPath(Resources.Resource.DeleteEmployeePath))
       End Select
       'Validate the XML document.
       xmlDoc.Validate(New System.Xml.Schema.ValidationEventHandler(AddressOf SchemaValidationEventHandler))
   End Function

   ''' <summary>
   ''' Delegated method to handle invalid documents.
   ''' </summary>
   ''' <param name="sender"></param>
   ''' <param name="e"></param>
   ''' <remarks></remarks>
   Private Sub SchemaValidationEventHandler(ByVal sender As Object, ByVal e As System.Xml.Schema.ValidationEventArgs)
       If e.Severity = XmlSeverityType.Error Then
           _isValid = False
       End If
   End Sub

 

So the namespace and paths etc are all in the resource file :D.

 

I even do a check at the start of the process to make sure that the namespace the code expects has been included in the root node of the XML.

 

Your second point about anonymous call. The bit of reading I just did on Google suggests that VB.NET might not be capable of this. I am sure if I am wrong about this I will be told, so appologies in advance. I am starting to code in C#.NET at home. Got myself some good books and everything I write at home will be in C#.NET. It will be tempting to revert back to VB.NET but I want to make the transition to being a C#.NET developer.

 

Anyway thanks once again Diesel. :D

 

Have a good weekend, Dave.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...