json_stringValue returns the decoder's internal pointer

Platform: Linux
API: C
SDK Version: 2.4.2

What Happened:

As I was parsing json using json_decoder, all of my assigned string values were changing to the current key's value, even when explicitly skipping a key.

The Issue / Reproduction Steps:

I'm not sure if this should be considered an API bug or a Documentation clarification point but the underlying issue is that json_stringValue() returns the char* used internally by the decoder.

This is confusing at best, because the other value functions don't behave in this way. Additionally, the pointer changes even if you skip the key with shouldDecodeTableValueForKey, so you can end up storing a value you intended to skip.

I understand that the return value needs to be a char*, but it should either be a copy of the internal pointer, or the documentation should make it clear that you need to copy the value when using json_stringValue.

Example:
data.json:

{ 
  "str1": "Hello", 
  "str2": "World", 
  "skip": "Skip" 
}

parser.c

static void decodeError( json_decoder* decoder, const char* error, int linenum );
static void willDecodeSublist( json_decoder* decoder, const char* name, json_value_type type );
static int shouldDecodeTableValueForKey( json_decoder* decoder, const char* key ) ;

static void didDecodeTableValue( json_decoder* decoder, const char* key, json_value value );
static int shouldDecodeArrayValueAtIndex( json_decoder* decoder, int pos ) ;
static void didDecodeArrayValue( json_decoder* decoder, int pos, json_value value );
static void didDecodeSublist( json_decoder* decoder, const char* name, json_value_type type );
static int readfile( void* readud, uint8_t* buf, int bufsize );

static JsonObj* newJsonObj( string path ) {

	JsonObj* obj = calloc( 1, sizeof( JsonObj ) );
	json_reader* reader = calloc( 1, sizeof( json_reader ) );
	json_decoder* decoder = calloc( 1, sizeof( json_decoder ) );
	SDFile* file = playdate->file->open( path, kFileRead );
	
	reader->read = readfile;
	reader->userdata = file;

	decoder->decodeError = decodeError;
	decoder->shouldDecodeTableValueForKey = shouldDecodeTableValueForKey;
	decoder->didDecodeTableValue = didDecodeTableValue;
	decoder->willDecodeSublist = willDecodeSublist;
	decoder->shouldDecodeArrayValueAtIndex = shouldDecodeArrayValueAtIndex;
	decoder->didDecodeArrayValue = didDecodeArrayValue;
	decoder->didDecodeSublist = didDecodeSublist;
	decoder->userdata = obj;

	playdate->json->decode( decoder, *reader, NULL );

	free( reader );
	free( decoder );

       printf( "str1: %s\n", obj->str1 ); // Prints "Skip"
       printf( "str2: %s\n", obj->str2 ); // Prints "Skip"

	return obj;

}

// JSON Parsing

static void decodeError( json_decoder* decoder, const char* error, int linenum ) {
	printf( "Error decoding json: '%s' at line %d", error, linenum );
}

static void willDecodeSublist( json_decoder* decoder, const char* name, json_value_type type ) {
    // skip
}

static int shouldDecodeTableValueForKey( json_decoder* decoder, const char* key ) { 

	if( strcmp( "skip", key ) == 0 ) {
		return 0;
	}

	return 1;

}

static void didDecodeTableValue( json_decoder* decoder, const char* key, json_value value ) {

	JsonObj* obj = decoder->userdata;

	if( strcmp( "str1", key ) ) {
		obj->str1 = json_stringValue( value );
		return;
	}

	if( strcmp( "str2", key ) ) {
		obj->str2 = json_stringValue( value );
		return;
	}

}

static int shouldDecodeArrayValueAtIndex( json_decoder* decoder, int pos ) { 
	return 0; 
}

static void didDecodeArrayValue( json_decoder* decoder, int pos, json_value value ) {
    // skip
}

static void didDecodeSublist( json_decoder* decoder, const char* name, json_value_type type ) {
    // skip
}

static int readfile( void* readud, uint8_t* buf, int bufsize ) {
	return pd->file->read( (SDFile*)readud, buf, bufsize );
}
1 Like