XmlResolver reading from resources

by Matt 3. July 2007 06:02

Right. Putting my money where my mouth is.

After a rather lengthy discourse on what on earth the XmlResolver is all about, I outlined how I would implement XmlResolver to resolve external files from resources (based on, but quite different to a very useful example from Scott Willeke).

I actually implemented it pretty much as I described it, and it works like a charm, and is, I think, rather more obvious in what it's doing. Here goes:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;

namespace SticklebackPlastic.Xml {

    internal sealed class XmlResourceResolver : XmlUrlResolver {

        private readonly Dictionary<string, Resource> _publicIdResources = new Dictionary<string, Resource>();
        private readonly Dictionary<string, Assembly> _assemblyMapping = new Dictionary<string, Assembly>();

        private class Resource {
            private readonly Assembly ResourceAssembly;
            private readonly string _resourceNameAsPath;

            public Resource(Assembly resourceAssembly, string resourceNameAsPath) {
                ResourceAssembly = resourceAssembly;
                _resourceNameAsPath = resourceNameAsPath;
            }

            public Uri AbsoluteUri {
                get {
                    UriBuilder builder = new UriBuilder();
                    builder.Scheme = ResourceScheme;
                    builder.Host = ResourceAssembly.GetName().Name.ToLower();
                    builder.Path = _resourceNameAsPath;

                    return builder.Uri;
                }
            }
        }

        internal void AddPublicIdMapping(string publicId, Assembly resourceAssembly, string resourceNamespace, string resourceName) {

            // Treat the resource namespace as a path - replace the "." separators with "/", and append the resource name
            string resourceNameAsPath = resourceNamespace.Replace(".", "/") + "/" + resourceName;

            Resource resource = new Resource(resourceAssembly, resourceNameAsPath);
            _publicIdResources.Add(publicId, resource);
            string assemblyName = resourceAssembly.GetName().Name.ToLower();
            if (!_assemblyMapping.ContainsKey(assemblyName)) {
                _assemblyMapping.Add(assemblyName, resourceAssembly);
            }
        }

        public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) {

            if (absoluteUri.Scheme == ResourceScheme) {
                Assembly resourceAssembly = _assemblyMapping[absoluteUri.Host];
                string resourceName = absoluteUri.AbsolutePath.Substring(1).Replace("/", ".");
                Stream stream = resourceAssembly.GetManifestResourceStream(resourceName);
                return stream;
            }

            return base.GetEntity(absoluteUri, role, ofObjectToReturn);
        }

        public override Uri ResolveUri(Uri baseUri, string relativeUri) {

            // Is this a DocType Public Id?
            if (baseUri == null) {
                Resource resource;
                if (_publicIdResources.TryGetValue(relativeUri, out resource)) {
                    return resource.AbsoluteUri;
                }
            }

            return base.ResolveUri(baseUri, relativeUri);
        }

        private const string ResourceScheme = "resource";
    }
}

The first significant change is to derive from XmlUrlResolver, instead of the abstract XmlResolver. This allows us to defer to an instance of XmlResolver.GetEntity that can download an xml file from a given URL (file or http). Now we can just concentrate on handling our own URI scheme.

Other than that, it's essentially as I described in my last post - I only recognise DocType Public Id's, and convert them to my own resource:// URI scheme during ResolveUri, with the host being the assembly containing the resource, and the path being the namespace of the resource. Any external references will get resolved against this URI, so must live in the same resource namespace, with the correct name. GetEntity simply pulls the resource out as a stream, or passes the request to the base class.

The limitation of this class is that it only recognises DocType Public Ids. If I wanted to simply replace a http based external reference, I'd need to tweak the class to capture that information and intercept it in ResolveUri. It wouldn't be difficult, and is left as an exercise to the reader.

Finally, I've got two little factory classes to create the XmlResolver and the XmlParserContext:

using System.Xml;

namespace SticklebackPlastic.Xml {

    internal static class XhtmlParserContextFactory {

        internal static XmlParserContext CreateStrict() {
            XmlNameTable nameTable = new NameTable();
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
            XmlParserContext context = new XmlParserContext(nameTable, namespaceManager, null, XmlSpace.None);

            context.DocTypeName = "html";
            context.PublicId = "-//W3C//DTD XHTML 1.0 Strict//EN";
            context.SystemId = "xhtml1-strict.dtd";
            return context;
        }
    }
}

and:

using System.Reflection;
using System.Xml;

namespace SticklebackPlastic.Xml {

    internal static class XhtmlResolverFactory {

        internal static XmlResolver Create() {
            XmlResourceResolver resolver = new XmlResourceResolver();

            Assembly assembly = typeof (XhtmlResolverFactory).Assembly;
            string resourceNamespace = typeof (XhtmlResolverFactory).Namespace;

            resolver.AddPublicIdMapping("-//W3C//DTD XHTML 1.0 Strict//EN", assembly, resourceNamespace, XhtmlStrictDtd);
            resolver.AddPublicIdMapping("-//W3C//DTD XHTML 1.0 Transitional//EN", assembly, resourceNamespace, XhtmlTransitionalDtd);
            resolver.AddPublicIdMapping("-//W3C//DTD XHTML 1.0 Frameset//EN", assembly, resourceNamespace, XhtmlFramesetDtd);

            return resolver;
        }

