@ agnasg

No hay nada que una buena tasa de café no pueda resolver

04-02-2017 4:13 AM

Estaba tratando de probar jsoncpp para evaluar unas expresiones json muy simples y me encontré con que no funcionaba. El programa era algo así como lo siguiente:

 string json_example = "{\"arg1\": \"val1\", \"arg1\": \"val2\"}";
 Json::Reader reader;
 bool parsedSuccess = reader.parse(json_example, root, false);
 
 if(!parsedSuccess) {
 cout << "Failed to parse JSON" << endl << reader.getFormattedErrorMessages() <<endl;
 return 1;
 }
 // Iterate over sequence elements 
 for (Json::ValueIterator itr = root.begin() ; itr != root.end() ; itr++) {
 cout << itr.key().asString().c_str() << " : " << (*itr).asString().c_str() << endl; 
 }

El resultado era

arg1 : val1

La entrada no podía ser más simple:

{“arg1” : “val1”, “arg1” : “val2”}

Estuve a punto de abandonarlo y hacer mi propio parser hasta que comencé a hacer recursión, cambiar la entrada, probar otras formas de navegar el árbol, etc. ¿Cuál era el problema? Si la entrada json tiene claves repetidas el parser ignora las primeras y toma la última. Fijese que “arg1” está repetido. ¿Interesante, verdad?
Me recordó un curso que dicté hace muchos años sobre awk. El curso estaba basado en un manual escrito por mi. En algún momento  dejé solos a los participantes del curso para que hicieran un ejercicio que consistía en manipular strings. Claro para manipular  strings requieres la función de awk que devuelve la longitud del string, que en el caso de awk es length (). Pero… en awk si una función está mal escrita no produce error sintático alguno, simplemente no hace nada. Encontré a los participantes del curso halándose los cabellos porque en el manual yo había escrito “lenght ()” en lugar de “length ()”. Ups… Pero tengo algo en mi favor y es que el inglés tiene esta peculiaridad de tener palabras como strength y length que terminan en “gth” y palabras como bright y bought que terminan en “ght”. Lo cual explica mi error, o casi lo explica. De hecho en mi escritorio tengo unas notas para recordar cómo se escriben algunas palabras, por ejemplo las célebres though, through y thought.

Hora de tomarme una tasa de café.

EDIT: Descarté jsoncpp porque yo no requiero evaluar complicadas expresiones json dentro de psyblast sino simplemente:

{“arg1” : “val1”, “arg2” : “val2”}

Así que me hice mi propio parser de json:

// Solo funciona para expresiones simples {"a" : "v1", "b" : "v2" }
 std::map<std::string,std::string> json_decode (std::string json)
{
	static std::map<std::string,std::string> result;
	int parent = 0;
	int elem = 0;
	int iskey = 1;
	std::string key, value;

	// Iterate over sequence elements and 
	for (std::string::iterator itr = json.begin() ; itr != json.end() ; itr++) {
		switch (*itr) {
                case '{':
			parent++;
			break;
		case '}':
			parent--;
			break;
		case '"':
			if (elem) {
				elem--;
				// falling down	
			} else {
				elem++;
				break;
			}
			// falling down
		case ':':
			iskey = 0;
			break;
		case ',':
			result[key] = value;
			iskey = 1;
			key.erase (key.begin (), key.end ());
			value.erase(value.begin (), value.end ());
			break;
		case ' ' : case '\t':
			if (!elem) { // if not in an element skip it
				break;
			}
			// falling down
		default:
			if (iskey) {
				key += *itr;
			} else {
				value += *itr;
			}
			break;
		}
	}
	result[key] = value;

	return result;
}

EDIT: corregí varios errores en el parsing de forma que ahora permite que el valor pueda contener espacios en blanco, newline, etc.