Important Update:
- Improve "printing" for JSON with single & multi line objects - Fix bugs in getting, setting & creating array elements - "array[]" allowed to create/add element at end of an array - No longer duplicate get() path (reduce memory allocation) - added NameLen to TMember to eliminate zero-terminate requirement - Fix ReadfromXXX() return values
This commit is contained in:
@@ -40,7 +40,8 @@ TDataMember * CDataTree::CreateMember( const char * Name )
|
|||||||
Member = (TDataMember *)calloc( 1, sizeof(TDataMember) );
|
Member = (TDataMember *)calloc( 1, sizeof(TDataMember) );
|
||||||
if (Name && *Name)
|
if (Name && *Name)
|
||||||
{
|
{
|
||||||
Member->Name = (char *)malloc( strlen( Name )+1 );
|
Member->Len = strlen( Name );
|
||||||
|
Member->Name = (char *)malloc( Member->Len+1 );
|
||||||
strcpy( Member->Name, Name );
|
strcpy( Member->Name, Name );
|
||||||
}
|
}
|
||||||
return Member;
|
return Member;
|
||||||
@@ -119,12 +120,12 @@ bool CDataTree::DestroyValue( TDataMember * Member )
|
|||||||
|
|
||||||
TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char * Path, bool Create, TDataMember ** Parent )
|
TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char * Path, bool Create, TDataMember ** Parent )
|
||||||
{
|
{
|
||||||
char * WorkPath = NULL;
|
|
||||||
TDataMember * Member;
|
TDataMember * Member;
|
||||||
TDataMember ** Child = NULL;
|
TDataMember ** Child = NULL;
|
||||||
char * Pos;
|
char * Pos;
|
||||||
char * EndPos;
|
char * EndPos;
|
||||||
char * MemberName;
|
char * Key;
|
||||||
|
unsigned short KeyLen;
|
||||||
int Index;
|
int Index;
|
||||||
int Count;
|
int Count;
|
||||||
bool Last = false;
|
bool Last = false;
|
||||||
@@ -134,8 +135,6 @@ TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char *
|
|||||||
if (Parent) *Parent = NULL;
|
if (Parent) *Parent = NULL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
WorkPath = (char*)malloc( strlen(Path)+1 );
|
|
||||||
strcpy( WorkPath, Path );
|
|
||||||
|
|
||||||
// Set init references
|
// Set init references
|
||||||
if (Parent) *Parent = NULL;
|
if (Parent) *Parent = NULL;
|
||||||
@@ -143,15 +142,12 @@ TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char *
|
|||||||
Member = (BaseMember)? BaseMember : RootMember;
|
Member = (BaseMember)? BaseMember : RootMember;
|
||||||
|
|
||||||
// Split path
|
// Split path
|
||||||
Pos = (char*)WorkPath;
|
Pos = (char*)Path;
|
||||||
while (Member && *Pos)
|
while (Member && *Pos)
|
||||||
{
|
{
|
||||||
// Reset Child reference
|
// Reset Child reference
|
||||||
Child = NULL;
|
Child = NULL;
|
||||||
|
|
||||||
// Set Name start
|
|
||||||
MemberName = Pos;
|
|
||||||
|
|
||||||
// Find delimiter
|
// Find delimiter
|
||||||
if (*Pos == '[')
|
if (*Pos == '[')
|
||||||
{
|
{
|
||||||
@@ -163,40 +159,61 @@ TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char *
|
|||||||
break; // Can't convert something else to an array
|
break; // Can't convert something else to an array
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Index value
|
// Set Index start
|
||||||
Pos++;
|
Pos++;
|
||||||
while (*Pos && (*Pos != ']')) {
|
Key = Pos;
|
||||||
Pos++;
|
|
||||||
}
|
|
||||||
if (*Pos != ']') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Index = (int)strtoul( Pos, &EndPos, 10 );
|
|
||||||
if (EndPos != Pos) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Pos++;
|
|
||||||
if (!*Pos) {
|
|
||||||
// Last element
|
|
||||||
Last = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find next parent
|
// Get Index value
|
||||||
|
while (*Pos && (*Pos != ']'))
|
||||||
|
Pos++;
|
||||||
|
if (!*Pos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Check if last
|
||||||
|
Pos++;
|
||||||
|
if (!*Pos)
|
||||||
|
Last = true;
|
||||||
|
|
||||||
|
if (Pos == Key+1) {
|
||||||
|
// Empty bracket only allowed for create
|
||||||
|
if (!Create)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// find end of list
|
||||||
|
Child = &(Member->FirstChild);
|
||||||
|
while (*Child)
|
||||||
|
Child = &((*Child)->Next);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Get requested index
|
||||||
|
Index = (int)strtoul( Key, &EndPos, 10 );
|
||||||
|
if (EndPos != Pos-1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Find element at requested index
|
||||||
Child = &(Member->FirstChild);
|
Child = &(Member->FirstChild);
|
||||||
Count = 0;
|
Count = 0;
|
||||||
while (*Child && (Count < Index)) {
|
while (*Child && (Count < Index)) {
|
||||||
Child = &((*Child)->Next);
|
Child = &((*Child)->Next);
|
||||||
Count++;
|
Count++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create element if needed
|
||||||
if (!*Child && Create) {
|
if (!*Child && Create) {
|
||||||
if (!Index || (Index = Count + 1)) {
|
if (!Index || (Index = Count + 1)) {
|
||||||
*Child = CreateMember( NULL );
|
*Child = CreateMember( NULL );
|
||||||
Member->Value++;
|
Member->Len++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Skip separator
|
||||||
|
if (*Pos == '/') {
|
||||||
|
Pos++;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
if (Create && (Member->Type == jtNull)) {
|
if (Create && (Member->Type == jtNull)) {
|
||||||
Member->Type = jtObject; // Convert to object
|
Member->Type = jtObject; // Convert to object
|
||||||
@@ -206,26 +223,25 @@ TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get key value
|
// Get key value
|
||||||
|
Key = Pos;
|
||||||
|
KeyLen = 0;
|
||||||
while (*Pos && (*Pos != '/') && (*Pos != '[')) {
|
while (*Pos && (*Pos != '/') && (*Pos != '[')) {
|
||||||
|
KeyLen++;
|
||||||
Pos++;
|
Pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// More elements?
|
||||||
if (!*Pos) {
|
if (!*Pos) {
|
||||||
// Last element
|
|
||||||
Last = true;
|
Last = true;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Zero terminate name
|
|
||||||
*Pos = 0;
|
|
||||||
Pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find next parent
|
// Find next parent
|
||||||
Child = &(Member->FirstChild);
|
Child = &(Member->FirstChild);
|
||||||
while (*Child && strcasecmp( (*Child)->Name, MemberName )) {
|
while (*Child && ((*Child)->NameLen != KeyLen) && strncasecmp( (*Child)->Name, Key, KeyLen )) {
|
||||||
Child = &((*Child)->Next);
|
Child = &((*Child)->Next);
|
||||||
}
|
}
|
||||||
if (!*Child && Create) {
|
if (!*Child && Create) {
|
||||||
*Child = CreateMember( MemberName );
|
*Child = CreateMember( Key );
|
||||||
Member->Len++;
|
Member->Len++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,10 +254,6 @@ TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char *
|
|||||||
// Next level
|
// Next level
|
||||||
Member = *Child;
|
Member = *Child;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy temp path
|
|
||||||
free( WorkPath );
|
|
||||||
|
|
||||||
return Child;
|
return Child;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ typedef struct SDataMember TDataMember;
|
|||||||
struct SDataMember
|
struct SDataMember
|
||||||
{
|
{
|
||||||
char * Name;
|
char * Name;
|
||||||
|
unsigned short NameLen;
|
||||||
EDataType Type;
|
EDataType Type;
|
||||||
|
|
||||||
TDataMember * FirstChild;
|
TDataMember * FirstChild;
|
||||||
char * Value;
|
char * Value;
|
||||||
int Len;
|
unsigned short Len;
|
||||||
|
|
||||||
TDataMember * Next;
|
TDataMember * Next;
|
||||||
};
|
};
|
||||||
@@ -65,6 +67,7 @@ public:
|
|||||||
|
|
||||||
EDataType GetType( TDataMember * BaseMember, const char * Path );
|
EDataType GetType( TDataMember * BaseMember, const char * Path );
|
||||||
TDataMember * GetMember( TDataMember * BaseMember, const char * Path, bool Create = false );
|
TDataMember * GetMember( TDataMember * BaseMember, const char * Path, bool Create = false );
|
||||||
|
TDataMember * GetFirstChild( TDataMember * BaseMember, const char * Path, bool Create = false );
|
||||||
|
|
||||||
const char * GetStr( TDataMember * BaseMember, const char * Path, const char * Default = NULL, bool Create = false );
|
const char * GetStr( TDataMember * BaseMember, const char * Path, const char * Default = NULL, bool Create = false );
|
||||||
const char * GetStr( TDataMember * BaseMember, const char * Path, int &Len, const char * Default = NULL, bool Create = false );
|
const char * GetStr( TDataMember * BaseMember, const char * Path, int &Len, const char * Default = NULL, bool Create = false );
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ bool CJSONparse::ReadFromFile( const char * RootPath, const char * Path, const c
|
|||||||
bool CJSONparse::ReadFromFile( const char * RootPath, const char * FilePath )
|
bool CJSONparse::ReadFromFile( const char * RootPath, const char * FilePath )
|
||||||
{
|
{
|
||||||
int Handle = -1;
|
int Handle = -1;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
if (!DataTree) {
|
if (!DataTree) {
|
||||||
@@ -182,16 +183,18 @@ bool CJSONparse::ReadFromFile( const char * RootPath, const char * FilePath )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Continuously refill buffer while loading
|
// Continuously refill buffer while loading
|
||||||
ReadFromHandle( RootPath, Handle, true );
|
result = ReadFromHandle( RootPath, Handle, true );
|
||||||
|
|
||||||
// Close File
|
// Close File
|
||||||
close( Handle );
|
close( Handle );
|
||||||
return true;
|
return result;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
bool CJSONparse::ReadFromHandle( const char * RootPath, int Handle, bool pRefillBuffer )
|
bool CJSONparse::ReadFromHandle( const char * RootPath, int Handle, bool pRefillBuffer )
|
||||||
{
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
// Clear Error
|
// Clear Error
|
||||||
Error = false;
|
Error = false;
|
||||||
|
|
||||||
@@ -214,14 +217,14 @@ bool CJSONparse::ReadFromHandle( const char * RootPath, int Handle, bool pRefill
|
|||||||
|
|
||||||
// Continuously refill buffer while loading
|
// Continuously refill buffer while loading
|
||||||
RefillBuffer = pRefillBuffer;
|
RefillBuffer = pRefillBuffer;
|
||||||
ReadFromBuffer( RootPath );
|
result = ReadFromBuffer( RootPath );
|
||||||
RefillBuffer = false;
|
RefillBuffer = false;
|
||||||
|
|
||||||
// Destroy buffer
|
// Destroy buffer
|
||||||
FreeBuffer();
|
FreeBuffer();
|
||||||
|
|
||||||
InputHandle = -1;
|
InputHandle = -1;
|
||||||
return true;
|
return result;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -768,6 +771,7 @@ bool CJSONparse::PrintObject( TDataMember * Object, const int Indent )
|
|||||||
TDataMember * Member;
|
TDataMember * Member;
|
||||||
bool First = true;
|
bool First = true;
|
||||||
bool Last = false;
|
bool Last = false;
|
||||||
|
bool MultiLine = false;
|
||||||
int Count = 0;
|
int Count = 0;
|
||||||
|
|
||||||
// Opening brace
|
// Opening brace
|
||||||
@@ -781,6 +785,7 @@ bool CJSONparse::PrintObject( TDataMember * Object, const int Indent )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save parameters
|
// Save parameters
|
||||||
|
MultiLine = (Object->Len > 1)? true : false;
|
||||||
for (Member = Object->FirstChild; Member != NULL; (Member = Member->Next))
|
for (Member = Object->FirstChild; Member != NULL; (Member = Member->Next))
|
||||||
{
|
{
|
||||||
// Whitespace around first bracket
|
// Whitespace around first bracket
|
||||||
@@ -791,6 +796,9 @@ bool CJSONparse::PrintObject( TDataMember * Object, const int Indent )
|
|||||||
write( OutputHandle, "\n", 1 );
|
write( OutputHandle, "\n", 1 );
|
||||||
write( OutputHandle, Spacer, SpacerLen );
|
write( OutputHandle, Spacer, SpacerLen );
|
||||||
}
|
}
|
||||||
|
else if (MultiLine) {
|
||||||
|
write( OutputHandle, Spacer, Indent-1 );
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
write( OutputHandle, " ", 1 );
|
write( OutputHandle, " ", 1 );
|
||||||
}
|
}
|
||||||
@@ -828,15 +836,17 @@ bool CJSONparse::PrintObject( TDataMember * Object, const int Indent )
|
|||||||
|
|
||||||
case jtArray :
|
case jtArray :
|
||||||
PrintArray( Member, Indent );
|
PrintArray( Member, Indent );
|
||||||
|
if (!MultiLine && Member->Len > 1) MultiLine = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case jtObject :
|
case jtObject :
|
||||||
PrintObject( Member, Indent );
|
PrintObject( Member, Indent );
|
||||||
|
if (!MultiLine && Member->Len > 1) MultiLine = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!Last)
|
if (!Last)
|
||||||
write( OutputHandle, ",", 1 );
|
write( OutputHandle, ",", 1 );
|
||||||
if (Indent)
|
if (Indent && MultiLine)
|
||||||
write( OutputHandle, "\n", 1 );
|
write( OutputHandle, "\n", 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,7 +857,10 @@ bool CJSONparse::PrintObject( TDataMember * Object, const int Indent )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Closing brace
|
// Closing brace
|
||||||
if (Object->Len) {
|
if (Indent) {
|
||||||
|
if (!MultiLine)
|
||||||
|
write( OutputHandle, " ", 1 );
|
||||||
|
else
|
||||||
write( OutputHandle, Spacer, SpacerLen );
|
write( OutputHandle, Spacer, SpacerLen );
|
||||||
}
|
}
|
||||||
write( OutputHandle, "}", 1 );
|
write( OutputHandle, "}", 1 );
|
||||||
@@ -860,6 +873,7 @@ bool CJSONparse::PrintArray( TDataMember * Array, const int Indent )
|
|||||||
TDataMember * Member;
|
TDataMember * Member;
|
||||||
bool First = true;
|
bool First = true;
|
||||||
bool Last = false;
|
bool Last = false;
|
||||||
|
bool MultiLine = false;
|
||||||
int Count = 0;
|
int Count = 0;
|
||||||
|
|
||||||
// Opening brace
|
// Opening brace
|
||||||
@@ -873,16 +887,28 @@ bool CJSONparse::PrintArray( TDataMember * Array, const int Indent )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save parameters
|
// Save parameters
|
||||||
|
MultiLine = (Array->Len > 1)? true : false;
|
||||||
for (Member = Array->FirstChild; Member != NULL; (Member = Member->Next))
|
for (Member = Array->FirstChild; Member != NULL; (Member = Member->Next))
|
||||||
{
|
{
|
||||||
// Whitespace around brace
|
// Whitespace around brace
|
||||||
if (Indent) {
|
if (Indent) {
|
||||||
if (First) {
|
if (First) {
|
||||||
First = false;
|
First = false;
|
||||||
|
if (Array->Name) {
|
||||||
write( OutputHandle, "\n", 1 );
|
write( OutputHandle, "\n", 1 );
|
||||||
}
|
|
||||||
write( OutputHandle, Spacer, SpacerLen );
|
write( OutputHandle, Spacer, SpacerLen );
|
||||||
}
|
}
|
||||||
|
else if (MultiLine) {
|
||||||
|
write( OutputHandle, Spacer, Indent-1 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
write( OutputHandle, " ", 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
write( OutputHandle, Spacer, SpacerLen );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Last = (++Count >= Array->Len);
|
Last = (++Count >= Array->Len);
|
||||||
switch (Member->Type)
|
switch (Member->Type)
|
||||||
@@ -905,15 +931,17 @@ bool CJSONparse::PrintArray( TDataMember * Array, const int Indent )
|
|||||||
|
|
||||||
case jtArray :
|
case jtArray :
|
||||||
PrintArray( Member, Indent );
|
PrintArray( Member, Indent );
|
||||||
|
if (!MultiLine && Member->Len > 1) MultiLine = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case jtObject :
|
case jtObject :
|
||||||
PrintObject( Member, Indent );
|
PrintObject( Member, Indent );
|
||||||
|
if (!MultiLine && Member->Len > 1) MultiLine = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!Last)
|
if (!Last)
|
||||||
write( OutputHandle, ",", 1 );
|
write( OutputHandle, ",", 1 );
|
||||||
if (Indent)
|
if (Indent && MultiLine)
|
||||||
write( OutputHandle, "\n", 1 );
|
write( OutputHandle, "\n", 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -924,7 +952,10 @@ bool CJSONparse::PrintArray( TDataMember * Array, const int Indent )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Closing brace
|
// Closing brace
|
||||||
if (Array->Len) {
|
if (Indent) {
|
||||||
|
if (!MultiLine)
|
||||||
|
write( OutputHandle, " ", 1 );
|
||||||
|
else
|
||||||
write( OutputHandle, Spacer, SpacerLen );
|
write( OutputHandle, Spacer, SpacerLen );
|
||||||
}
|
}
|
||||||
write( OutputHandle, "]", 1 );
|
write( OutputHandle, "]", 1 );
|
||||||
|
|||||||
Reference in New Issue
Block a user