Pregunta

Estoy tratando de analizar una matriz de objetos JSON en una matriz de cadenas en C #. Puedo extraer la matriz del objeto JSON, pero no puedo dividir la cadena de la matriz en una matriz de objetos individuales.

Lo que tengo es esta cadena de prueba:

string json = "{items:[{id:0,name:\"Lorem Ipsum\"},{id:1,name" 
            + ":\"Lorem Ipsum\"},{id:2,name:\"Lorem Ipsum\"}]}";

Ahora mismo estoy usando las siguientes expresiones regulares para dividir los elementos en objetos individuales. Por ahora son 2 expresiones regulares separadas hasta que soluciono el problema con la segunda:

Regex arrayFinder = new Regex(@"\{items:\[(?<items>[^\]]*)\]\}"
                                 , RegexOptions.ExplicitCapture);
Regex arrayParser = new Regex(@"((?<items>\{[^\}]\}),?)+"
                                 , RegexOptions.ExplicitCapture);

El regex arrayFinder funciona de la manera que lo esperaba pero, por razones que no entiendo, el regex arrayParser no funciona en absoluto. Lo único que quiero que haga es dividir los elementos individuales en sus propias cadenas, así que obtengo una lista como esta:

  

{id: 0, name: " Lorem Ipsum "}
   {id: 1, name: " Lorem Ipsum "}
   {id: 2, name: " Lorem Ipsum "}

Si esta lista es una matriz de string [] o una colección de Group o Match no importa, pero estoy desconcertado como a cómo conseguir los objetos divididos. Usando el arrayParser y la json declarados anteriormente, probé este código que asumí que no funcionaría sin suerte:

string json = "{items:[{id:0,name:\"Lorem Ipsum\"},{id:1,name" 
            + ":\"Lorem Ipsum\"},{id:2,name:\"Lorem Ipsum\"}]}";

Regex arrayFinder = new Regex(@"\{items:\[(?<items>[^\]]*)\]\}"
                                 , RegexOptions.ExplicitCapture);
Regex arrayParser = new Regex(@"((?<items>\{[^\}]\}),?)+"
                                 , RegexOptions.ExplicitCapture);

string array = arrayFinder.Match(json).Groups["items"].Value;
// At this point the 'array' variable contains: 
// {id:0,name:"Lorem Ipsum"},{id:1,name:"Lorem Ipsum"},{id:2,name:"Lorem Ipsum"}

// I would have expected one of these 2 lines to return 
// the array of matches I'm looking for
CaptureCollection c = arrayParser.Match(array).Captures;
GroupCollection g = arrayParser.Match(array).Groups;

¿Alguien puede ver qué es lo que estoy haciendo mal? Estoy totalmente atascado en esto.

¿Fue útil?

Solución

Los paréntesis equilibrados son, literalmente, un ejemplo de libro de texto de un idioma que no se puede procesar con expresiones regulares. JSON es esencialmente paréntesis equilibrados más un montón de otras cosas, con las llaves reemplazadas por parens. En la jerarquía de lenguajes formales , JSON es un lenguaje libre de contexto. Las expresiones regulares no pueden analizar lenguajes libres de contexto.

Algunos sistemas ofrecen extensiones a las expresiones regulares que tipo-sorta manejan expresiones balanceadas. Sin embargo, todos son hacks feos, todos son imposibles de transportar y, en última instancia, son la herramienta incorrecta para el trabajo.

En el trabajo profesional, casi siempre usaría un analizador JSON existente. Si desea rodar el suyo con fines educativos, sugiero comenzar con una gramática aritmética simple que admita + - * / (). (JSON tiene algunas reglas de escape que, aunque no son complejas, harán que su primer intento sea más difícil de lo que debe ser). Básicamente, deberá:

  1. Descomponer el lenguaje en un alfabeto de símbolos
  2. Escriba una gramática libre de contexto en términos de esos símbolos que reconocen el lenguaje
  3. Convierta la gramática a la forma normal de Chomsky, o lo suficientemente cerca para hacer el paso 5 fácil
  4. Escriba un lexer que convierta el texto en bruto en su alfabeto de entrada
  5. Escriba un analizador de descenso recursivo que tome la salida de su lexer, la analice y produzca algún tipo de salida

Esta es una tarea típica de CS de tercer año en casi cualquier universidad.

El siguiente paso es descubrir qué tan compleja es la cadena JSON que necesitas para desencadenar un desbordamiento de pila en tu analizador recursivo. Luego mire los otros tipos de analizadores que se pueden escribir, y comprenderá por qué cualquier persona que tenga que analizar un lenguaje libre de contexto en el mundo real usa una herramienta como yacc o antlr en lugar de escribir un analizador a mano.

Si aprendes más de lo que estabas buscando, no dudes en utilizar un analizador JSON disponible, satisfecho de que hayas aprendido algo importante y útil: los límites de las expresiones regulares.

Otros consejos

  

Los paréntesis equilibrados son, literalmente, un ejemplo de libro de texto de un idioma que no se puede procesar con expresiones regulares

bla bla bla ... mira esto:

arrayParser = "(?<Key>[\w]+)":"?(?<Value>([\s\w\d\.\\\-/:_]+(,[,\s\w\d\.\\\-/:_]+)?)+)"?

esto me funciona

si desea hacer coincidir los valores vacíos, cambie el último '+' a '*'

¿Estás utilizando .NET 3.5? Si es así, puede usar el DataContractJsonSerializer para analizar esto. No hay razón para hacerlo tú mismo.

Si no está utilizando .NET 3.5, puede usar Jayrock .

public Dictionary<string, string> ParseJSON(string s)
{
    Regex r = new Regex("\"(?<Key>[\\w]*)\":\"?(?<Value>([\\s\\w\\d\\.\\\\\\-/:_\\+]+(,[,\\s\\w\\d\\.\\\\\\-/:_\\+]*)?)*)\"?");
    MatchCollection mc = r.Matches(s);

    Dictionary<string, string> json = new Dictionary<string, string>();

    foreach (Match k in mc)
    {
        json.Add(k.Groups["Key"].Value, k.Groups["Value"].Value);

    }
    return json;
}

Esta función implementa la expresión regular de Lukasz. Solo agrego para incluir + char al grupo de valores (porque lo uso para analizar el token de autenticación de Live Connect)

Por lo general,

JSON no se puede analizar con expresiones regulares (ciertas variantes extremadamente simplificadas de JSON pueden, pero luego no son JSON sino otra cosa).

Necesitas un analizador real para analizar JSON correctamente.

Y de todos modos, ¿por qué estás tratando de analizar JSON en absoluto? Hay numerosas bibliotecas por ahí que pueden hacerlo por usted, y mucho mejor de lo que lo haría su código. ¿Por qué reinventar la rueda, cuando hay una fábrica de ruedas a la vuelta de la esquina con las palabras FOSS sobre la puerta?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top