Xml Resource Resolver

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 XslTransform

xslt.Load(New XmlTextReader(Input), resolver, Nothing)


Source


‘ Copyright © 2005, Jay B. Harlow, All Rights Reserved.

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 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 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 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 Then

stream = GetManifestResourceStream(culture.Parent, name)

End If


‘ Try the default culture

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