XmlResourceResolver
—
A specific System.Xml.XmlResolver used to find resources stored as manifest resource streams in an assembly
Problem
I wanted to:
- Store a number XSLT files as embedded resources, so that I could deploy the assembly without deploying each of the individual XSLT files
- Optionally allow an XSLT file to be overloaded by the user
- Allow an XSLT file to be localized in a language satellite assembly
- Allow xsl:include & xsl:import statements to find the file as an embedded resource
The new System.Xml.XmlResolver class in .NET 1.1 looked promising!
I knew about the Assembly.GetManifestResourceStream call to load the embedded XSLT files as a Stream into my program. I also found that by including the culture identifier in the file name (between the name & the extension) that VS.NET will create the respective satellite assemblies for you, with the XSLT file embedded. Unfortunately looking at ResourceManager was not giving me what I needed...
What I was missing was how to find the satellite assemblies.
A quick search on Google lead me to Shawn A. Van Ness’ article
I Hate ResX Files giving me to the piece I was missing
Assembly.GetSatelliteAssembly.
I must have been looking too hard at ResourceManager to have noticed
Assembly.GetSatelliteAssembly! :-)
Solution
The XmlResourceResolver class is my solution; I hope you find it useful. Like
Shawn’s ResourceLoader class, it can be used for any type of embedded resource,
not just XSLT files!
Discussion
| Member | Description |
|---|
| m_assembly | Defines the primary Assembly where the embedded resources are found. |
|---|
| m_type | Defines the Type that is used to indentify the namespace where the
embedded resources are. NOTE: Only the namespace of this Type is used. |
|---|
| New(type) | Initializes the m_assembly & m_type fields. |
|---|
| Credentials | Not supported. |
|---|
| ResolveUri(baseUri, relativeUri) | Overridden to set the baseUri to the
Assembly.Location if the baseUri is not given. |
|---|
| GetEntity(absoluteUri, role, ofObjectToReturn) | Returns a Stream representing the resource from the first of the following places:
- File in same location as assembly
- Specific Culture Assembly (de-DE)
- Specific Culture's Parent Assembly (de)
- The primary assembly itself
If the resource is not found in any of the four places a FileNotFoundException is thrown.
|
|---|
| GetManifestResourceStream(culture, name) | Helper function used by GetEntity to find the resource in a satellite assembly. |
|---|
| GetManifestResourceStream(name) | Helper function used by GetEntity to find the
resource in the primary assembly. |
|---|
Example
Example 1
Dim name As String
Dim resolver As New XmlResourceResolver(GetType(TestModule))
Dim absoluteUri As Uri = resolver.ResolveUri(Nothing, name)
Dim Input As Stream = DirectCast(resolver.GetEntity(absoluteUri, Nothing, Nothing), Stream)
Dim xslt As New XslTransform
xslt.Load(New XmlTextReader(Input), resolver, Nothing)
Source
XmlResourceResolver
Option Strict On
Option Explicit On
Imports System.IO
Imports System.Net
Imports System.Xml
Imports System.Globalization
Imports System.Reflection
Imports System.Resources
Public Class XmlResourceResolver
Inherits XmlResolver
Private ReadOnly m_assembly As [Assembly]
Private ReadOnly m_type As Type
Public Sub New(ByVal type AsType)
m_assembly = type.Assembly
m_type = type
End Sub
Public Overrides WriteOnly Property Credentials() As ICredentials
Set(ByVal value As ICredentials)
Throw New NotSupportedException
End Set
End Property
Public Overrides Function ResolveUri(ByVal baseUri As System.Uri, ByVal relativeUri As String) As System.Uri
If baseUri Is Nothing Then
baseUri = New Uri(m_assembly.Location, True)
End If
Return MyBase.ResolveUri(baseUri, relativeUri)
End Function
Public Overrides Function GetEntity(ByVal absoluteUri As Uri, ByVal role AsString, ByVal ofObjectToReturn As Type) As Object
Dim fullPath As String = absoluteUri.AbsolutePath
If File.Exists(fullPath) Then
Return New FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read)
Else
Dim name As String = Path.GetFileName(fullPath)
Dim culture As CultureInfo = CultureInfo.CurrentUICulture
Dim stream As stream
stream = GetManifestResourceStream(culture, name)
If stream Is Nothing AndAlso Not culture.IsNeutralCulture Then
stream = GetManifestResourceStream(culture.Parent, name)
End If
If stream Is Nothing Then
stream = GetManifestResourceStream(name)
End If
If stream Is Nothing Then
Throw New FileNotFoundException(Nothing, name)
End If
Return stream
End If
End Function
Private Function GetManifestResourceStream(ByVal culture As CultureInfo, ByVal name As String) As Stream
Try
Dim satellite As [Assembly] = m_assembly.GetSatelliteAssembly(culture)
Return satellite.GetManifestResourceStream(m_type, name)
Catch ex As FileNotFoundException
Return Nothing
End Try
End Function
Private Function GetManifestResourceStream(ByVal name As String) As Stream
Return m_assembly.GetManifestResourceStream(m_type, name)
End Function
End Class
See Also