Below is most of the code for the JSON Parser state machine function..
#include "stdafx.h" #include "jsonparser.h" //For working out error source in code: int ErrNo = -1; ///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// /// Json Parse code /// Parses JSon string both for records and name value pairs within records /// Parser parses a stream and is implemented as a State machine // Parser state machine states: enum Expecting { ... ... }; void IncrementState() { ... ... } BOOL Expect(char c) { ... ... } //The current state of the State Machine Expecting parseState = startOfArray; //Assume by default getting an array, ie starts with //If starts with { then just getting one record so jump to that mode and done one that record is interpretted. BOOL gGettingArray = true; NameValue CurrentNameValue; /* Parses a JSon array of records of name value pairs */ BOOL ParseJsonString(char c) { //If result is false then subsequent parses are aborted BOOL result = true; ErrNo = -1; int strnlength; // Signal to format print a name-vale pair after switch statement BOOL gotANameValuePair = false; switch (parseState) { case startOfArray: result = Expect(c); if (result) { //Start of array so no records yet. RecordNo = 0; output.println(F("\r\n1: Starting parse of array.")); } //If the string starts with { just getting one record rather than an array if (parseState == startOfName) { RecordNo++; //Start of new record so no no name value pairs yet NameValueIndex = 0; output.println(F("\r\n2: New record.")); gGettingArray = false; } break; case startOfRecord: result = Expect(c); if (result) { RecordNo++; //Start of new record so no no name value pairs yet NameValueIndex = 0; output.println(F("\r\n2: New record.")); } break; case startOfName: result = Expect(c); if (result) { NameValueIndex++; int index = REC_NO(RecordNo - 1, NameValueIndex - 1); if (1 == 0) //(index >= MAX_NO_NAME_VALUE_PAIRS) { output.println(F("\r\n2: Max No of Name-value pairs exceeded")); ErrNo = 11; NameValueIndex--; RecordNo--; result = false; } else { //output.println(F("3: New record Name-Value Pair.")); strcpy(CurrentNameValue.Name, ""); strcpy(CurrentNameValue.StringValue, ""); CurrentNameValue.BooleanValue = false; CurrentNameValue.IntegerValue = 0; CurrentNameValue.FloatValue = 0; CurrentNameValue.DType = tUnknown; } } break; case gettingName: if (c != '\"') { //output.println(F("4: Getting Name-Value Name.")); //Append to name strnlength = strlen(CurrentNameValue.Name); if (strnlength == 0) { if ((isalpha(c)) || (c == '_')) //Can lead only with alpha and _ { strnlength = strlen(CurrentNameValue.Name); CurrentNameValue.Name[strnlength] = c; CurrentNameValue.Name[strnlength + 1] = '\0'; } else { result = false; ErrNo = 101; } } else if ((isalpha(c)) || (c == '_') || (isdigit(c))) //Can use _ or alpha in name { strnlength = strlen(CurrentNameValue.Name); CurrentNameValue.Name[strnlength] = c; CurrentNameValue.Name[strnlength + 1] = '\0'; } else if (parseState != done) { result = false; ErrNo = 1; } } else { //Got end of name IncrementState(); } break; case nameValueSeparator: result = Expect(c); break; case startOfValue: //output.println(F("Getting value.")); CurrentNameValue.StringValue[0] = '\0'; switch (c) { case '\"': parseState = gettingString; break; case 'T': parseState = gettingBoolean; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; break; case 't': parseState = gettingBoolean; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; break; case 'F': parseState = gettingBoolean; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; break; case 'f': parseState = gettingBoolean; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; break; case 'N': parseState = gettingNull; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; break; case 'n': parseState = gettingNull; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; break; default: if (isdigit(c)) { parseState = gettingInteger; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; } else { ErrNo = 2; result = false; } break; } break; case gettingString: if (c == '\"') { gotANameValuePair = true; CurrentNameValue.DType = tString; parseState = gettingEndOfValueORRecord; } else { //Added these brackets 5/4/15 strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; } break; case gettingBoolean: if ((c == '}') || (c == ',')) { if (c == '}') parseState = gotEndOfRecord; else { //Comma, so start new name-value pair parseState = startOfName; } int i = -1; while (CurrentNameValue.StringValue[++i]) { CurrentNameValue.StringValue[i] = tolower(CurrentNameValue.StringValue[i]); } if (strcmp(CurrentNameValue.StringValue, "false") == 0) { CurrentNameValue.BooleanValue = false; CurrentNameValue.DType = tBoolean; gotANameValuePair = true; } else if (strcmp(CurrentNameValue.StringValue, "true") == 0) { CurrentNameValue.BooleanValue = true; CurrentNameValue.DType = tBoolean; gotANameValuePair = true; } else { ErrNo = 3; result = false; } } else { strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; } break; case gettingNull: if ((c == '}') || (c == ',')) { if (c == '}') parseState = gotEndOfRecord; else { //Comma, so start new name-value pair parseState = startOfName; } int i = 0; while (CurrentNameValue.StringValue[i]) { CurrentNameValue.StringValue[i] = tolower(CurrentNameValue.StringValue[i++]); } if (strcmp(CurrentNameValue.StringValue, "null") == 0) { CurrentNameValue.BooleanValue = false; CurrentNameValue.DType = tNull; gotANameValuePair = true; } else { ErrNo = 4; result = false; } } else { strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; } break; case gettingInteger: if ((c == '}') || (c == ',')) { int val = atoi(CurrentNameValue.StringValue); CurrentNameValue.IntegerValue = val; CurrentNameValue.DType = tInteger; gotANameValuePair = true; if (c == '}') { parseState = gotEndOfRecord; } else //Comma, so start new name-value pair parseState = startOfName; } else if (c == '.') { parseState = gettingFloat; strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; } else if (isdigit(c)) { strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; } else { ErrNo = 5; result = false; } break; case gettingFloat: if ((c == '}') || (c == ',')) { //Parse number CurrentNameValue.FloatValue = (float) atof(CurrentNameValue.StringValue); CurrentNameValue.DType = tFloat; gotANameValuePair = true; if (c == '}') { parseState = gotEndOfRecord; } else //Comma, so start new name-value pair parseState = startOfName; } else if (isdigit(c)) { strnlength = strlen(CurrentNameValue.StringValue); CurrentNameValue.StringValue[strnlength] = c; CurrentNameValue.StringValue[strnlength + 1] = '\0'; } else { ErrNo = 6; result = false; } break; case gettingEndOfValueORRecord: //PREV: gettingString //This state is only taken (as next state) after the end of a string literal if ((c == '}') || (c == ',')) { if (c == '}') { parseState = gotEndOfRecord; if (!gGettingArray) { output.println(F("End of Record.")); output.println(F("End of Array.\r\n")); parseState = done; } } else { parseState = startOfName; } } else { ErrNo = 7; result = false; } break; case gotEndOfRecord: // PREV : getting<value type except string> or gettingEndOfValueORRecord // After a record can get comma which means more name-values to come // Or which means at end of array and string if (c == ',') { //Got comma so expect another record parseState = startOfRecord; output.println(F("End of Record.")); } else if (c == '') { //Got so end of array. We are done output.println(F("End of Record.")); output.println(F("End of Array.\r\n")); parseState = done; } else { ErrNo = 8; result = false; } break; default: { ErrNo = 9; result = false; } } if ((gotANameValuePair) && (result)) { if (RecordNo == 1) numNameValuePairsPerRecord = NameValueIndex; AddNameValue(CurrentNameValue, RecordNo, NameValueIndex - 1); } else if (!result) { // If any errors then signal halt to state machine. parseState = error; output.print(F("An error has occured. ErrNo:")); output.println(ErrNo); output.println(CurrentNameValue.StringValue); output.print(F("StringValue:")); CurrentNameValue.StringValue[8] = '\0'; output.println(CurrentNameValue.StringValue);*/ delay(2000); if (ErrNo == 11) { output.println(F("Couldn't parse all name-value pairs")); } } return result; }