IEnumerable<char> Random Password Generator

Last time I showed that IEnumerable uses lazy evaluation with a Fibonacci Sequence generator as an example. Perhaps a more practical example of an infinite series is a random character sequence from which you could generate passwords. This is using IEnumerable a lot like the /dev/random file in Unix.

This example is a password generator program based on a RandomCharSequence type which implements IEnumerable<char>. RandomCharSequence is again not much more than a factory for RandomCharEnumerator. The interesting thing here is that it passes the set of characters to be used in the random generation to RandomCharEnumerator. The character sets are upper, lower, digit and special and are themselves statically defined but are composable  using a CharTypes enumeration.

Like FibonacciSequence, RandomCharSequence is an infinite set. You have to limit how much of it you grab at a particular time using the Take(int) extension method from System.Linq.

RandomCharSequence as Password Generator

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Security.Cryptography;
using System.Reflection;
using System.Collections.ObjectModel;

namespace NewPassword
{
    class Program
    {
        static void Main( string[] args )
        {
            int length = -1;
            int count  = -1;
            if( args.Length > 0 )
            { int.TryParse( args[0], out length ); }
            else
            { length = 8; }
            if( args.Length > 1 )
            { int.TryParse( args[1], out count ); }
            else
            { count = 5; }
            
            var pwdseq = new RandomCharSequence();
            for( int i = 0; i < count; i++ )
            {
                Console.WriteLine( pwdseq.Take( length ).ToArray() );
            }
        }
    }

    [Flags]
    public enum CharTypes
    {
        Lower   = 0x01,
        Upper   = 0x02,
        Digit   = 0x04,
        Special = 0x08,
        All     = Upper | Lower | Digit | Special
    }
    public class RandomCharSequence : IEnumerable<char>
    {
        static readonly IEnumerable<char> s_lower   = new[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'q', 'y', 'z' };
        static readonly IEnumerable<char> s_upper   = new[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'Q', 'Y', 'Z' };
        static readonly IEnumerable<char> s_digit   = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
        static readonly IEnumerable<char> s_special = new[] { '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' };

        //these static properties must exactly match the flags defined in the CharTypes enum
        private static IEnumerable<Char> Lower   { get{ return s_lower; } }
        private static IEnumerable<Char> Upper   { get{ return s_upper; } }
        private static IEnumerable<Char> Digit   { get{ return s_digit; } }
        private static IEnumerable<Char> Special { get{ return s_special; } }

        public RandomCharSequence() : this( CharTypes.All ){}
        public RandomCharSequence( CharTypes charTypes )
        {
            CharTypes = charTypes;
        }

        public CharTypes CharTypes {get; set;}

