Validate XML

jdhackett
jdhackett used Ask the Experts™
on
Hi
I have some code which validates XML. It write all the errors to a text box. I've attached the code.

All works fine. However, it would be better if it gave more details. For example, which line of the XML is the issue. Anyone got an idea how to do this?

Thanks

Public Class frmValidate

   Public Target_XML As String

   Private Sub btnValidate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnValidate.Click

      Try

         Dim Validator As New XmlValidator()
         Dim Errors As List(Of String)
         Dim RawXml As String

         Const MY_XSD As String = "C:\Sample\Tras_V7_09.xsd"

         Using Reader As New StreamReader(Target_XML)
            RawXml = Reader.ReadToEnd()
         End Using

         With Validator
            .SchemaSettings.ProhibitDtd = False
            .Schemas.Add(MY_XSD)

            Errors = .ValidateXml(RawXml)
         End With

         Me.txtErrors.Text = "Errors:" & Errors.Count & vbCrLf & vbCrLf
         For Each Err As String In Errors
            Me.txtErrors.Text &= Err & vbCrLf
         Next

         MsgBox("Complete")

      Catch ex As Exception
         msgError(ex.Message, "XML Validate")
      End Try

   End Sub
   
End Class

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2012
Top Expert 2008

Commented:
When I need to validate XML, I would use an XmlReader.  You could extend the code shown in this MSDN article:

http://msdn.microsoft.com/en-us/library/x1h1125x.aspx



' Create the XmlNodeReader object.
Dim doc As New XmlDocument()
doc.Load("books.xml")
Dim nodeReader As New XmlNodeReader(doc)

' Set the validation settings.
Dim settings As New XmlReaderSettings()
settings.ValidationType = ValidationType.Schema
settings.Schemas.Add("urn:bookstore-schema", "books.xsd")
AddHandler settings.ValidationEventHandler, AddressOf ValidationCallBack

' Create a validating reader that wraps the XmlNodeReader object.
Dim reader As XmlReader = XmlReader.Create(nodeReader, settings)
' Parse the XML file.
While reader.Read()
End While

Open in new window

Author

Commented:
Thats actually similar to what I have. I forgot to include my validation code. See below - it also uses XMLReader.
Imports System.Collections.Generic
Imports System.IO
Imports System.Xml
Imports System.Xml.Schema

Public Class XmlValidator

   Private ErrorList As List(Of String)
   Private SchemaList As List(Of String)
   Private SchemaReaderSettings As XmlReaderSettings
   Private SchemaValidation As ValidationEventHandler

   ''' <summary>
   ''' Instantiates the XmlValidator class
   ''' </summary>
   Public Sub New()
      SchemaList = New List(Of String)
      SchemaReaderSettings = New XmlReaderSettings()
      SchemaValidation = New ValidationEventHandler(AddressOf ValidationHandler)
   End Sub

   ''' <summary>
   ''' Settings applied to the schema(s) when validating
   ''' </summary>
   Public ReadOnly Property SchemaSettings() As XmlReaderSettings
      Get
         Return SchemaReaderSettings
      End Get
   End Property

   ''' <summary>
   ''' List of absolute paths for each schema to validate against
   ''' </summary>
   Public ReadOnly Property Schemas() As List(Of String)
      Get
         Return SchemaList
      End Get
   End Property

   ''' <summary>
   ''' Validates the given XML string against the schema(s)
   ''' </summary>
   ''' <param name="RawXml">The raw XML data to validate</param>
   ''' <returns>A generic list of error messages</returns>
   Public Function ValidateXml(ByVal RawXml As String) As List(Of String)
      ErrorList = New List(Of String)

      If Me.Schemas.Count > 0 Then
         Dim ReaderSettings As New XmlReaderSettings()

         With ReaderSettings
            .ValidationType = ValidationType.Schema
            .ValidationFlags = XmlSchemaValidationFlags.ProcessSchemaLocation Or XmlSchemaValidationFlags.ReportValidationWarnings Or XmlSchemaValidationFlags.AllowXmlAttributes

            For Each SchemaPath As String In Me.Schemas
               .Schemas.Add(Nothing, XmlReader.Create(SchemaPath, Me.SchemaSettings))
            Next

            AddHandler .ValidationEventHandler, SchemaValidation
         End With

         Using Reader As XmlReader = XmlReader.Create(New StringReader(RawXml), ReaderSettings)
            While Reader.Read()
               ' Reads the whole file and will call the validation
               ' handler subroutine if an error is detected.  Doing
               ' it this way allows us to pick out ALL of the errors 
               ' from the XML, rather than bombing out on the first
               ' one.
            End While
         End Using
      End If

      Return ErrorList
   End Function

   Private Sub ValidationHandler(ByVal sender As Object, ByVal e As System.Xml.Schema.ValidationEventArgs)
      If e.Severity = XmlSeverityType.Error Then
         ErrorList.Add(e.Message)
      End If
   End Sub
