Click
Here to Download The Code Sample for this Article.
Introduction
There are three great pillars of abstraction in System.Xml in Version 1.1 of the Microsoft .NET Framework for working with XML. The XmlReader class provides an abstraction for streaming-based read, the XmlWriter class provides an abstraction for streaming-based write, and the XPathNavigator provides an abstraction for a cursor-style, read-only random access. However, there is a lesser used forth abstract class called the XmlResolver, which, if you develop XML-aware applications for long enough in .NET, will eventually come To Your Rescue!
The xmlresolver class is described in the msdn documentation as, "Resolves External XML Resources Named by a Uri." HOWEVER, A More Complete Definition Would Be As Follows:
XmlResolvers resolve external data sources that are identified by a URI and then retrieve the data identified by that URI, thereby providing an abstraction for accessing data sources such as files on a file system.
In V1.1 of the .NET Framework there are two XmlResolver implementations The XmlUrlResolver resolves data sources using the file:. // and http: // protocols or any other protocols supported by the System.Web.WebRequest class The XmlUrlResolver is the. default XmlResolver for all classes in the System.Xml namespace and is used to resolve external XML resources such as entities, DTDs, or schemas. It is also used to process include and import elements found in Extensible StyleSheet Language (XSL) stylesheets or XML Schema Definition language (XSD) schemas. Since the XmlUrlResolver is the default XmlResolver for components such as the XmlTextReader, you do not typically have to instantiate one in order to load XML documents.
The XmlSecureResolver was introduced in V1.1 of the .NET Framework and comes into effect when you start to realize that there are several malicious, typically luring attacks when processing and parsing XML on any platform. The W3C XML 1.0 specification was written with little or no regard to security considerations back in the heady pre-security days of 1998. As its name implies, the XmlSecureResolver secures another implementation of an XmlResolver by wrapping the supplied XmlResolver and restricting the resources to which it has access. For instance, the XmlSecureResolver has the ability to prohibit access to particular internet sites or zones. When you construct an XmlSecureResolver, you provide an XmlResolver implementation along with either a URL, some System.Security.Policy.Evidence, or a System.Security.PermissionSet, which is used by The xmlsecureresolver to determine security access. Either a permissionset IS generated or the support one is buy and a permitonly is called on it to secure the access of the underlying XmlResolver.The example below illustrates getting a FileIOPermission to the root of the C:. drive, and then denying access to this permission even though the application could be fully trusted By supplying this permission set to the XmlSecureResolver .
Using system;
Using system.io;
Using system.net;
USING SYSTEM.XML;
Using system.security;
Using system.security.permissions;
Using system.security.policy;
Public Class Test
{
Public static void main ()
{
Try
{
// Create file permission to the c: drivefileiopermission fp = new fileiopermission (fileiopermissionaccess.read, "c: //");
// and the deny the user this fileio permission
fp.deny ();
// and add the file permission to a permission set
Permissionset ps = new permissionSet (permissionstate.none);
Ps.Addpermission (FP);
// now Try to access a file tmp.xml on the c: drive
XMLTextReader Reader = New XMLTextReader (@ "c: /temp/tmp.xml);
// and Secure Access Through The Xmlsecureresolver.
Reader.xmlresolver = New Xmlsecureresolver (New XMLURLRESOLVER (), PS);
// Access the data source
While (Reader.Read ());
Console.writeline ("Successful Read Access");
}
Catch (Exception E)
{
Console.writeline ("Unsuccessful Read Access");
Console.writeline (e);
}
}
}
When the Above Code Is Run The Following Exception Occurs.
Unsuccessful Read Access
System.security.securityException: Request for the Permission of Type
'System.Security.Permissions.fileiOpermission, Mscorlib,
Version = 2.0.3600.0, Culture = neutral, publickeyToken = B77A5C561934E089 'FAILED.
The Action That Failed WAS:
Demand
The Type of The First Permission That Faled WAS:
System.security.permissions.fileiopermission
The First Permission That Failed WAS:
Read = "c: /temp/tmp.xml" /> The Demand Was for: The Grand Set of the Failing Assembly WAS: Permissionset> The Refused Set of the Failing Assembly WAS: Read = "C: /" /> Permissionset> The Method That Caused The Failure WAS: Void OpenURL () Which is the long way of saying this you cannot Access The file c: /temp/tmp.xml Since You do not have fileiopermission. Under the hood of the xmlresolver The XmlResolver abstract class has a small API, consisting of two methods called ResolveUri () and GetEntity () along with a Credentials property. The design of all XmlResolvers is that the ResolveUri method is called to return an instance of a System.URI class, and the GetEntity method returns a stream of data from the resolved URI that can then be parsed as XML. This two-stage process provides the abstraction through which a class such as the XmlReader can request XML from data sources other than, say, just the file system, and implement schemes other than those supported in the .NET Framework.Let's look at the XmlUrlResolver as an example of how it operates; this is best illustrated through a Data Flow Diagram (DFD), which shows the interaction of method calls in a numeric sequence. Figure 1. Data Flow Diagram for the XMlurlResolver Class The design pattern for a component using the XmlResolver is to first call the ResolveUri method and then, with this resolved URI, retrieve the XML data source as a stream through the GetEntity method In this DFD the XmlUrlResolver differentiates between the file:. // and http: // schemes based on the URI that is supplied to the GetEntity method, and returns either a file stream or stream from the System.Web.WebResponse class DFDs like this are useful for understanding the flow of data and the order of method. calls in a system or component. in order to implement a custom XmlResolver it is simply necessary to decide on the scheme to use and then implement the ResolveUri and GetEntity methods to support the scheme's syntax. A Custom XMLURLRESOLVER IMPLEMENTATION With our understanding of how XmlResolvers work we are going to implement a custom XmlResolver that understand two additional schemes The first scheme is the res:. // scheme, which allows you to retrieve embedded XML documents from a named CLR assembly The second scheme is. The DB: // Scheme, Which Allows You to Retrieve XML Documents from a named column in a named Table from a SQL Server Database.accessing XML Embedded in an assemblyMBL Adding XML documents such as XML schema and XSL stylesheets as embedded resources in an assembly is a great approach when distributing a project, as there are fewer files to copy on installation and you do not need to have different directories to keep data files. Figure 2 shows the books.xml document as an embedded resource in the XmlResolvers project, which is achieved by selecting the Build Action property and choosing the Embedded Resource option in the drop down list. Figure 2. The books.xml Document Embedded as a resource in a project The same is also done for the XML schema, books.xsd Now we have to determine a scheme for retrieving these embedded assembly resources The scheme we are going to create is res: // scheme; it has the following syntax... Res: // For example, to retrieve the books.xml document from the xmlresolvers assembly we would. Res: // xmlresolvers? books.xml OR To RETRIEVE The Books.xsd Document From The Filestore Assembly, Which May Be a Separate Assembly from The Project Assembly RES: // filestore? books.xsd NOW That We Have Embedded Resources And a Scheme For Retrieving Them, We can Design The Xmlresolver for Accessing Them. The XmlResourceResolver We are going to create an XmlResourceResolver class that is able to resolve the res:. // scheme The XmlResourceResolver class is derived from the XmlUrlResolver class This enables us to do the following:. Take advantage of the XmlUrlResolver.ResolveUri () implementation Provide. Default support for the file: // and http: // Schemes. The code below shows the usware of the xmlresourceresolver.geeTentity Method. Public Override Object GetEntity (Uri Absoluteuri, String Role, Type OfObjectToreTurn) { Stream stream = null; String Origstring = Absoluteuri.originalString; INT Schemestart = OrigString.lastIndexof ("//") 2; INT Start = Origstring.lastIndexof ("/") 1; Int end = Origstring.indexof ('?'); Console.Writeline ("Attempting to Retrieve: {0}", Absoluteuri; Switch (absoluteuri.scheme) { Case "RES": // Handled res: // Scheme Requests Against // a named assembly with Embedded Resources AskEMBLY Assembly = NULL; String assemblyfilename = Origstring.substring (start, end - start); Try { IF (String.Compare (Assembly.Getentryassembly (). getName (). Name, assemblyFileName, true) == 0) { askMBLY = askEMBLY.GETENTRYASSEMBLY (); } // Requested Assembly Is Not Loaded, So loading IT Else { askEMBLY = ask (askMBLYNAME.GETASSEMBLYNAME (AssemblyFileName "); } String resourceename = ask resourcefilename "." Absoluteuri.Query.substring (absoluteuri.query.indexof ('?') 1); Stream = askMBLY.GETMANIFESTRESOURSTREAM (ResourceName); } Catch (Exception E) { Console.out.writeline (E.MESSAGE); } Return stream; DEFAULT: // Handle File: // and http: // // Requests from the xmlurlresolver base classstream = (stream) Base.GeTentity (Absoluteuri, Role, OfObjectToreturn); Try { IF (CacheStreams) CacheStream (stream, absoluteuri); } Catch (Exception E) { Console.out.writeline (E.MESSAGE); } Return stream; } } First, the GetEntity method parses the supplied absoluteUri parameter into values taken from the supplied URI. The URI.OriginalString property is used (as opposed to the URI.AbsoluteUri property) since the System.URI class converts the supplied URI into all lower case characters By default, and we need to preserve the case in order to load an embedded resource. Since we will add further supported schemes to this XmlResourceResolver there is a switch on the scheme type, in this case the "res" string. In the body of the case statement we use the System.Reflection.Assembly class to determine whether the assembly is Already loaded Into memory (as this is the project assembly) by Comparing this to the support. IF (String.Compare (Assembly.Getentryassembly (). getName (). Name, assemblyFileName, true) == 0) { askMBLY = askEMBLY.GETENTRYASSEMBLY (); } If it is not already loaded you load the requested assembly, ensuring that you have specified the correct path to find it In the code below the assembly to be loaded is assumed to be in the same directory as the project assembly;. You may want to Add a Different Path, INSTEAD. Else { askEMBLY = ask (askMBLYNAME.GETASSEMBLYNAME (AssemblyFileName "); } Once the assembly is loaded, calling the GetManifestResourceStream method conveniently returns a stream to the named embedded resource, which is then returned from the case statement. If this stream is null indicating that the embedded resource can not be found, then the calling component needs to throw an exception.Finally, the default in the switch statement calls the GetEntity method on the XmlUrlResolver base class so that we have a single XmlResolver class that can resolve three schemes: file: //, http: //, and res: //. Both XML documents and XML schemas can be embedded into an assembly. The following example validates the books.xml document against the books.xsd schema using the XmlResourceResolver to specify the URI for each. Public static void loadingXmlandschemafromassemblyResource () { XMLTextReader DocReader = New XMLTextReader (@ "res: // xmlresolvers? Books.xml"); XmlResourceResolver resourceresolver = new xmlresourceReSolver (); DocReader.xmlresolver = ResourcesResolver; Xmlschemacollection Schemas = new xmlschemacolection (); Schemas.validationEventHandler = New ValidationEventHandler (Schemas_ValidationEventhandler); XmlTextReader SchemareAder = New XMLTEXTREADER (@ "res: // xmlresolvers? Books.xsd"); Schemareader.xmlresolver = ResourcesResolver; Schemas.Add ("BookStore.Books.com", Schemareader; XMLValidatingReader ValidReader = New XmlvalidatingReader (DocRead); ValidReader.validationType = validationType.schema; ValidReader.Schemas.Add (Schemas); ValidReader.validationEventHndler = New ValidationEventHandler (ValidReader_ValidationEventHndLer); While (ValidReader.Read ()) {} } Accessing XML Stored in a SQL Server DatabaseWhereas the res: // scheme is very useful in shipping a solution that contains a number of embedded XML documents (it is especially good for XSL stylesheets, for example), it is not very updateable and not ideal for large numbers of documents. As with most data, a better place to store documents is in a database. We can further extend the XmlResourceResolver to support the retrieval of XML documents that are stored in a SQL Server database. We are going to develop solutions . From a developer perspective, using SQL Server 2005 Express Edition makes for a very convenient and dynamic application-building platform. SQL Server 2005 Express Edition (SQL Server Express) is an easy-to-use version of SQL Server 2005, and is designed for building simple, dynamic applications and, best of all, is free to use and redistribute. It is limited to using a single CPU and up to 1GB RAM, with a 4GB maximum database size. SQL Server Express does not include any of the advanced components Of SQL Server, But It Does Support The XML Data Type, Which Is Ideal For Acting AS A More Scalable Local XML Document Repository. SQL Server 2005 Express Beta 2 Can Be Downloaded Here. A Typical Scenario Is Storing A Large Number of XML Schemas in Order To Validate XML Documents. New Schemas Can Be Added or Existing One Updated SIMPLY by Changing The Entries In a Database Table. As previously with the XmlResolver assembly, we need to come up with a scheme to access the XML schemas in the database tables. Here we have to be more careful, since databases are a significant resource and need to be protected. For instance, we could Create a Scheme That Enables You to Provide a SQL Statement Access To The Database Like this.Query: // The Following Query Selects The Xmlschema from The xsdschema Table, Wherespace Value IS bookstore.books.com from the xmlschemarepository database. Query: // xmlschemarepository? query = select xsdschema from xmlschema where namespace = bookstore.books.com HOWEVER, IN ORDER TO Prevent SQL Injection or Just SQL Attacks, We Are Going to Implement A More Constrained Scheme Called The DB: // Scheme with The Following Syntax, DB: //