/* * JSONparseCore.cpp * * Created on: 5 Mar 2017 * Author: wentzelc */ // redA Libraries #include "JSONparseCore.h" // Standard C/C++ Libraries #include #include #include #include #include //--------------------------------------------------------------------------- CJSONparse::CJSONparse( CDataTree * pDataTree ) { // Object tree DataTree = pDataTree; // File Operation InputHandle = -1; OutputHandle = -1; Buffer = NULL; BufEnd = NULL; Level = 0; // Parsing operation BufPos = NULL; LineMark = NULL; LineNo = 0; // Printing operation Spacer[0] = 0; SpacerLen = 0; // Error reporting Error = false; ErrorText[0] = 0; } //--------------------------------------------------------------------------- CJSONparse::~CJSONparse() { // Destroy buffer if (Buffer) { delete Buffer; } } //--------------------------------------------------------------------------- bool CJSONparse::LoadFromFile( const char * FilePath, int pBufLen ) { TDataMember * RootObject = NULL; // Clear Error Error = false; // Validate if (!FilePath || !FilePath[0]) { Error = true; sprintf( ErrorText, "No File path specified" ); return false; } // Open file if (!(InputHandle = open( FilePath, O_RDONLY ))) { Error = true; sprintf( ErrorText, "Could not open file" ); return false; } // Load Buffer CreateBuffer( pBufLen ); if (!FillBuffer()) { Error = true; sprintf( ErrorText, "Could not read from file" ); FreeBuffer(); return false; } // Reset values LineNo = 1; Error = false; // Create Root object DataTree->DeleteAll(); RootObject = DataTree->GetRootMember(); Level = 0; // Parse Root Object SkipWhiteSpace(); if (!ParseObject( RootObject )) { if (!Error) { Error = true; sprintf( ErrorText, "First entry in file must be an Object on line %d:%ld", LineNo, BufPos-LineMark ); } FreeBuffer(); return false; } // Ensure remainder of file is empty SkipWhiteSpace(); if (*BufPos != 0) { Error = true; sprintf( ErrorText, "No content expected after Root object on line %d:%ld", LineNo, BufPos-LineMark ); FreeBuffer(); return false; } // Success close( InputHandle ); InputHandle = -1; FreeBuffer(); return true; } //--------------------------------------------------------------------------- bool CJSONparse::CreateBuffer( int pBufLen ) { // Validate if (!pBufLen) return false; // Create buffer Buffer = new CShiftBuffer( pBufLen ); // Reset markers Buffer->PeekDirect( &BufPos, 0 ); BufEnd = BufPos; LineMark = BufPos; return true; } //--------------------------------------------------------------------------- bool CJSONparse::FillBuffer() { // Read from file Buffer->ReadFromFD( InputHandle ); // Update Markers Buffer->PeekDirect( &BufEnd, Buffer->Len() ); Buffer->PeekDirect( &BufPos, 0 ); LineMark = BufPos; return true; } //--------------------------------------------------------------------------- void CJSONparse::FreeBuffer() { // Destroy buffer if (Buffer) { delete Buffer; Buffer = NULL; // Update Markers BufEnd = NULL; BufPos = NULL; LineMark = NULL; } } //--------------------------------------------------------------------------- bool CJSONparse::ParseString( char ** Value, int &pLen ) { char * Mark; char * EndMark; int Len; // Check for opening quote if (*BufPos != '"') { return false; } // Clear values *Value = NULL; Len = 0; // Mark start of quote Mark = BufPos; BufPos++; // Check for closing quote while ((BufPos = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" ))) { if (*BufPos == '"') { // End of string found BufPos++; break; } else if (!*BufPos) { Error = true; sprintf( ErrorText, "Expect closing '\"' for string on line %d:%ld", LineNo, Mark-LineMark ); return false; } else if (*BufPos == '\\') { if (*(BufPos+1) == 'u') { for (EndMark = BufPos+2; EndMark < BufPos+6; EndMark++) { if (!isxdigit( *EndMark )) { Error = true; sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%ld", LineNo, Mark-LineMark ); return false; } } BufPos += 6; } else if (strchr( "bfnrt/\\\"", *(BufPos+1) )) { BufPos += 2; } else { Error = true; sprintf( ErrorText, "Invalid escape sequence on line %d:%ld", LineNo, Mark-LineMark ); return false; } } else { Error = true; sprintf( ErrorText, "Un-escaped special character in string on line %d:%ld", LineNo, BufPos-LineMark ); return false; } } // Validate size of name Len = BufPos-Mark-2; // Create Return value pointer *Value = (char*)malloc( Len+1 ); memcpy( *Value, Mark+1, Len ); (*Value)[Len] = 0; // Set other parameters pLen = Len; // Success return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParseObject( TDataMember * Object ) { TDataMember * Member = NULL; char * MemberName = NULL; int Len = 0; // Check for start of Object if (*BufPos != '{') { return false; } BufPos++; // Set Type Level++; DataTree->SetValue( Object, jtObject, NULL, -1 ); while (true) { // Look for Member Name SkipWhiteSpace(); if (*BufPos == '}') { break; } else if (!ParseString( &MemberName, Len )) { if (!Error) { Error = true; sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark ); } return false; } else if (!Len) { Error = true; sprintf( ErrorText, "Empty parameter name on line %d:%ld", LineNo, BufPos-LineMark ); return false; } // Check if Member exists Member = DataTree->GetMember( Object, MemberName, true ); // Check for delimiter SkipWhiteSpace(); if (*BufPos != ':') { Error = true; sprintf( ErrorText, "Expected ':' delimiter on line %d:%ld", LineNo, BufPos-LineMark ); return false; } BufPos++; // Get Value SkipWhiteSpace(); if (!ParseObject( Member ) && !Error && !ParseArray( Member ) && !Error && !ParseString( Member ) && !Error && !ParsePrimitive( Member )) { DataTree->Delete( Object, MemberName ); return false; } // Check if more parameters to follow SkipWhiteSpace(); if (*BufPos != ',') { // No more parameters break; } BufPos++; } // Expect end of object if (*BufPos != '}') { Error = true; sprintf( ErrorText, "Closing brace for object '}' expected on line %d:%ld", LineNo, BufPos-LineMark ); return false; } BufPos++; Level--; // success return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParseArray( TDataMember * Array ) { TDataMember * Member = NULL; // Check for start of Object if (*BufPos != '[') { return false; } BufPos++; // Set Type Level++; DataTree->SetValue( Array, jtArray, NULL, -1 ); while (true) { // Look for Member Name SkipWhiteSpace(); if (*BufPos == ']') { break; } // Add new element Member = DataTree->CreateMember( NULL ); // Get Value SkipWhiteSpace(); if (!ParseObject( Member ) && !Error && !ParseArray( Member ) && !Error && !ParseString( Member ) && !Error && !ParsePrimitive( Member )) { DataTree->DestroyMember( &Member ); return false; } else { DataTree->AddMember( Array, Member ); } // Check if more parameters to follow SkipWhiteSpace(); if (*BufPos != ',') { // No more parameters break; } BufPos++; } // Expect end of object if (*BufPos != ']') { Error = true; sprintf( ErrorText, "Closing brace for array ']' expected on line %d:%ld", LineNo, BufPos-LineMark ); return false; } BufPos++; Level--; // success return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParseString( TDataMember * Member ) { char * Value = NULL; int Len = 0; // Try to parse if (!ParseString( &Value, Len )) { return false; } // Set string DataTree->SetValuePtr( Member, jtString, Value, Len ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParsePrimitive( TDataMember * Member ) { char * Value = NULL; int Len = 0; char * Mark; char * EndMark; // Mark start of value Mark = BufPos; // Get end of value while ((*BufPos != 0) && !isspace(*BufPos) && (*BufPos != ',')) { BufPos++; } // Check length of value Len = BufPos - Mark; if (!Len) { Error = true; sprintf( ErrorText, "Missing param value on line %d:%ld", LineNo, Mark-LineMark ); return false; } // Check for primitive values if ((Len == 4) && !strncasecmp( Mark, "null", 4 )) { DataTree->SetValuePtr( Member, jtNull, NULL, -1 ); } else if ((Len == 4) && !strncasecmp( Mark, "true", 4 )) { DataTree->SetValue( Member, jtBool, "1", 1 ); } else if ((Len == 5) && !strncasecmp( Mark, "false", 5 )) { DataTree->SetValue( Member, jtBool, "0", 1 ); } else { // Try conversion to int strtol( Mark, &EndMark, 10 ); if (EndMark == BufPos) { Value = (char*)malloc( Len+1 ); memcpy( Value, Mark, Len ); Value[Len] = 0; DataTree->SetValuePtr( Member, jtInt, Value, Len ); } else { // Try conversion to float strtod( Mark, &EndMark ); if (EndMark == BufPos) { Value = (char*)malloc( Len+1 ); memcpy( Value, Mark, Len ); Value[Len] = 0; DataTree->SetValuePtr( Member, jtFloat, Value, Len ); } else { Error = true; sprintf( ErrorText, "Invalid primitive param value on line %d:%ld", LineNo, Mark-LineMark ); return false; } } } // Success return true; } //--------------------------------------------------------------------------- bool CJSONparse::PrintToScreen( const int Indent ) { TDataMember * RootObject; // Set to StdOut OutputHandle = 1; // Save Root object Level = 0; RootObject = DataTree->GetRootMember(); PrintObject( RootObject, Indent ); // Close file close( OutputHandle ); OutputHandle = -1; return false; } //--------------------------------------------------------------------------- bool CJSONparse::SaveToFile( const char * FilePath, const int Indent ) { TDataMember * RootObject; // Validate if (!FilePath || !FilePath[0]) return false; // Open file if (!(OutputHandle = open( FilePath, O_WRONLY ))) return false; // Save Root object Level = 0; RootObject = DataTree->GetRootMember(); PrintObject( RootObject, Indent ); // Close file close( OutputHandle ); OutputHandle = -1; return false; } //--------------------------------------------------------------------------- bool CJSONparse::PrintObject( TDataMember * Object, const int Indent ) { TDataMember * Member; bool First = true; bool Last = false; int Count = 0; // Opening brace write( OutputHandle, "{", 1 ); Level++; // Extend spacer memset( &Spacer[SpacerLen], ' ', 2 ); SpacerLen += 2; Spacer[SpacerLen] = 0; // Save parameters for (Member = Object->FirstChild; Member != NULL; (Member = Member->Next)) { // Write parameter name if (First) { First = false; if (Object->Name) { write( OutputHandle, "\n", 1 ); write( OutputHandle, Spacer, SpacerLen ); } else { write( OutputHandle, " ", 1 ); } } else { write( OutputHandle, Spacer, SpacerLen ); } // Print key name write( OutputHandle, "\"", 1 ); write( OutputHandle, Member->Name, strlen(Member->Name) ); write( OutputHandle, "\" : ", 4 ); // Print value Last = (++Count >= Object->Len); switch (Member->Type) { case jtNull : write( OutputHandle, "null", 4 ); break; case jtBool : case jtInt : case jtFloat : write( OutputHandle, Member->Value, Member->Len ); break; case jtString : write( OutputHandle, "\"", 1 ); write( OutputHandle, Member->Value, Member->Len ); write( OutputHandle, "\"", 1 ); break; case jtArray : PrintArray( Member, Indent ); break; case jtObject : PrintObject( Member, Indent ); break; } if (!Last) { write( OutputHandle, ",", 1 ); } write( OutputHandle, "\n", 1 ); } // Shorten spacer SpacerLen -= 2; Spacer[SpacerLen] = 0; // Closing brace Level--; if (Object->Len) { write( OutputHandle, Spacer, SpacerLen ); } write( OutputHandle, "}", 1 ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::PrintArray( TDataMember * Array, const int Indent ) { TDataMember * Member; bool First = true; bool Last = false; int Count = 0; // Opening brace write( OutputHandle, "[", 1 ); Level++; // Extend spacer memset( &Spacer[SpacerLen], ' ', 2 ); SpacerLen += 2; Spacer[SpacerLen] = 0; // Save parameters for (Member = Array->FirstChild; Member != NULL; (Member = Member->Next)) { // Write parameter name if (First) { First = false; write( OutputHandle, "\n", 1 ); } write( OutputHandle, Spacer, SpacerLen ); Last = (++Count >= Array->Len); switch (Member->Type) { case jtNull : write( OutputHandle, "null", 4 ); break; case jtBool : case jtInt : case jtFloat : write( OutputHandle, Member->Value, Member->Len ); break; case jtString : write( OutputHandle, "\"", 1 ); write( OutputHandle, Member->Value, Member->Len ); write( OutputHandle, "\"", 1 ); break; case jtArray : PrintArray( Member, Indent ); break; case jtObject : PrintObject( Member, Indent ); break; } if (!Last) { write( OutputHandle, ",", 1 ); } write( OutputHandle, "\n", 1 ); } // Shorten spacer SpacerLen -= 2; Spacer[SpacerLen] = 0; // Closing brace Level--; if (Array->Len) { write( OutputHandle, Spacer, SpacerLen ); } write( OutputHandle, "]", 1 ); return true; } //---------------------------------------------------------------------------