Ardjson: https://ardjson.codeplex.com

Previous:Ardjson Part 4: DoItems App on Arduino


Code now on Codeplex: https://Ardjson.codeplex.com  as 1.3a jsonToDoArduinoProjects.zip

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.


1. Pretty print

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        
        }

 

2. Simple JSon Parser

This is in two parts: (i) Separate the records (ii) For each record parse it into its fields:

In the first program:

(i) Separate records

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;
			}
		}
	}

 

 

Parse each record:

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:

  1. Just display all of the HTTP response
  2. Only display the tenth line of the response, the JSon string.
  3. As per 2 but replace each comma in the string with CR LF so each record is on a separate line.
  4. As per 2 but call simple JSon parser after request is received.

Discussion

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