5

Background

I am working on an application that requires the consumption of JSON services on the server, and then repackaging of that JSON into a view model that can be referenced using Razor syntax in the view. Server-side manipulation of this code is a must for various reasons.

We are using C#, .NET 4, MVC3, Razor, JsonFx.

We currently have code that works just fine, but it is taking up to a minute to iterate 250 items in the received JSON object and this is unacceptable (not to mention baffling). I have already isolated the problem to the following loop; the JSON comes in lightening-fast so that is not the problem. Here is the working but extremely slow code:

        var reader = new JsonReader();
        var json = GetJson(SPListName);

        var admItems = new List<IDictionary<String, object>>();
        dynamic _items = reader.Read(json); //This part is REALLY fast.  No problem here.
        foreach (var itm in _items)
        {
            dynamic obj = new ExpandoObject();
            foreach (dynamic admObj in itm)//Here begins the slow part.
            {
                var item = obj as IDictionary<String, object>;
                var encodedValue = "";
                try
                {
                    if(admObj.Key == "Title")
                    {
                        encodedValue = admObj.Value.ToString();
                    }else
                    {
                        encodedValue = admObj.Value[0].ToString();
                    }
                }
                catch (Exception)
                {
                    encodedValue = admObj.Value.ToString();                   
                }

                item[admObj.Key] = encodedValue.EncodeNonAscii().FixHtmlEntities();
            }
            admItems.Add(obj);
        }
        return admItems;

You may also notice a few custom extension methods. Here they are (in case that matters):

public static string EncodeNonAscii(this Object str)
            {
                StringBuilder sb = new StringBuilder();
                foreach (char c in str.ToString())
                {
                    if (c > 127)
                    {
                        // This character is too big for ASCII
                        string encodedValue = "\\u" + ((int) c).ToString("x4");
                        sb.Append(encodedValue);
                    }
                    else
                    {
                        sb.Append(c);
                    }
                }
                return sb.ToString();
            }

            public static string FixHtmlEntities(this Object str)
            {
                var fixedString = str.ToString().Replace("\\u00c2\\u00ae", "&reg;");
                return fixedString;
            }

Question

What the heck am I doing wrong/how do I speed this up. My brain is hamburger right now so I hope someone points out a simple oversight.

Update/Resolution

Rophuine and Joshua Enfield both pointed to the root of the speed issue: the catching of exceptions was slowing everything down.

Many folks suggested that I use Json.Net or something similar. While I appreciate that advice, it really wasn't at the root of my problem (even though it may have appeared that way); I have used Json.Net extensively in the past and came to prefer JsonFx over it a few months ago. In this particular case, I am more concerned with the construction of a view model object as I have already deserialized the JSON using JsonFx. Please let me know if you still think I am missing your point ;).

The reason for my brain-dead try/catch scheme was that I needed to do different things with each property of the JSON string depending on it's type (string, string[], object[], etc). I forgot that System.Type can handle that for me. So here is the final code:

var reader = new JsonReader();
                var json = GetJson(SPListName);

                var admItems = new List<IDictionary<String, object>>();
                dynamic _items = reader.Read(json);
                foreach (var itm in _items)
                {
                    dynamic obj = new ExpandoObject();
                    foreach (dynamic admObj in itm)
                    {
                        var item = obj as IDictionary<String, object>;
                        var encodedValue = "";

                        Type typeName = admObj.Value.GetType();

                        switch (typeName.ToString())
                        {
                            case("System.String[]"):
                                encodedValue = admObj.Value[0].ToString();
                                break;
                            default:
                                encodedValue = admObj.Value.ToString();
                                break;
                        }

                        item[admObj.Key] = encodedValue.EncodeNonAscii().FixHtmlEntities();
                    }
                    admItems.Add(obj);
                }
                return admItems;

Hope this helps someone!

Matt Cashatt
  • 23,490
  • 28
  • 78
  • 111
  • How many exceptions are occuring? If it's border-line flow control exception handling may be slowing you down some, but a minute sounds awfully long for 250 items. Do you really need to use dynamic here? – Joshua Enfield Feb 11 '12 at 00:54
  • 2
    Are there any reason you don't use json library such as JSON.NET or ServiceStack.NET? It's easier (and also fast) to parse json string. – Khairuddin Ni'am Feb 11 '12 at 00:55
  • In addition to @Ni'am, you can have a dictionary of objects with a single line of code `JsonConvert.DeserializeObject`. Examples: http://stackoverflow.com/questions/8662233/parsing-non-standard-json/8662323#8662323 http://stackoverflow.com/questions/8887029/parsing-json-file-c-sharp/8887177#8887177 – L.B Feb 11 '12 at 01:14
  • I think that @JoshuaEnfield and Rophuine are on the money. Unfortunately the server that issues the JSON just went down. Ugh! Will let you guys know shortly. Thanks! – Matt Cashatt Feb 11 '12 at 01:20
  • @MatthewPatrickCashatt, I also think that your problem is related with the exceptions, but using for ex, Json.Net can help you to replace all of your code you posted with a single line( and no exceptions any more) – L.B Feb 11 '12 at 01:30
  • Did you profile your application using a tool like ANTS or the built-in VS profiler? – Daniel Mann Feb 11 '12 at 05:08
  • @DBM--No I did not. In fact, I have never done that and am very interested in learning how. So are these good tools to begin learning about profiling? – Matt Cashatt Feb 11 '12 at 05:10

1 Answers1

6
            try
            {
                if(admObj.Key == "Title")
                {
                    encodedValue = admObj.Value.ToString();
                }else
                {
                    encodedValue = admObj.Value[0].ToString();
                }
            }
            catch (Exception)
            {
                encodedValue = admObj.Value.ToString();                   
            }

I don't have a C# compiler in front of me, or access to your data, but this looks suspect to me. Exceptions are extremely slow - are you hitting the catch block often? If you are, try to work out what's causing the exceptions and handle them without causing an exception - leave exceptions to handle rare situations you haven't thought of.

Edit: Final solution is in the question edit - I won't repeat it here. If you have a performance problem and you have exception-handling somewhere in your loop, that's often the very first thing to try and eliminate. Exceptions are awfully slow - much more so than you might think. They're best kept for very unusual circumstances.

Rophuine
  • 724
  • 3
  • 8
  • Cheers Rophuine! Thanks for the help. Please see my final update above and feel free to incorporate any of it into your answer as you see fit. – Matt Cashatt Feb 11 '12 at 05:05
  • Apparently exceptions are only slow in debug mode, however as implied by the name, exceptions should be the exception and not the rule. http://yoda.arachsys.com/csharp/exceptions.html – Stephen Oberauer Sep 04 '13 at 13:45