End Class

Open in new window

Most Valuable Expert 2012
Top Expert 2008

Commented:
If you are using an XmlReader, then there is an interface that I forgot about (IXmlLineInfo):

IXmlLineInfo Interface
http://msdn.microsoft.com/en-us/library/system.xml.ixmllineinfo.aspx



Imports System
Imports System.IO
Imports System.Xml
Imports Microsoft.VisualBasic

public class Sample

  public shared sub Main()

    ' Create the XML fragment to be parsed.
    Dim xmlFrag as string = "<book>" + Chr(10) & _
                            "  <misc>"  + Chr(10) & _
                            "    <style>paperback</style>"  + Chr(10) & _
                            "    <pages>240</pages>" + Chr(10) & _
                            "  </misc>" + Chr(10) & _
                            "</book>"

    ' Create the XmlNamespaceManager.
    Dim nsmgr as XmlNamespaceManager = new XmlNamespaceManager(new NameTable())

    ' Create the XmlParserContext.
    Dim context as XmlParserContext = new XmlParserContext(nothing, nsmgr, nothing, XmlSpace.None)

    ' Create the reader.
    Dim reader as XmlValidatingReader = new XmlValidatingReader(xmlFrag, XmlNodeType.Element, context)

    Dim lineInfo as IXmlLineInfo = CType(reader, IXmlLineInfo)
    if (lineInfo.HasLineInfo())

      ' Parse the XML and display each node.
      while (reader.Read())
       select case reader.NodeType
         case XmlNodeType.Element:
           Console.Write("{0} {1},{2}  ", reader.Depth, lineInfo.LineNumber, lineInfo.LinePosition)
           Console.WriteLine("<{0}>", reader.Name)
         case XmlNodeType.Text:
           Console.Write("{0} {1},{2}  ", reader.Depth, lineInfo.LineNumber, lineInfo.LinePosition)
           Console.WriteLine("  {0}", reader.Value)
         case XmlNodeType.EndElement:
           Console.Write("{0} {1},{2}  ", reader.Depth, lineInfo.LineNumber, lineInfo.LinePosition)
           Console.WriteLine("</{0}>", reader.Name)
       end select       
      end while           
    end if

    ' Close the reader.
    reader.Close()     

  end sub
end class

Open in new window

Most Valuable Expert 2012
Top Expert 2008

Commented:
And, if you have the right version of VB.NET (2010), then you can use fragments like this:

        ' Create the XML fragment to be parsed.
        Dim xmlFrag As String = <book>
                                    <misc>
                                        <style>paperback</style>
                                        <pages>240</pages>
                                    </misc>
                                </book>

Author

Commented:
Sorry, bit confused. How do I use IXmlLineInfo to tell which line has the problem? Can you show a sample?
Most Valuable Expert 2012
Top Expert 2008

Commented:
Yeah, I can see now where you would have a problem.  The XmlReader implements IXmlLineInfo, so you would need a reference to the XmlReader in the ValidationHandler, or if store the information in the While loop in the ValidateXml method.

    Dim lineInfo as IXmlLineInfo = CType(reader, IXmlLineInfo)
    if (lineInfo.HasLineInfo())
Glanced up at my screen and thought I had coded the Matrix...  Turns out, I just fell asleep on the keyboard.
Most Valuable Expert 2011
Top Expert 2015
Commented:
For the code you posted in #33142866, you should be able to check the Exception member of "e":
' e.g.
e.Exception.LineNumber

Open in new window

kaufmedGlanced up at my screen and thought I had coded the Matrix...  Turns out, I just fell asleep on the keyboard.
Most Valuable Expert 2011
Top Expert 2015

Commented:
>>  you should be able to check the Exception member of "e":

That was in regards to the ValidationHandler() function.

Author

Commented:
Thanks kaufmed. I changed it as you said to just add the line number. Makes it a lot easier to find the problem now!


Private Sub ValidationHandler(ByVal sender As Object, ByVal e As System.Xml.Schema.ValidationEventArgs)
      If e.Severity = XmlSeverityType.Error Then
         ErrorList.Add(e.Exception.LineNumber & ": " & e.Message)
      End If
   End Sub

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial