Dynamic JSON Parser

abril 10, 2009
By

Estuve buscando en Internet algún framework que dado un Stream o un String con formato JSON, me devolviera una jerarquía de objetos en .NET que mapeara la estructura JSON descrita en el String de forma tal que fuese sencillo su utilización. Encontré algunas librerías:

  • JavaScriptSerializer in System.Web.Extensions
  • DataContractJsonSerializer in System.Runtime.Serialization.Web
  • AjaxPro

Sin embargo, ninguna de las librerías anteriores me sirvió debido a que necesito interpretar un JSON arbitrario, del cual no conozco su estructura. Es aquí donde aparece el problema. El CLR está orientado a lenguajes estáticamente tipados, por lo que resulta imposible (a menos sin utilizar Reflection) la creación de una jerarquía de tipos que mapee el string JSON. Mas adelante encontré el siguiente artículo:

  • C# 4.0, Dynamic Programming and JSON

    http://www.nikhilk.net/CSharp-Dynamic-Programming-JSON.aspx

    !Viva C# 4.0! Exactamente eso es lo que estaba buscando. Lamentablemente, no puedo esperar a la salida de la plataforma .NET 4.0, así que tengo que buscar una solución con las herramientas actuales que están en producción. Volviendo a la búsqueda apareció la siguiente librería:

    JsonFx.Net

    http://jsonfx.net/download/

    Esa librería (bajo licencia MIT, Software Libre) me resolvió el problema. El mapeo que realiza es el siguiente:

    JSON .NET
    object {..} Dictionary<String,T>
    array [..] T[]
    value value type (int, bool, etc)

    Me llamó la atención la inferencia de tipos que realiza JsonFx para T. Por ejemplo, si en JSON se define un arreglo de cadenas, en .NET se obtiene el tipo System.String[], pero si se define un arreglo con múltiples tipos, se obtiene entonces System.Object[].

    ¿Que pasaría si tuviésemos que hacer nuestro propio Parser?

    Por suerte para mí, apareció JsonFx.Net, pero ¿qué pasaría si no hubiese encontrado ninguna solución? Para aquellos que estén en esa situación he escrito este Post, con algunas ideas de cómo yo le daría solución a esa problemática.

    En el sitio http://www.json.org/ está publicada la gramática, incluso diagramas de los autómatas que reconocen la sintaxis. De forma general el procedimiento sería el siguiente:

    1. Desarrollar un Lexer + Parser
    2. Convertir el AST resultante en una jerarquía de objetos útiles (Diccionarios, Arreglos, etc) en .NET

    El desarrollo de los puntos anteriores puede parecer complejo, sin embargo, conociendo un poco de compilación y utilizando las herramientas correctas puede implementarse sin mucho trabajo. Las variantes son las siguientes:

    1. ANTLR
    2. Microsoft DLR (Dynamic Language Runtime)

    El ANTLR es una herramienta que puede utilizarse para generar compiladores para gramáticas LL(*) para el desarrollo tanto de pequeños DSL (Domain Specific Language) como el de lenguajes complejos como C, C#, Java entre otros. (Ver el libro “The Definitive ANTLR Reference: Building Domain Specific Languages”)

    La segunda variante, Microsoft DLR (Dynamic Language Runtime) permite el desarrollo de lenguajes dinámicos (como por ejemeplo Python y Ruby) pero como característica singular permite la interoperación con el CLR. En el caso de JSON, al ser un DSL dinámico, el DLR es uno de los candidatos a valorar. (Ver los artículos “Create your own language on top of the DLR” de Lionel Laské y “Jim Hugunin’s Thinking Dynamic”)

    Para cualquiera de los dos casos es válido el estudio de qué son gramáticas LL(k)/LL(*) atributadas. Por ejemplo, imaginemos que queremos parsear la siguiente línea:

    {“Texto”:35}

    El objeto equivalente en C# podría construirse de la siguiente forma:

    Dictionary<String, Object> resultado = new Dictionary<String, Object>();
    resultado.Add("Texto",35);

    Veamos un fragmento (ANTLR) de la gramática atributada LL(1) necesaria para parsear la línea de ejemplo:

    dictionary returns [Dictionary<String,Object> result]:

    {result = new Dictionary<String,Object>();}

    ‘{‘ dictionaryItem[$result] (‘,’ dictionaryItem[$result])* ‘}’

    ;

    dictionaryItem [Dictionary<String,Object> currentDictionary]

    :key=STRING ‘:’

    (strValue=STRING {currentDictionary.Add(key.Text,strValue.Text);}

    |nmbrValue=NUMBER {currentDictionary.Add(key.Text,int.Parse(nmbrValue.Text));}

    |dictValue=dictionary {currentDictionary.Add(key.Text,dictValue.result);})

    ;

    El código marrón representa la definición de tokens del parser. El azúl, tokens del Lexer y el código en verde representa código C# que se ejecuta en el momento que el parser LL alcanza la posición en que está ubicado dicho código. Note que en el proceso de compilación, cuando se encuentra un “{” se crea un diccionario que se devuelve como atributo de retorno para ser utilizado por el AST padre (o retorno del proceso de compilación). Como el contenido entre llaves representan elementos de diccionario, pues se llama a la regla dictionaryItem pasándole como atributo la referencia del diccionario en que deben agregarse los elementos. La regla dictionaryItem a su vez diferencia entre los tipos que pueden aparecer en la parte del “valor” del elemento de diccionario y llama a las respectivas reglas que interpretan ese contenido. Note que en esta pequeña gramática de ejemplo (no se implementa JSON completamente) se permite la definición de diccionarios anidados. Ejemplo:

    {“Texto A”:{“Texto B”:35,”Texto C”:”Texto D”}}

    Lo interesante del uso de ANTLR para este ejemplo es que el trabajo que tenemos que pasar es la escritura correcta de la gramática. Una vez asegurado esto, el ANTLR nos genera el código C# del “compilador” y solamente tenemos que “utilizarlo” tal y como se generó. Ejemplo:

    ANTLRStringStream input = new ANTLRStringStream(query);
    JSONGrammarLexer lex = new JSONGrammarLexer(input);
    CommonTokenStream tokens = new CommonTokenStream(lex);
    JSONGrammarParser parser = new JSONGrammarParser(tokens);
    Dictionary<String,Object> jsonObject = parser.dictionary().result;

    Las clases JSONGrammarLexer y JSONGrammarParser son generadas por ANTLR. En el objeto jsonObject se obtiene el mapeo que deseábamos (por supuesto, si no ocurre ningún error sintáctico o semántico en el proceso de compilación).

    Utilizando el Microsoft DLR, de forma similar puede resolverse este problema, sin embargo, no voy a detenerme en como utilizar el DLR debido a que el ejemplo que se muestra en el artículo “Create your own language on top of the DLR” está lo suficientemente completo como para publicar aquí una vaga imitación.

  • Tags: , , , ,

    2 Responses to Dynamic JSON Parser

    1. Oskr! on junio 2, 2009 at 9:29 pm

      Justo en este momento necesito hacer un parser de JSON usando gramaticas y cosas asi y tu post me he llegado de maravilla!!!!

    2. Jose Bermudez on febrero 3, 2014 at 11:28 am

      Hola Amigo quiero desarrollar un launcher pero que me descargue los archivos desde internet pero quiero hacer el porgreess con json me podrias ayudar

    Deja un comentario

    Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

    *

    Acerca del autor...

    Alejandro Tamayo

    Web: http://www.linkedin.com/in/atamayocastillo
    Alejandro Tamayo
    Professor, Researcher, Developer, Consultant and technology enthusiast. Master of Science (MSc) in Computer Science and member of Weboo Research Group.Leer completo