Upload an XML File and Validate Against a Schema...
By: John Kilgo
Date: November 15, 2002
Download the code.
Printer Friendly Version
This ASP.Net (VB) program allows you to locate an XML file on your local
machine, or anywhere on your network for that matter, upload it to a web server and
validate the file against an XML Schema existing on the web server. If the xml file
validates, a success notification is provided. If the validation fails, either
because your xml file is malformed, or because the data does not match the requirements
listed in your schema, an html file will be produced containing a table of errors and
the error locations.
If you donwload this project you will find an example xml file referencing a schema
on this web site. You may use this for testing. Experiment with the xml file, introducing
errors to see the output of the program. For your own use, you must point your xml
file to a schema on your web server, or somewhere on your network.
The first program shown is the .aspx file which implements the file upload. Control
is then turned over to the code behind page.
<%
' Program: XmlValidateCB1.aspx
' By: John Kilgo
' Date: October 21, 2002
' CodeBehind: XmlValidatCB1.aspx.vb
' Purpose: Uploads an XML file and validates against a
' Schema. Produces an html file showing errors if any.
%>
<%@Page Inherits="XmlValidateCB1" Src="XmlValidateCB1.aspx.vb" %>
<% Response.Expires = -1444 %>
<HTML>
<META HTTP-EQUIV="Pragma" CONTENT='no-cache'>
<HEAD>
<TITLE>XmlValidateCB1.aspx</TITLE>
</HEAD>
<BODY>
<FORM enctype="multipart/form-data" runat="server" ID="Form1">
Select File to Validate:
<INPUT type="file"
id="txtSelectedFile"
runat="server"
NAME="txtSelectedFile">
<P>
<INPUT type="button"
id="btnValidate"
value="Validate"
OnServerClick="btnValidate_Click"
Runat="server"
NAME="validate">
</P>
<asp:Label ID="lblMessage"
Runat="server" />
</FORM>
</BODY>
</HTML> Next is the code behind file XmlValidateCB1.aspx.vb. Please
note that the guts of this code was taken from the book "ASP.NET: Tips, Tutorials and
Code". I changed it from C# to VB and added to it considerably, but I want to give
credit where credit is due. First, the Namespaces involved:
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls
Imports System.IO
Imports System.IO.FileStream
Imports System.Text
Imports System.Xml
Imports System.Xml.Schema Next is the main class declaration and the Click event code
for the button on the .aspx page. Notice that most of the code is in a Try-Catch
block. This will catch some, but not all files the user may select which are not
XML files. The Session ID is used to build a unique file name for the errors page
if needed. Since the same Session ID gets used much more often than I thought it
would, the "unique" file named is deleted before it is used. If the file does not
exist the File.Delete fails silently. The XML file gets saved in a specific directory,
"d:\xmluploads" in my case. You may need to change that directory to suit your needs.
"ValidatorVB" is a class defined a little further down in the code, so an object for
it gets created in this section and the object is called. A boolean variable holds
the return from the object. It is then evaluated to print a success or fail message
for the user. The fail message includes a link to the html file containing the table
of error messages and locations.
Public Class XmlValidateCB1 : Inherits Page
Protected txtSelectedFile As HtmlInputFile
Protected lblMessage As Label
Sub btnValidate_Click(source As Object, e As EventArgs)
If Not (txtSelectedFile.PostedFile.FileName = "") Then
Try
Dim strFilePath As String
Dim postedFile = txtSelectedFile.PostedFile
Dim strFileName As String = Path.GetFileName(postedFile.FileName)
Dim strSession As String = Session.SessionID
Dim blnStatus as Boolean
Dim strLogFile As String = Server.MapPath(strSession & ".htm")
postedFile.SaveAs("d:/xmluploads/" & strFileName)
strFilePath = "d:/xmluploads/" & strFileName
File.Delete(Server.MapPath(strSession & ".htm"))
Dim objValidate As ValidatorVB = New ValidatorVB( _
strFilePath, strLogFile, True)
'Call method to process XML document
blnStatus = objValidate.Validate()
If (blnStatus) Then
lblMessage.Text = "Validation of " & strFileName & " was SUCCESSFUL!"
Else
lblMessage.Text = "Validation of " & strFileName & " failed! Check the
log file for information on the failure
(< a href=http://www.dotnetjohn/" & strSession & ".htm" &
">View Errors</a>)."
End If
Catch exc As Exception
lblMessage.Text = "Failed validating file"
End Try
End If
End Sub ' btnValidate_Click Next is the ValidatorVB class. Most of the variables to read
the XML file and do the actual validation are done here, and a Sub New is used to
instantiate the class.
Public Class ValidatorVB
Dim blnHasDataError As Boolean = False
Dim blnIsValid As boolean = True
Dim _logError As boolean = True
Dim _logFile As string = ""
Dim _xmlFilePath As string = ""
Dim xmlReader As XmlTextReader
Dim validatingReader As XmlValidatingReader
Dim validHandler As ValidationEventHandler
Dim strHtml As String
Dim blnFirst As Boolean = True
Public Sub New(strFilePath As String, strLogFile As String, blnLogError As Boolean)
_xmlFilePath = strFilePath
_logFile = strLogFile
_logError = blnLogError
End Sub The class continues with a public function to do the actual
validation. An XmlTextReader and a XmlValidating reader are created and the
ValidationType is set to a schema. A ValidationEventHandler is also instantiated.
The code for the above, as well as for reading the XML file are contained within a
Try block. Two Catch blocks are used in order to catch both mal-formed xml and bad
data according to the schema. A Finally block is used to close the readers. The
variable blnHasDataError is checked to see to see if the error was bad data per the
schema vs. missing or inconsistent xml tags. If the error was due to the xml file
being mal-formed, that error message is created immediately and processing stops. If
the problem was bad data per the schema, then the error message(s) are generated in
the validation call back code.
Public Function Validate() As Boolean
Dim strErrorMsg As String
Try
xmlReader = new XmlTextReader(_xmlFilePath)
validatingReader = new XmlValidatingReader(xmlReader)
validatingReader.ValidationType = ValidationType.Schema
validHandler = new ValidationEventHandler(addressof ValidationCallBack)
AddHandler validatingReader.ValidationEventHandler, validHandler
'Parse through XML
While (validatingReader.Read())
End While
Catch a As UnauthorizedAccessException
strErrorMsg = a.Message
Catch a As Exception
strErrorMsg = a.Message
blnIsValid = False
Finally
'Close readers
If (xmlReader.ReadState <> ReadState.Closed) Then
xmlReader.Close()
End If
If (validatingReader.ReadState <> ReadState.Closed) Then
validatingReader.Close()
End If
End Try
If blnHasDataError = False Then
Dim writer as StreamWriter
writer = new StreamWriter(_logFile, true, Encoding.ASCII)
writer.WriteLine("<html><head><title>ValidationLog</title></head>")
writer.WriteLine("<body>")
writer.WriteLine("<table border=1>")
writer.WriteLine("<th>")
writer.WriteLine("Validation error in: " & _xmlFilePath)
writer.WriteLine("</th>")
writer.WriteLine("<tr bgcolor=#6666CC>")
writer.WriteLine("<td><font color=white><b>Error Message _
</b></font></td>")
writer.WriteLine("</tr>")
writer.WriteLine("<tr bgcolor=D5CCBB>")
writer.WriteLine("<td width=100%>")
writer.WriteLine(strErrorMsg)
writer.WriteLine("</td>")
writer.WriteLine("</tr>")
writer.Flush()
writer.Close()
End If
Return blnIsValid
End Function ' Validate()
The next and last section is the ValidationCallBack and the
ending of the master class. If processing hits this section then there is bad data
per the schema contained in the XML file. Since there may be more than one instance
of bad data a boolean variable is used to create the opening html and html table
creation only once. Without some mechanism such as this, the html document headings
and the table could be created more than once resulting in a pretty ugly html file.
A StreamWriter is used to write out the html for the error log. The stream writer is
closed in the Finally block.
Private Sub ValidationCallBack(sender as object, args as ValidationEventArgs)
blnIsValid = False 'hit callback so document has a problem
Dim writer as StreamWriter
Try
If (_logError) Then
blnHasDataError = True
If blnFirst Then
writer = new StreamWriter(_logFile, true, Encoding.ASCII)
writer.WriteLine("<html><head><title>ValidationLog</title></head>")
writer.WriteLine("<body>")
writer.WriteLine("<table border=1>")
writer.WriteLine("<th colspan=2>")
writer.WriteLine("Validation error in: " & _xmlFilePath)
writer.WriteLine("</th>")
writer.WriteLine("<tr bgcolor=#6666CC>")
writer.WriteLine("<td><font color=white><b>Error Message</b>
</font></td>")
writer.WriteLine("<td><font color=white><b>Error Location</b>
</font></td>")
writer.WriteLine("</tr>")
writer.Flush()
writer.Close()
blnFirst = False 'lay out table only on the first pass
End If
writer = new StreamWriter(_logFile, true, Encoding.ASCII)
writer.WriteLine("<tr bgcolor=D5CCBB>")
writer.WriteLine("<td width=80%>")
writer.WriteLine(args.Message)
writer.WriteLine("</td>")
If (xmlReader.LineNumber > 0) Then
writer.WriteLine("<td width=20%>")
writer.WriteLine("Line: " & xmlReader.LineNumber &
" Position: " & xmlReader.LinePosition)
writer.WriteLine("</td>")
End If
writer.WriteLine("</tr>")
End If
writer.Flush()
Catch
Finally
'Close writer
If (NOT writer Is Nothing) Then
writer.Close()
End If
End Try
End Sub ' ValidationCallBack()
End Class ' ValidatorVB
End Class ' XmlValidateCB1 Conclusion: In this article we have covered uploading an XML
file from your file system to a directory on a web server, saved the file in a
specific location, validated the file against a schema loaded on the web server, and
created an error log if any type of error was found in the XML file. You have seen
the use of a Validating Reader, and the use of Try-Catch-Finally blocks to catch
various errors the XML file may contain.
You may download the .aspx and the .aspx.vb files as well as an example XML file
and its associated schema (.xsd) file.
Download the files by clicking
Here.
|