*.dannyg

Serializing .NET Resources (.resx) as JSON (part 2)

Jun
09

So after playing with my own code after a while I realized that there was a slight flaw in my thinking.  Since every application is slightly different in the way it determines what culture to use, and that each request is technically a different Thread, I need a more generic way to capture that information.

I decided to use a querystring value.  Here is my updated code in regards to reading the querystring culture, setting it and fired up the appropriate ResourceManager..

Serializing .NET Resources (.resx) as JSON

Jun
08

This is actually quite a useful solution.  The goal here is to use the same resource file for client and server side coding, while also utilizing the “Culture” awareness of .NET that might be built into your application.

Be sure to create your Resources as a separate satellite assembly.

We are going to create an HttpHandler.  Basically create a class and implement the IHttpHandler interface.

Look at the sample code below.

    public class ResourceJS : IHttpHandler
    {
        public bool IsReusable
        {
            get { return true; }
        }
        public void ProcessRequest(HttpContext context)
        {
            ResourceManager rm = new ResourceManager(ConfigurationManager.AppSettings["JSResourcesAssemblyType"].ToString(),
                Assembly.LoadFile(ConfigurationManager.AppSettings["JSResourcesAssemblyPath"].ToString()));
            rm.GetString("");
            ResourceSet rs = rm.GetResourceSet(Thread.CurrentThread.CurrentCulture, false, false);
            var sbInitial = "var rm = {";
            var sb = new StringBuilder(sbInitial);
            var resEnum = rs.GetEnumerator();
            while (resEnum.MoveNext())
            {
                if (sb.ToString() != sbInitial) sb.Append(",");
                sb.Append(""" + resEnum.Key + "":"" +
                    resEnum.Value.ToString().Replace("rn", "").Replace(""", "\"") + """);
            }

            sb.Append("}");
            sb.ToString();

            context.Response.ContentType = "text/javascript";
            context.Response.Write(sb.ToString());
        }
    }

You can see here that there are two Web.config values that have specified here.  JSResourcesAssemblyType is the full Namespace and root class name of the Resource files.  JSResourcesAssemblyPath is the location of satellite assembly.  This uses reflection to load up the metadata of the assembly.

Once this compiled, you need to add this to the web.config.  Here is a sample for IIS7.

 

<handlers>
            <remove name="WebServiceHandlerFactory-Integrated" />
            <remove name="ScriptHandlerFactory" />
            <remove name="ScriptHandlerFactoryAppServices" />
            <remove name="ScriptResource" />
            <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
            <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
            <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add name="ResourceJS" path="ResourceJS.axd" verb="GET" type="ResourceJS" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode" />
      <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </handlers>

You can see the ResourceJS is being extended to the url ResourceJS.axd.  Below is my sample page.

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ResourceJSTest.aspx.cs" Inherits="ResourceJSTest" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript" src="js/2.1.0.0-debug/jquery-1.4.4.min.js"></script>
    <script type="text/javascript" src="/ResourceJS.axd"></script>
    <script type="text/javascript">
        $(function () {
            $("#target").html(rm.FAQ_Q8_TEXT);
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div id="target"></div>
    </form>
</body>
</html>

The rm object in javascript has all the “keys” that were defined in the original RESX file for that culture.  You can call any of these as a property of rm.   Very re-usable.