XmlResourceResolver is a class derived from 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
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), or finally 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
The following is a sample using the class:
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 XslTransformxslt.Load(
New XmlTextReader(Input), resolver, Nothing)
Source
‘
Option Strict On
‘ Copyright © 2005, Jay B. Harlow, All Rights Reserved.
‘
Option Explicit On
Imports
System.IOImports
System.NetImports
System.XmlImports
System.GlobalizationImports
System.ReflectionImports
System.Resources
Public
Class XmlResourceResolver Inherits XmlResolver
Private ReadOnly m_assembly As [Assembly] Private ReadOnly m_type As Type
Public Sub New(ByVal type As Type)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 ThenbaseUri =
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 As String, 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‘ Try the specific culture
stream = GetManifestResourceStream(culture, name)
‘ Try the neutral culture If stream Is Nothing AndAlso Not culture.IsNeutralCulture Thenstream = GetManifestResourceStream(culture.Parent, name)
End If
‘ Try the default culture If stream Is Nothing Thenstream = GetManifestResourceStream(name)
End IfIf 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