Ardjson: https://ardjson.codeplex.com
Previous:Ardjson Part 4: DoItems App on Arduino
In the previous blog three apps were presented that run on an Arduino board that mimick the functionality of the ToDoItems Universal Microsoft Mobile Services app. Each app exhibits one of the UI functions for the Universal app. Whilst the Universal app runs one Windows 8.1 desktop and phone, the Arduino apps perform the same functions with the same backend Mobile Service data. This first of the Arduino apps requests the current active (complete=false) tasks from the service and just displays the HTTP response, without any interpretation. This blog presents a simple JSon parser in the Arduino context that extracts the data entities from each record. in the JSon string.
1. A simple first step is to to "Pretty Print" the JSon string such that each JSon record string is on a separate line
2. The second step is parse each record.
This is simple to to implement: In activity 1 in read_response, append CR LF to every close brace. This will print each record on a separate line:
//In read_response replace (App 1) if (numline == 0 ) { //Capture the 10th line in the response //To do: Could be more deterministic about this: // Expect certain content, checks and balances etc. jsonBuffer[jsonBufferCntr++] = c; jsonBuffer[jsonBufferCntr] = '\0'; } //with if (numline == 0) { //Capture the 10th line in the response //To do: Could be more deterministic about this: // Expect certain content, checks and balances etc. jsonBuffer[jsonBufferCntr++] = c; if (true) { if (c == '') { // JSon array starts with // Put first record on newline jsonBuffer[jsonBufferCntr++] = '\r'; jsonBuffer[jsonBufferCntr++] = '\n'; } else if (c == '}') { //JSon record end with } //Put each record on a new line jsonBuffer[jsonBufferCntr++] = '\r'; jsonBuffer[jsonBufferCntr++] = '\n'; } } jsonBuffer[jsonBufferCntr] = '\0'; //So that buffer is always a null term string }
This is in two parts: (i) Separate the records (ii) For each record parse it into its fields:
In the first program:
In read_response( ) after the Json string has been extracted (after the while loop) insert the following:
//Separate the string on commas then parse record jsonBufferCntr = 0; recordBufferCntr = 0; done = false; //Expect first char of Json array to be if (jsonBuffer[jsonBufferCntr] == '') { jsonBufferCntr++; //Skip first and last chars as [ and ] jsonStringLength = strlen(jsonBuffer); recordNo = 0; //Expect Json array to finish with while ((jsonBuffer[jsonBufferCntr] != '') && (!done)) { //Extract each record from array //Json object (record) starts with open brace if (jsonBuffer[jsonBufferCntr] == '{') { recordBufferCntr = 0; recordNo++; //Json object (record) starts with open brace recordBuffer[recordBufferCntr++] = '{'; recordBuffer[recordBufferCntr] = '\0'; jsonBufferCntr += 1; //Consume { //Json object (record) ends with closing brace while (jsonBuffer[jsonBufferCntr] != '}') { recordBuffer[recordBufferCntr++] = jsonBuffer[jsonBufferCntr++]; recordBuffer[recordBufferCntr] = '\0'; } jsonBufferCntr++; //Consume } } recordBuffer[recordBufferCntr++] = '}'; recordBuffer[recordBufferCntr++] = '\0'; //Now have one complete Json object string to parse Serial.println(" "); Serial.print("Record: "); Serial.println(recordNo); Serial.println(recordBuffer); delay(1000); //Parse the object string parseJasonRecord(); //Expect , in the Json array string to separate records if (jsonBuffer[jsonBufferCntr] == ',') { jsonBufferCntr++; } else { //Otherwise must be at the array ending that is finished. done = true; } } }
Insert the following function above read_response
/* ** Get name string value pair from record ** Format of string: ** {"name":"string value"} ** Assumes ** Limitations: Assumes no spaces except inside strings ** No error checking ** Fixed buffer size, limits number of records */ void parseJasonRecord() { int jsonBufferCntr=0; int buffLen; boolean done= false; boolean isStringValue=false; //Record opens with openning brace and closes with closing brace while ((recordBuffer[jsonBufferCntr] != '}') && (!done)) { //Expect openning brace if (recordBuffer[jsonBufferCntr]=='{') { jsonBufferCntr +=1; //Skip { char name[64]; char strVal[64]; //Each name value pair is comma separated,,but but last will be terminated by closing brace while ((recordBuffer[jsonBufferCntr] != ',') && (recordBuffer[jsonBufferCntr] != '}') && (!done)) { int cntr=0; //Skip " jsonBufferCntr++; //Name value pairs are colon separated while (recordBuffer[jsonBufferCntr] != ':') { name[cntr++] =recordBuffer[jsonBufferCntr++]; name[cntr] = '\0'; //Serial.println(name); } name[cntr-1]= '\0'; //Overwrite " on end cntr=0; jsonBufferCntr++;//Skip : if (recordBuffer[jsonBufferCntr] =='"') { isStringValue = true; jsonBufferCntr++;//Skip " } else isStringValue=false; while ((recordBuffer[jsonBufferCntr] != ',') && (recordBuffer[jsonBufferCntr] != '}')) { strVal[cntr++] =recordBuffer[jsonBufferCntr++]; strVal[cntr]='\0'; //Serial.println(strVal); } //If is a string value then reduce string by one char on end if (isStringValue) strVal[cntr-1]='\0'; //Overwrite " on end if (recordBuffer[jsonBufferCntr] == ',') jsonBufferCntr++; //Skip , else done = true; Serial.print(name); Serial.print(" : "); if (isStringValue) Serial.print("\""); Serial.print(strVal); if (isStringValue) Serial.print("\""); Serial.println(); } } } }
See the project in Ardjson JSonToDoGetIncomplete for details. This has options for output:
Because fixed buffer sizes are used, the number of records that can be handled is limited to a small number. If the response string is too long, the app crashes and restarts. A better solution would be to process the JSon parsing "on-th-fly", that is as a stream. This will be covered in a later blog.
Next: Arduino version of Telemetry Microsoft Mobile Service App