How to use ServiceStack.Text for Json deserialization in ASP.NET MVC ValueProvider

288 views Asked by At

How can I use ServiceStack.Text Json serializer for deserializing strings in ASP.NET MVC request during value binding for the controller method parameters?

1

There are 1 answers

0
Sumrak On

I ended up having to write custom class. To use the factory below update your Application_Start with:

ValueProviderFactories.Factories.Remove( ValueProviderFactories.Factories.OfType< JsonValueProviderFactory >().FirstOrDefault() );
ValueProviderFactories.Factories.Add( new JsonServiceStackValueProviderFactory() )

Here's what worked for me:

public sealed class JsonServiceStackValueProviderFactory: ValueProviderFactory
{
    public override IValueProvider GetValueProvider( ControllerContext controllerContext )
    {
        if( controllerContext == null )
            throw new ArgumentNullException( "controllerContext" );

        var jsonData = GetDeserializedObject( controllerContext );
        if( jsonData == null )
            return null;

        var backingStore = new Dictionary< string, object >( StringComparer.OrdinalIgnoreCase );
        AddToBackingStore( backingStore, String.Empty, jsonData );
        return new DictionaryValueProvider< object >( backingStore, CultureInfo.CurrentCulture );
    }

    private static object GetDeserializedObject( ControllerContext controllerContext )
    {
        if( !controllerContext.HttpContext.Request.ContentType.StartsWith( "application/json", StringComparison.OrdinalIgnoreCase ) )
        {
            // not JSON request
            return null;
        }

        var reader = new StreamReader( controllerContext.HttpContext.Request.InputStream );
        var bodyText = reader.ReadToEnd();
        if( String.IsNullOrEmpty( bodyText ) )
        {
            // no JSON data
            return null;
        }

        var firstNonEmptyChar = GetFirstNonEmptyChar( bodyText );

        if( firstNonEmptyChar == '[' )
        {
            var jsonData = JsonSerializer.DeserializeFromString< List< Dictionary< string, object > > >( bodyText );
            return jsonData;
        }
        else
        {
            var jsonData = JsonSerializer.DeserializeFromString< Dictionary< string, object > >( bodyText );
            return jsonData;
        }
    }

    private static void AddToBackingStore( Dictionary< string, object > backingStore, string prefix, object value )
    {
        var d = value as IDictionary< string, object >;
        if( d != null )
        {
            foreach( var entry in d )
            {
                AddToBackingStore( backingStore, MakePropertyKey( prefix, entry.Key ), entry.Value );
            }
            return;
        }

        var l = value as IList;
        if( l != null )
        {
            for( var i = 0; i < l.Count; i++ )
            {
                AddToBackingStore( backingStore, MakeArrayKey( prefix, i ), l[ i ] );
            }
            return;
        }

        // primitive
        backingStore[ prefix ] = value;
    }

    private static string MakeArrayKey( string prefix, int index )
    {
        return prefix + "[" + index.ToString( CultureInfo.InvariantCulture ) + "]";
    }

    private static string MakePropertyKey( string prefix, string propertyName )
    {
        return ( String.IsNullOrEmpty( prefix ) ) ? propertyName : prefix + "." + propertyName;
    }

    private static char? GetFirstNonEmptyChar( string @string )
    {
        for( var i = 0; i < @string.Length; i++ )
        {
            if( !char.IsWhiteSpace( @string[ i ] ) )
                return @string[ i ];
        }
        return new char?();
    }
}