        private const string XhtmlStrictDtd = "xhtml1-strict.dtd";
        private const string XhtmlTransitionalDtd = "xhtml1-transitional.dtd";
        private const string XhtmlFramesetDtd = "xhtml1-frameset.dtd";
    }
}

Forgive the lack of xml docs, this post is long enough without it...

Tags:

Comments (21) -

casinos en lignes
casinos en lignes France
7/19/2011 10:37:57 AM #

An powerful share, I just settled this onto a mate who was doing a little assay on this. And he in event bought me breakfast  because I institute it for him.. smile. So obstruction me express differently that: Thnx in return the treat! But yeah Thnkx during spending the moment to deliberate over this, I  feel strongly to it and love reading more on this topic. If practicable, as you suit expertise, would you mind updating your blog with  more details? It is highly caring recompense me. Big thumb up an eye to this blog post!

Reply

best suv 2011
best suv 2011
7/20/2011 10:34:28 PM #

I've recently began a weblog, the data you provide on this site has helped me tremendously. Thanks for all your time &amp; work.

Reply

Stephane
Stephane
10/22/2011 9:43:47 AM #

Hi

nice post here may come back soon
keep update

Reply

Guide Marrakech
Guide Marrakech France
11/12/2011 4:16:58 PM #

Rattling nice word can be base on blog .

Reply

philix
philix United States
11/19/2015 1:51:07 PM #

Great Site

Reply

Philix
Philix United States
11/20/2015 8:50:45 AM #

Great Web Site

Reply

Philix
Philix United States
11/23/2015 12:19:10 AM #

Superb Site

Reply

Riley Laurino
Riley Laurino United States
1/15/2016 3:48:10 AM #

Congrats! This is such a great website, I know it will translate well into book form. Which is AWESOME!

Reply

Selma Houzah
Selma Houzah United States
1/15/2016 4:15:32 AM #

Well good for you! I have a lot but one I just thought of was when you lick a crease of paper so that it'll rip in a straight line and it actually tears in a frayed straight line rather than rips awkwardly. I love that bleachy taste and that bleachy satisfaction.

Reply

Vonnie Liter
Vonnie Liter United States
1/15/2016 4:34:17 AM #

Congrats! So glad to see this site getting the recognition it deserves, the world can always use a little more awesome.

Reply

Walter Rigsbee
Walter Rigsbee United States
1/15/2016 4:35:51 AM #

Congratulations on your success! You deserve it for brightening all of my work days with something to be excited about.

Reply

Douglas Ramler
Douglas Ramler United States
1/15/2016 4:51:37 AM #

Hey, congratulations Neil. This site really is something special and it's been a pleasure to share it with my readership too. Looking forward to future awesome things, and I'll send any suggestions that are sufficiently awesome.

Reply

Riley Laurino
Riley Laurino United States
1/15/2016 5:07:46 AM #

Let me be the first to say: Congrats, Neil! I've been following this site from day one, and have loved every single day of awesomeness that it has delivered. You deserve all the success coming your way. Keep it up!AWESOME!

Reply

Ronnie Motter
Ronnie Motter United States
1/15/2016 5:15:08 AM #

Congrats. And all of the items featured so far should be in the book.

Reply

Taren Esh
Taren Esh United States
1/15/2016 5:33:12 AM #

Let me be the first to say: Congrats, Neil! I've been following this site from day one, and have loved every single day of awesomeness that it has delivered. You deserve all the success coming your way. Keep it up!AWESOME!

Reply

Chang Carbonneau
Chang Carbonneau United States
1/15/2016 5:44:16 AM #

Congratulations, Neil! I love this site and I know I'll love the book. Christmas, birthdays, Father's Day 2010  check!

Reply

Rico Petronis
Rico Petronis United States
1/15/2016 5:54:01 AM #

Congrats! So glad to see this site getting the recognition it deserves, the world can always use a little more awesome.

Reply

Irvin Dewinter
Irvin Dewinter United States
1/15/2016 6:07:42 AM #

Congrats! So glad to see this site getting the recognition it deserves, the world can always use a little more awesome.

Reply

Chang Carbonneau
Chang Carbonneau United States
1/15/2016 6:48:11 AM #

The dolphin saving you one is the best one!

Reply

Palmer Rinck
Palmer Rinck United States
1/15/2016 7:06:55 AM #

Congratulations on your success! You deserve it for brightening all of my work days with something to be excited about.

Reply

Raleigh Maarx
Raleigh Maarx United States
1/15/2016 7:44:57 AM #

I can't wait to read this masterpiece. Ordering off the menu at fast food restaurants and old dangerous playground equipment have to be in the book!

Reply

Pingbacks and trackbacks (1)+

Add comment

biuquote
  • Comment
  • Preview
Loading

Rel=Me

Month List

RecentComments

Comment RSS