        public IEnumerator<char> GetEnumerator()
        {
            List<char> pool = new List<char>();
            foreach( var type in (CharTypes[])Enum.GetValues(typeof(CharTypes) ) )
            {
                //CharTypes.All is not a single bit flag. We don't want that one.
                if( type != CharTypes.All && (CharTypes & type) == type )
                {
                    //use reflection to add the static char list properties which
                    //match the flag bits.
                    pool.AddRange( 
                        (IEnumerable<char>)typeof(RandomCharSequence).GetProperty( 
                            type.ToString(), 
                            BindingFlags.Static | BindingFlags.NonPublic 
                            ).GetValue( this, null )
                        );
                }
            }
            return new RandomCharEnumerator( pool );
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public class RandomCharEnumerator : IEnumerator<char>
    {
        public RandomCharEnumerator( IList<char> pool )
        {
            Disposing = false;
            Pool      = new ReadOnlyCollection<char>( pool );
            Random    = RandomNumberGenerator.Create();
        }

        RandomNumberGenerator Random { get; set; } 
        public IList<char> Pool { get; private set; }
        int Index { get; set; }

        public char  Current
        {
            get { return Pool[Index]; }
        }
        object IEnumerator.Current { get { return Current; } }


        private bool Disposing{ get; set; }
        public void  Dispose()
        {
            //RandomNubmerGenerator only implements IDisposable as of .NET Framework 4.0
            IDisposable randDisposable = Random as IDisposable;
            if( randDisposable != null && !Disposing )
            {
                randDisposable.Dispose();
                Disposing = true;
            }
        }

        public bool  MoveNext()
        {
            Index = Random.GetInt( 0, Pool.Count );
            //infinite sequence of random chars. There's always a next one.
            return true;
        }

        public void  Reset(){ /* nothing */ }
    }

    public static class RandomNumberGeneratorExtension
    {
        public static int GetInt( this RandomNumberGenerator random, int min, int max )
        {
            if( min >= max )
                throw new InvalidOperationException( "The min value must be less than the max value." );
            byte[] buff = new byte[8];
            random.GetBytes(buff);
            long r = Math.Abs( BitConverter.ToInt64( buff, 0 ) );
            return (int)((long)min + (r % ((long)max - (long)min)));
        }
    }
}

Compile and run the program to pull some password strings out of the infinite random character sequence.

PS> csc -nologo -out:newpassword.exe ./Program.cs; ./newpassword 10 15
^mVhNjHdds
o4Eq=q;`es
%1R\rL3r3{
+PFRrl4e)u
TvAB+tjNu-
?8zCq~{?KB
@WY)+sF;^I
B+)'?sRee-
G'\e)QkjYp
L8(`@o};$$
)j`$7?RG)4
QcC{4;fZcb
_S89_tm]76
}kAiirj^!=
,qq|%-(s)@

Update: RandomNumberGenerator does not implement IDisposable until .Net Framework 4.0 so I needed to dynamically invoke the interface if present rather than statically compiling with Random.Dispose() in there.

IEnumerable is Lazy… And That’s Cool

We tend to think of IEnumerable<T> as this thing in C# collections which allows foreach to iterate a collection without having to deal with a counter. There’s another semantic feature of IEnumerable that is different from an Array or List<T> or any other concrete collection. IEnumerable is lazy while an array or list is implicitly eager.

Consider an array. First, you have to define its size which is fixed. Then you have to loop over the array and populate it with values. Then you can use it in some other loop later. Array is conducive to eager evaluation because you have to define its size in advance and you have to fill it up before you can use it. Lists are similar conceptually and are actually implemented with arrays internally. The main advantage of a list is that it is dynamically expandable.

IEnumerable is a very different thing. At its core IEnumerable is a contract to provide a factory for an IEnumerator<T> object. IEnumerator then just provides a mechanism to get the current element of the enumeration, move to the next element and reset. IEnumerator only has to know how to get the next element or start over. It doesn’t actually need a collection to exist. MoveNext() only has to get the next value. There’s no requirement that all values are known at any give time and there’s no requirement that the values are finite. This is the essence of lazy evaluation. Only enough work needs to be done to get the next answer. Nothing more.

Fibonacci: An infinite IEnumberable<T>

To illustrate that IEnumerable is a lazy evaluation pattern, I implemented the Fibonacci Sequence as an enumerable type in C#. I can pass a FibonacciSequence object to things that understand IEnumerable and it works. If IEnumerable were an eager implementation my code would spin in an infinite loop but it doesn’t because MoveNext() only calculates the next number in the series if it is asked.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;

namespace LazyFibonacci
{
    class Program
    {
        static void Main( string[] args )
        {
            int i = -1;
            if( args.Length > 0 )
            { int.TryParse( args[0], out i ); }
            else
            { i = 100; }

            var fibonacci = new FibonacciSequence();
            /***
             * fibonacci is infinite.
             * Don't try operations that require the entire set to be enumerated:
             *  - fibinacci.ToList() --> NO!
             *  - fibonacci.Reverse() --> NO!
             *
             * Using .Skip(int) and .Take(int) is a good way to get slices of fibonacci, though.
             ***/

            foreach( var item in fibonacci.Take( i ) )
            {
                Console.WriteLine( item );
            }
        }
    }

    //FibonnacciSequence has no state.
    //It's just a factory for FibonacciEnumerator.
    public class FibonacciSequence : IEnumerable<BigInteger>
    {
        public IEnumerator<BigInteger> GetEnumerator()
        {
            return new FibonacciEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public class FibonacciEnumerator : IEnumerator<BigInteger>
    {
        public FibonacciEnumerator()
        {
            Reset();
        }

        BigInteger Last { get; set; }
        public BigInteger Current { get; private set; }
        object IEnumerator.Current { get { return Current; } }

        public void Dispose() { /*do nothing*/}

        public void Reset()
        {
            Current = -1;
        }

        public bool MoveNext()
        {
            if( Current == -1 )
            {
                //State after first call to MoveNext() before the first element is read
                //Fibonacci is defined to start with 0.
                Current = 0;
            }
            else if( Current == 0 )
            {
                //2nd element in the Fibonacci series is defined to be 1.
                Current = 1;
            }
            else
            {
                //Fibonacci infinite series algorithm.
                BigInteger next = Current + Last;
                Last = Current;
                Current = next;
            }
            //infinite series. there is always another.
            return true;
        }
    }
}

The moment of truth. If IEnumerable is lazy this will work.

 

PS> csc -nologo -r:System.Numerics.dll -out:fibonacci.exe ./program.cs
PS> $fib = (./fibonacci.exe 25); [string]::join(', ', $fib)
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368

Shocking: Google Removing h.264 Support from Chrome

To that end, we are changing Chrome’s HTML5 <video> support to make it consistent with the codecs already supported by the open Chromium project. Specifically, we are supporting the WebM (VP8) and Theora video codecs, and will consider adding support for other high-quality open codecs in the future. Though H.264 plays an important role in video, as our goal is to enable open innovation, support for the codec will be removed and our resources directed towards completely open codec technologies.

via The Chromium Blog

I’m very, very sad to see this announcement. h.264 is the web video standard while WebM is a new Google codec that nobody uses and nobody uses Theora. We really don’t need video codec fragmentation in HTML5. The likely result of that will just be standardization on Flash video. Do we get to re-hash the video format wars of the 1990s in the 2010s?

This sucks and basically shows that HTML5 is not standardized and not ready for prime time. Not cool at all.

This is an opportunity for Microsoft to look like a hero and fix this for Google like they did for Mozilla.

Exploring Functional Programming with Erik Meijer and Haskell

Haskell logoErik Meijer of Microsoft Research is an inspiring guy and his work on LINQ is impressive. I just stumbled across his lecture series on exploring Functional Programming Fundamentals and I decided I would do the course and learn Haskell. I want to expand my frame of reference from primarily imperative and relational thinking to functional thinking. I like the idea of starting with Haskell instead of something like F# simply because Haskell is designed as a pure functional language for teaching and research. Working with Haskell sets aside the complexity of integrating huge a huge framework library.

The text that Dr. Meijer is using is Graham Hutton’s Programming in Haskell which is conveniently available at a discount on Kindle. Hutton’s text uses the Hugs98 Haskell interpreter which is available from haskell.org but seems to have been unmaintained since 2006. The Haskell Platform is also available from haskell.org and is prominently linked on the haskell.org home page. The Haskell Platform is “batteries included’ (ala Python) and based on the Glasgow Haskell Compiler (GHC) and interpreter (GHCi). It seems like GHCi is equivalent to Hugs and works in the same way.

A quick aside here. The maintainer of GHC, Simon Peyton Jones, is also a fellow at Microsoft Research. Interesting.

Homework: Transliterate QuickSort from Haskell C#

OK. I’ve read the first chapter of Hutton and watched the first lecture from Dr. Meijer. In addition to the exercises in Hutton’s text, Dr. Meijer suggested a homework problem of transliterating the Haskell qsort example into C# using LINQ.

Here’s my implementation.

QuickSort in Haskell

qsort []     = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger
                where
                    smaller = [a|a <- xs, a <= x]
                    larger = [b|b <- xs, b > x]

QuickSort in C# with LINQ extension methods

The C# version is more verbose primarily because of some of the Haskell syntactic sugar, like the ++ operator for concatenating lists and the sort of function overloading in Haskell:

  • f [] = [] means that when an empty list is the input of function, f, then an empty list is the output.
  • f (x:xs) automagically takes the first element of the list an puts it into x while the remainder is in xs.

Also, Haskell implicitly knows that types are comparable whereas in C# I have to do some gross syntax with generic constraints an explicitly use IEnumerable<T>.CompareTo(T). Other than those syntactic differences, the C# version works essentially the same way as the Haskell version.

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

namespace QuickSort
{
    static class Program
    {
        static void Main( string[] args )
        {
            var t = new [] { 3, 5, 1, 4, 2 };
            Console.WriteLine( string.Join( ", ", t.QuickSort().ToArray() ) );
        }

        //this does not do the sort in place, but neither did the Haskell version
        public static IEnumerable<T> QuickSort<T>( this IEnumerable<T> list ) where T : IComparable<T>
        {
            //qsort [] = []
            if( list.Count() == 0 )
            { return list; }

            //qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)
            var x  = list.First();
            var xs = list.Skip( 1 );

            return QuickSort( xs.Where( a => a.CompareTo( x ) <= 0 ) )
                    .Concat( new[] { x } )
                    .Concat( QuickSort( xs.Where( a => a.CompareTo( x ) > 0 ) ) );
        }
    }
}

PS> csc -nologo Program.cs; ./program
1, 2, 3, 4, 5

The Daily Flashback: The String in, String out API

I just ran across a recent Daily WTF story “XML’d XML” in my news feed. The gist is that some web service call which inherently has XML as return data returned a “string” as its payload. And it was a string, but a string of XML.

I instantly flashed back on an integration project from a few years back that we did for a Fortune 500 company. The company had contracted with a 3rd party service provider that provided a SOAP API for all of its message passing and we thought this would be much better than something out of the 1990s like passing PGP-encrypted CSV files over FTP. We were working with an—even at the time—ancient Sun ONE 6.0 server platform but it was able to support SOAP web services using Apache Axis.

I became concerned when I realized that every method took a single string “input” argument and returned a string.

I just pulled a sample from our source repository for old time sake.

/**
 * SubscriberAPISoap_PortType.java
 *
 * This file was auto-generated from WSDL
 * by the Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java emitter.
 */

package com.shallRemainNameless;

import javax.xml.soap.SOAPException;

public interface SubscriberAPISoap extends java.rmi.Remote, SoapAuthentication {
    public java.lang.String createSubscriber(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String updateSubscriber(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String addCategory(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String removeCategory(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String getSubscriber(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String getSubscriberData(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String authenticateSubscriber(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String getSubscribers(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String getSubscribersByField(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String deactivateSubscriber(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String activateSubscriber(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String sendOptInMessage(java.lang.String input) throws java.rmi.RemoteException;
    public java.lang.String getRegistrationMetaData(java.lang.String input) throws java.rmi.RemoteException;
}

Every one of those input and return strings was actually XML. I remember when I realized it was XML all the way down, I sent an internal email with the subject “String in, String out == [expletive-deleted]”.

We created a wrapper API to hide the insanity but the actual XML API wasn’t so much better than the String version. They had a unified ApiResultDocument XML schema that encoded any possible result and an ApiRequestDocument schema which encoded any possible combination of arguments across all web methods.

/*
 * SubscriberAPISoapDocument.java
 *
 * Created on December 4, 2006, 2:59 PM
 *
 */

package com.shallRemainNameless.service;

/**
 *
 * @author breiter
 */

import com.shallRemainNameless.service.schemas.apiResult.ApiResultDocument;
import java.rmi.RemoteException;
import javax.xml.soap.SOAPException;
import org.apache.xmlbeans.XmlException;

public interface SubscriberAPISoapStrong extends java.rmi.Remote, SoapAuthentication 
{
    public ApiResultDocument createSubscriber(com.shallRemainNameless.service.schemas.subscriberCreateAndUpdate.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument updateSubscriber(com.shallRemainNameless.service.schemas.subscriberCreateAndUpdate.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument addCategory(com.shallRemainNameless.service.schemas.addRemoveCategory.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument removeCategory(com.shallRemainNameless.service.schemas.addRemoveCategory.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument getSubscriber(com.shallRemainNameless.service.schemas.getSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument getSubscriberData(com.shallRemainNameless.service.schemas.getSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument authenticateSubscriber(com.shallRemainNameless.service.schemas.authenticateSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument getSubscribers(com.shallRemainNameless.service.schemas.getSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument getSubscribersByField(com.shallRemainNameless.service.schemas.getSubscribersByField.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument deactivateSubscriber(com.shallRemainNameless.service.schemas.deactivateSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument activateSubscriber(com.shallRemainNameless.service.schemas.activateSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument sendOptInMessage(com.shallRemainNameless.service.schemas.sendOptInMessage.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
    public ApiResultDocument getRegistrationMetaData(com.shallRemainNameless.service.schemas.getSubscribers.ApiRequestDocument xmldoc) throws RemoteException, XmlException;
}

I remember that things got interesting when we created unit tests to confirm that the SOAP API did what it was supposed to do. There are 15 versions of unit tests checked in as we established that the documentation provided was not, in fact, based on the actual behavior of the system.

At least the designer of this API didn’t realize s/he could have just added an <ApiMethodRequest /> element to the ApiRequestDocument and used a single ApiResultDocument doApiRequest( ApiRequestDocument ) method. Oh wait, it would have been String doApiRequest( String ). That would have been epic.

Good times.

Custom JsonResult Class for ASP.Net MVC to Avoid MaxJsonLength Exceeded Exception

Shortly before Christmas, I was working on an application that sent a large data set to jqGrid using an Ajax JSON stream in ASP.Net MVC. We were trying out using a “get everything once” model where all of the I/O happens when jqGrid is initialized or reset and then all of the data is available on the client for fast sort and filter. It was working well with test data of ~5-6K rows until a colleague checked in a change that added a new  column. Suddenly my jqGrid was blank while hers (with a different, somewhat smaller, set of test data) was fine. Usually this sort of behavior from jqGrid indicates that the JSON was broken. Sure enough when I fired up my Fiddler HTTP debugger, I saw an error 500 for the JSON ajax query.


Server Error in ‘/’ Application.

Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.

Source Error: 


An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[InvalidOperationException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.]
   System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj, StringBuilder output, SerializationFormat serializationFormat) +551497
   System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj, SerializationFormat serializationFormat) +74
   System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj) +6
   System.Web.Mvc.JsonResult.ExecuteResult(ControllerContext context) +341
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +10
   System.Web.Mvc.<>c__DisplayClass14.<InvokeActionResultWithFilters>b__11() +20
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +251
   System.Web.Mvc.<>c__DisplayClass16.<InvokeActionResultWithFilters>b__13() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +178
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +314
   System.Web.Mvc.Controller.ExecuteCore() +105
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +39
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
   System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__4() +34
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +59
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +44
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +7
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8682542
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155


Version Information: Microsoft .NET Framework Version:2.0.50727.4952; ASP.NET Version:2.0.50727.4955

 

The error is inside of a call from MVC to BCL

It turns out that the exception happens inside of JsonResult.ExecuteResult(ControllerContext context). What is going on in there? Well, fortunately, ASP.Net MVC code is open source (MS-PL) and the .NET Framework class library source code is available for reference as well. Let’s take a look.

The meat of JsonResult.ExecuteResult(ControllerContext)

if (Data != null) {
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    response.Write(serializer.Serialize(Data));
}

The meat of JavaScriptSerializer.Serialize(object obj)

public string Serialize(object obj) {
    return Serialize(obj, SerializationFormat.JSON);
}

private string Serialize(object obj, SerializationFormat serializationFormat) {
    StringBuilder sb = new StringBuilder(); 
    Serialize(obj, sb, serializationFormat); 
    return sb.ToString();
}

internal void Serialize(object obj, StringBuilder output, SerializationFormat serializationFormat) {
    SerializeValue(obj, output, 0, null, serializationFormat); 
    // DevDiv Bugs 96574: Max JSON length does not apply when serializing to Javascript for ScriptDescriptors
    if (serializationFormat == SerializationFormat.JSON && output.Length > MaxJsonLength) {
        throw new InvalidOperationException(AtlasWeb.JSON_MaxJsonLengthExceeded);
    } 
}

JavaScriptSerializer completely serializes object obj into StringBuilder output and then, having allocated that memory, checks the size of the StringBuilder and if it is larger than the MaxJsonLength property it throws an InvalidOperationException. JsonResult just creates a new JavaScriptSerializer and uses it so there is no way to change the default MaxJsonLength when using JsonResult in MVC. Since the memory is allocated before the InvalidOperationException is thrown, I’m not really clear what the point of MaxJsonLength is this deep in the framework. Surely whatever is going to use the JSON string would be in a better position to decide if the string returned by JavaScriptSerializer.Serialize() was too long to use?

Anyway, we have the problem isolated now for a solution. We need to implement our own ActionResult that will generate JSON while allowing the caller to twiddle the knobs on JavaScriptSerializer.

LargeJsonResult ActionResult class

using System;
using System.Web.Script.Serialization;

namespace System.Web.Mvc
{
    public class LargeJsonResult : JsonResult
    {
        const string JsonRequest_GetNotAllowed = "This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.";
        public LargeJsonResult()
        {
            MaxJsonLength = 1024000;
            RecursionLimit = 100;
        }

        public int MaxJsonLength { get; set; }
        public int RecursionLimit { get; set; }

        public override void ExecuteResult( ControllerContext context )
        {
            if( context == null )
            {
                throw new ArgumentNullException( "context" );
            }
            if( JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
                String.Equals( context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase ) )
            {
                throw new InvalidOperationException( JsonRequest_GetNotAllowed );
            }

            HttpResponseBase response = context.HttpContext.Response;

            if( !String.IsNullOrEmpty( ContentType ) )
            {
                response.ContentType = ContentType;
            }
            else
            {
                response.ContentType = "application/json";
            }
            if( ContentEncoding != null )
            {
                response.ContentEncoding = ContentEncoding;
            }
            if( Data != null )
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer() { MaxJsonLength = MaxJsonLength, RecursionLimit = RecursionLimit };
                response.Write( serializer.Serialize( Data ) );
            }
        }
    }
}

You can use return new LargeJsonResult(){ Data = data } from any Action method where you would have used return Json(data). Also, you have direct control over the MaxJsonLength and RecursionLimit properites of JavaScriptSerializer.

return new LargeJsonResult() { Data = output, MaxJsonLength = int.MaxValue };
Follow

Get every new post delivered to your Inbox.

Join 84 other followers

%d bloggers like this: