Monday, 2 June 2014

Serving up Silverlight Apps

Building on the previous post covering a simple web-service/server one of the first uses I wanted for this was to serve up some Silverlight apps with the associated services. This is pretty easy on the Silverlight side as you just need to bundle up the bin directory and point the server to those folders and allow them to be served up... like this:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.IO;

namespace SimpleService
{
    [ServiceContract]
    public interface IApplicationRetriever
    {
        [OperationContract, WebGet(UriTemplate = "/")]
        Stream GetBasePage();

        [OperationContract, WebGet(UriTemplate = "/{page}")]
        Stream GetSilverlightPage(string page);
    }


    public class ApplicationRetrieverService : PolicyRetrieverService, IApplicationRetriever
    {
        public static string style = @"<style type=""text/css"">
                                        html, body { height: 100%; overflow: auto; }
                                        body { padding: 0; margin: 0; }
                                        #silverlightControlHost { height: 100%; text-align:center; }
                                      </style>";



        public static string errorscript = @"<script type=""text/javascript"">
                                            function onSilverlightError(sender, args) {
                                                var appSource = """";
                                                if (sender != null && sender != 0) {
                                                    appSource = sender.getHost().Source;
                                                }
            
                                                var errorType = args.ErrorType;
                                                var iErrorCode = args.ErrorCode;

                                                if (errorType == ""ImageError"" || errorType == ""MediaError"") {
                                                    return;
                                                }

                                                var errMsg = ""Unhandled Error in Silverlight Application "" +  appSource + ""\n"" ;

                                                errMsg += ""Code: ""+ iErrorCode + ""    \n"";
                                                errMsg += ""Category: "" + errorType + ""       \n"";
                                                errMsg += ""Message: "" + args.ErrorMessage + ""     \n"";

                                                if (errorType == ""ParserError"") {
                                                    errMsg += ""File: "" + args.xamlFile + ""     \n"";
                                                    errMsg += ""Line: "" + args.lineNumber + ""     \n"";
                                                    errMsg += ""Position: "" + args.charPosition + ""     \n"";
                                                }
                                                else if (errorType == ""RuntimeError"") {           
                                                    if (args.lineNumber != 0) {
                                                        errMsg += ""Line: "" + args.lineNumber + ""     \n"";
                                                        errMsg += ""Position: "" +  args.charPosition + ""     \n"";
                                                    }
                                                    errMsg += ""MethodName: "" + args.methodName + ""     \n"";
                                                }

                                                throw new Error(errMsg);
                                            }
                                            </script>";




        public string applicationpath = "";

        public Stream GetBasePage()
        {
            return GetApplication(null);
        }

        public Stream GetApplication(string docid)
        {
            Console.WriteLine("Get Index");
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";

            //string path = Directory.GetCurrentDirectory() + "\\application\\index.html";
            //return new FileStream(path, FileMode.Open);

            string page = "";

            page += @"<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">";
            page += @"<html xmlns=""http://www.w3.org/1999/xhtml"" >";

            page += "<head>";
            page += "<title>AgApplication</title>";
            page += style;

            page += @"<script type=""text/javascript"" src=""Silverlight.js""></script>";


            page += errorscript;
            page += "</head>";
            page += "<body>";

            page += @"<form id=""form1"" runat=""server"" style=""height:100%"">";
            page += @"<div id=""silverlightControlHost"">";
            page += @"<object data=""data:application/x-silverlight-2,"" type=""application/x-silverlight-2"" width=""100%"" height=""100%"">";

            page += String.Format(@"<param name=""source"" value=""{0}""/>", "AgApplication.xap");
            
            page += @"<param name=""onError"" value=""onSilverlightError"" />";
      page += @"<param name=""background"" value=""white"" />";

            if (docid != null)
            {
                page += String.Format(@"<param name=""initparams"" value=""doc={0}"" />",docid);
            }

      page += @"<param name=""minRuntimeVersion"" value=""4.0.50826.0"" />";
      page += @"<param name=""autoUpgrade"" value=""true"" />";
      page += @"<a href=""http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0"" style=""text-decoration:none"">";
    page += @"<img src=""http://go.microsoft.com/fwlink/?LinkId=161376"" alt=""Get Microsoft Silverlight"" style=""border-style:none""/>";
      page += @"</a>";
         page += @"</object>";
            page += @"<iframe id=""_sl_historyFrame"" style=""visibility:hidden;height:0px;width:0px;border:0px""></iframe></div>";
            page += @"</form>";

            page += "</body>";
            page += "</html>";

            return new MemoryStream(UTF8Encoding.Default.GetBytes(page));
        }

        public Stream GetSilverlightPage(string page)
        {
            Console.WriteLine("Get Page {0}", page);
            Stream stream = null;

            try
            {
                string[] pageitems = page.Split('.');

                if (pageitems.Length > 1)
                {
                    switch (pageitems[pageitems.Length - 1])
                    {
                        case "xml":
                        case "html":
                            WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
                            break;
                        case "js":
                            WebOperationContext.Current.OutgoingResponse.ContentType = "text/javascript";
                            break;
                    }

                }
                string path = String.Format("{0}\\{1}", applicationpath, page);
                stream =  new FileStream(path, FileMode.Open);
            }
            catch (Exception)
            {
                string path = Directory.GetCurrentDirectory() + "\\application\\error.html";
            }

            return stream;
        }
    }

} 
 


So, what this does is add a set of service calls to respond to request for pages. The root URL is used to get the 'base page' which returns the Silverlight App, which refers to the app 'Application.Xap'. The second function returns any requested pages (files) in the bundle with the appropriate content types.

All you need to do now is to have the SimpleService class inherit this and you can serve up Silverlight Apps.

So, what's next? Well, if you're serving up the app, it's pretty usual to think you want to provide some service back-end. All fine if you're running the service and the app from the same server, but when you're testing this and running the Silverlight app from the IDE it kicks off its own temporary server and so any of the services on this web server are seen as cross-process access and are refused in the Silverlight app, so you need to be able to serve up a Silverlight policy file as well. For sake of completeness I've added in the Flash policy file as well, but commented out as this was in my original source and it's not worth losing.


using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.IO;

namespace SimpleService
{
    [ServiceContract]
    public interface IPolicyRetriever
    {
        [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
        Stream GetSilverlightPolicy();
        //[OperationContract, WebGet(UriTemplate = "/crossdomain.xml")]
        //Stream GetFlashPolicy();
    }

    public class PolicyRetrieverService : IPolicyRetriever
    {
        private Stream StringToStream(string result)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
            return new MemoryStream(Encoding.UTF8.GetBytes(result));
        }

        /*
        public Stream GetFlashPolicy()
        {
            Console.WriteLine("Get Flash Policy");
            string result = @"<?xml version=""1.0"" encoding=""utf-8""?>";
            return StringToStream(result);
        }
        */

        public Stream GetSilverlightPolicy()
        {
            Console.WriteLine("Get Silverlight Policy");
            string result = @"<?xml version=""1.0"" encoding=""utf-8""?>
            <access-policy>
            <cross-domain-access>
                <policy>
                <allow-from http-request-headers=""*"">
                <domain uri=""*""/>
            </allow-from>
            <grant-to>
                <resource path=""/"" include-subpaths=""true""/>
            </grant-to>
        </policy>
        </cross-domain-access>
        </access-policy>";
            return StringToStream(result);
        }
    }
}

All that needs to be done now is to inherit from this class as well and start to put some web-service calls into the original SimpleService class.

Happy coding!

Simple Standalone WebService/Server

I was noodling around with some other ideas a few weeks ago using some base code that I now have in a number of small projects and thought this would be good for a small post. The initial intention (and you'll see this is interesting for other follow-on work) was to have a small command-line programe that I could serve up some basic web-services to test algorithms and other ideas. It took a bit of searching and playing when I first had a go, so maybe the information is more easily accessible now, however, as I use this quite a bit it's worthwhile getting the thoughts down.

Basically how to make a relatively smallish set of code that can run up as a web-service/server without the need to configure and install IIS using C#.

First of all, you'll need to kick-off a basic console project and set the target framework to .NET Framework 4 (not the Client Profile as is my default). To do this go to Project > SimpleService Properties (or another name for your project) and change the target framework. It'll probably warn that you need to save the project first.













You then need to import a couple of references. So go to the Solution Explorer, right click on References > Add References and then import System.ServiceModel and System.ServiceModel.Web:










You should then have something like this:













Now for some code! This is the boilerplate:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.IO;
using System.Threading;

namespace SimpleService
{
    [ServiceContract]
    public interface ISimpleService
    {
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public partial class SimpleService : ISimpleService
    {
    }

    class Program
    {

        static void Main(string[] args)
        {
            string uripath;

            uripath = "http://localhost/";

            bool live = false;

            SimpleService service = new SimpleService();

            // run service

            WebServiceHost host = new WebServiceHost(service, new Uri(uripath));
            WebHttpBinding binding = new WebHttpBinding();
            binding.MaxBufferSize = 2147483647;
            binding.MaxReceivedMessageSize = 2147483647;
            ServiceEndpoint ep = host.AddServiceEndpoint(typeof(ISimpleService), binding, "");



            host.Open();
            Console.WriteLine("Service is running");
            Console.WriteLine("Press enter to quit...");
            Console.ReadLine();
            host.Close();


        }
    }
}

If you try and run this now you're going to have one of two problems. Firstly if you haven't already then you need to adjust your ACL to allow web services to work on the port of your choice. In this case the default port 80. This took some figuring out! You're gong t need to have administrator privilidges. Firstly start the command-line from the 'start' menu by typing in 'cmd' then hit ctrl+shift+enter. This will start the command-line in administrator mode. Then type the following cryptic line:

netsh http add urlacl url=http://+:80/ user=domain\user

Where domain\user is obviously your domain and user account.

The second (and more positive) problem you will have is that the code as it is does not provide any service entry points. Let's do the classic 'helloworld':



    [ServiceContract]
    public interface ISimpleService
    {
        [OperationContract, WebGet(UriTemplate = "helloworld")]
        Stream GetHelloWorld();
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public partial class SimpleService : ISimpleService
    {
        private Stream StringToStream(string result)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
            return new MemoryStream(Encoding.UTF8.GetBytes(result));
        }

        public Stream GetHelloWorld()
        {
            String response = "<html><body>hello world</body></html>";

            return StringToStream(response);
        }
    }

As we're effectively hi-jacking the C# WCF REST framework this needed another little function StringToStream to enable the response to be sent back as text and to inform the browser that the content type is HTML.

You can test this by running up a browser and putting in 'http://localhost/helloworld which returns back the html formatted string 'hello world'.

So, with very little code a small little server that can provide web pages and other web-services.... in the next couple of posts I'll show some other additions to this that I've been using.

Talking Web-Services to JIRA and Parsing JSON

In my previous post I mentioned a quick-n-simple way of parsing JSON web-service responses in C# with a helping hand from some other code. In this post I wanted to show a simple example of parsing the JIRA data. This is the basis of what I am using to do more more complex processing and then dumping to a simple CSV for more analysis in Excel.

Getting a single 'Issue'
At the basic level the following call can be used: https://jiraserver/rest/api/latest/issue/{0}
where the {0} is the number of the issue required. This effectively returns the same information that can be downloaded via already available web page functions.

However, in my case what I wanted to do was to work through the issue history and look at the state-transitions over a time period to analyse issue resolution. In this case the following call needs to be used: https://jiraserver/rest/api/latest/issue/{0}?expand=changelog

I've then got some pretty standard C# web-request code and reading the response back into a JSON string that I can then process:


static string GetIssue(string key)
{
    string url = String.Format("https://jiraserver/rest/api/latest/issue/{0}?expand=changelog",key);

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

    IWebProxy proxy = request.Proxy;
    if (proxy != null)
    {
        string proxyuri = proxy.GetProxy(request.RequestUri).ToString();
        request.UseDefaultCredentials = true;
        request.Proxy = new WebProxy(proxyuri, false);
        request.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
    }


    string username = "username";
    string password = "password";
    string authInfo = username + ":" + password;
    authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
    request.Headers["Authorization"] = "Basic " + authInfo;

    HttpWebResponse response = (HttpWebResponse)request.GetResponse();


    // we will read data via the response stream
    Stream s = response.GetResponseStream();

    // used on each read operation
    byte[] buf = new byte[8192];
    string tempString = null;
    int count = 0;

    // used to build entire input
    StringBuilder sb = new StringBuilder();

    do
    {
        // fill the buffer with data
        count = s.Read(buf, 0, buf.Length);

        // make sure we read some data
        if (count != 0)
        {
            // translate from bytes to ASCII text
            tempString = Encoding.ASCII.GetString(buf, 0, count);

            // continue building the string
            sb.Append(tempString);
        }
    }
    while (count > 0); // any more data to read?

    return sb.ToString(); // json
}

Processing the JSON response
The next step is to extract the data into an object that I can use for more processing. Keeping things simple this is a basic class containing just the data I needed. If I were using this more heavily I probably should do a proper job of JSON to object mappings (may for a follow-on post), but time here was really important, I knocked this up and completed the analysis in a couple of hours one afternoon and have been using it weekly ever since.


class Issue
{
    public string key;
    public string type;
    public string description;
    public string priority;
    public string component;
    public DateTime created;
    public string target;

    public class Transition
    {
        public DateTime date;
        public string from;
        public string to;

    }

    public List<Transition> transitions = new List<Transition>();
}

As processing the progression of issues was also a key point I added a simple hacky member to parse the date-time data:


public void SetCreated(string c)
{
    int yy = Convert.ToInt32(c.Substring(0, 4));
    int MM = Convert.ToInt32(c.Substring(5, 2));
    int dd = Convert.ToInt32(c.Substring(8, 2));
    int hh = Convert.ToInt32(c.Substring(11, 2));
    int mm = Convert.ToInt32(c.Substring(14, 2));
    int ss = Convert.ToInt32(c.Substring(17, 2));

    date = new DateTime(yy, MM, dd, hh, mm, ss);
}

I then had a simple function to process the JSON response into the class:


static Issue ProcessIssue(string json)
{
    JavaScriptSerializer jss = new JavaScriptSerializer();
    jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
    DynamicJsonConverter.DynamicJsonObject j = jss.Deserialize(json, typeof(object)) as DynamicJsonConverter.DynamicJsonObject; //dynamic;

    dynamic d = jss.Deserialize(json, typeof(object)) as dynamic;

    Issue i = new Issue();

    i.key = d.key;
    i.type =  d.fields.issuetype.name;
    i.description = d.fields.summary;
    i.SetCreated(d.fields.created); 
    i.priority = d.fields.priority.name;

    Dictionary<string, object> fields = (Dictionary<string, object>)j._dictionary["fields"];
    if (fields.ContainsKey("customfield_11342") && fields["customfield_11342"] != null)
    {
        i.target = d.fields.customfield_11342.value;
    }


    foreach (dynamic h in d.changelog.histories)
    {
        foreach (dynamic item in h.items) // items (fields)
        {
            if (item.field == "status")
            {
                Issue.Transition t = new Issue.Transition();
                t.SetCreated(h.created);
                t.from = item.fromString;
                t.to = item.toString;

                i.transitions.Add(t);
            }
        }
    }

    return i;
}

This then gives enough for me to do the later processing I needed. You can see from this how easy the JSON parsing code is.

In testing I also put a simple routine together to enable easier checking of JSON and allow dumping of the data to XML in case I wanted to process further or use for other purposes. This also helped figure out some of the JSON structure. It's a bit hacky again, but does the job:


public static void MakeXML(XElement x, IDictionary<string, object> d)
{
    foreach (KeyValuePair<string, object> kv in d)
    {
        if (kv.Value is String)
        {
            x.Add(new XElement("field",
                    new XAttribute("key", kv.Key),
                    new XAttribute("value", kv.Value.ToString())));
        }
        if (kv.Value is Dictionary<string, object>)
        {
            XElement o = new XElement(kv.Key);
            x.Add(o);
            MakeXML(o, kv.Value as IDictionary<string, object>);
        }
        if (kv.Value is ArrayList)
        {
            XElement o = new XElement(kv.Key);
            x.Add(o);
            MakeXML(o, kv.Value as ArrayList);
        }
    }
}


public static void MakeXML(XElement x, ArrayList a)
{
    foreach (object o in a)
    {
        if (o is String)
        {
            x.Add(new XElement("field", o.ToString()));
        }
        if (o is Dictionary<string, object>)
        {
            XElement aa = new XElement("fields");
            x.Add(aa);
            MakeXML(aa, o as IDictionary<string, object>);
        }
        if (o is ArrayList)
        {
            XElement aa = new XElement("fields");
            x.Add(aa);
            MakeXML(aa, o as ArrayList);
        }
    }
}

This can be easily called in the earlier processing function as follows:


XElement x = new XElement("json");
MakeXML(x, j._dictionary as IDictionary<string, object>);

XDocument xdoc = new XDocument();
xdoc.Add(x);
xdoc.Save(String.Format("issue-{0}.xml",i.key));


Processing All Issues
Now that I can process a single issue I needed to do some analysis over all the issues, so another call is needed to get all the issues for a particular project:

https://jiraserver/rest/api/latest/search?jql=project={0}&maxResults=100&startAt={1}

As you can see this the project name is in the first field and the results are paged which adds some extra work. I'll omit the calling function as it's pretty much the same as the previous one for a single issue, it's the processing of the JSON that is important:


static List<string> ProcessIssues(string json)
{
    JavaScriptSerializer jss = new JavaScriptSerializer();
    jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
    DynamicJsonConverter.DynamicJsonObject j = jss.Deserialize(json, typeof(object)) as DynamicJsonConverter.DynamicJsonObject; //dynamic;

    dynamic d = jss.Deserialize(json, typeof(object)) as dynamic;


    List<string> issues = new List<string>();

    foreach (dynamic i in d.issues)
    {
        issues.Add(i.key);
    }

    return issues;
}

Pretty basic code, in this case the response is to process through a list of the issues that come back and add them into a list that I can then use to call the individual issues later.

So, here's the code for calling the pages and getting all of the issues into a list:


string json;

int total = -1;
int index = 0;

List<string> allissues = new List<string>();

do
{
    json = GetIssues("PROJECT", index++);
    if (total < 0)
    {
        total = TotalIssues(json);
    }
    List<string> issues = ProcessIssues(json);

    foreach (string i in issues)
    {
        allissues.Add(i);
    }
}
while (index*100 < total);


It's not the most elegant bunch of code, but it's now a good tool that does a useful job for me. It took a couple of hours end-to-end one afternoon in the middle of other jobs and now saves considerably more time that the time it took to put together. Enjoy!



Quick-n-Simple JSON Parsing

A few weeks back I found myself continually downloading data into an Excel spreadsheet, manipulating the data and then producing some neat graphs to show our bug tracking progress from JIRA. This led to needing a bit more data and some analysis that was not so easy with the data download directly available from JIRA as it did not contain the bug history, so enough was enough, time to break out some simple code.

Thankfully JIRA provides a REST web-service interface which after a bit of googling and working through their sparse online documentation managed to get me the calls I needed. The only downside is that they did not offer an XML based response from the REST queries that I would have hoped and only provided JSON instead.

So, looking at most of the mappings avaibale in C# these all had as expected JSON to object mappings. This was a bit more tedious than what I needed as the intention was only to have a quick script that I could run, process the data and dump into a CSV that I can then re-work in Excel. So, I had the search for something simpler to parse the JSON response.

After a bit of searching I turned up a link to Shawn Weisfeld who was using dynamic to parse JSON building on work from a few others. He's got a pretty good description of his thinking and how to use the code. I then looked at the modification by Drew Noakes in his StackOverflow posting.

In the end to make this work I had to hack about and change two of the private members to public. I know it's hacky, but this was a quick job and the script was secondary to the work I needed to do on the data. Anyway, here's what I used (as much for myself next time I need it!):



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.Xml;
using System.Xml.Linq;

namespace JSONProcess
{
    public sealed class DynamicJsonConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
        }

        #region Nested type: DynamicJsonObject

        //private sealed class DynamicJsonObject : DynamicObject
        public sealed class DynamicJsonObject : DynamicObject
        {
            //private readonly IDictionary<string, object> _dictionary;
            public readonly IDictionary<string, object> _dictionary;

            public DynamicJsonObject(IDictionary<string, object> dictionary)
            {
                if (dictionary == null)
                    throw new ArgumentNullException("dictionary");
                _dictionary = dictionary;
            }

            public override string ToString()
            {
                var sb = new StringBuilder("{");
                ToString(sb);
                return sb.ToString();
            }

            private void ToString(StringBuilder sb)
            {
                var firstInDictionary = true;
                foreach (var pair in _dictionary)
                {
                    if (!firstInDictionary)
                        sb.Append(",");
                    firstInDictionary = false;
                    var value = pair.Value;
                    var name = pair.Key;
                    if (value is string)
                    {
                        sb.AppendFormat("{0}:\"{1}\"", name, value);
                    }
                    else if (value is IDictionary<string, object>)
                    {
                        new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
                    }
                    else if (value is ArrayList)
                    {
                        sb.Append(name + ":[");
                        var firstInArray = true;
                        foreach (var arrayValue in (ArrayList)value)
                        {
                            if (!firstInArray)
                                sb.Append(",");
                            firstInArray = false;
                            if (arrayValue is IDictionary<string, object>)
                                new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
                            else if (arrayValue is string)
                                sb.AppendFormat("\"{0}\"", arrayValue);
                            else
                                sb.AppendFormat("{0}", arrayValue);

                        }
                        sb.Append("]");
                    }
                    else
                    {
                        sb.AppendFormat("{0}:{1}", name, value);
                    }
                }
                sb.Append("}");
            }

            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                if (!_dictionary.TryGetValue(binder.Name, out result))
                {
                    // return null to avoid exception.  caller can check for null this way...
                    result = null;
                    return true;
                }

                result = WrapResultObject(result);
                return true;
            }

            public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
            {
                if (indexes.Length == 1 && indexes[0] != null)
                {
                    if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
                    {
                        // return null to avoid exception.  caller can check for null this way...
                        result = null;
                        return true;
                    }

                    result = WrapResultObject(result);
                    return true;
                }

                return base.TryGetIndex(binder, indexes, out result);
            }

            private static object WrapResultObject(object result)
            {
                var dictionary = result as IDictionary<string, object>;
                if (dictionary != null)
                    return new DynamicJsonObject(dictionary);

                var arrayList = result as ArrayList;
                if (arrayList != null && arrayList.Count > 0)
                {
                    return arrayList[0] is IDictionary<string, object>
                        ? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
                        : new List<object>(arrayList.Cast<object>());
                }

                return result;
            }

        }

        #endregion

    }

}

Using this then was pretty easy, like below. I'll cover more in parsing the JIRA specifics as an example in another post.


JavaScriptSerializer jss = new JavaScriptSerializer();
jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
DynamicJsonConverter.DynamicJsonObject j = jss.Deserialize(json, typeof(object)) as DynamicJsonConverter.DynamicJsonObject; 

dynamic d = jss.Deserialize(json, typeof(object)) as dynamic;