Handling unknown data

In some cases you'll need to deal with data that is of an unknown type or that varies.

The decoder provides the functions pps_decoder_type() and pps_decoder_name(), which provide the type and attribute name associated with the current element. These functions allow your application to iterate over an object in the same manner as an array, advancing from element to element and extracting the type, name, and value of each element.

At all times, the decoder is positioned on an element of an array or object. So, how do you know if you're within an object or array? The API provides the special attribute name ".", which refers to the current container much like "." refers to the current directory in a filesystem. Your application can determine the type of the current container by calling the function pps_decoder_type() with the "." attribute. For example:

if ( pps_decoder_type(&decoder, ".") == PPS_TYPE_ARRAY ) {
      . . .
}

A good example of how to handle unknown data is the pps_decoder_dump_tree() function, which takes an arbitrary object or array and dumps it to a file. The complete source for this function is as follows:

void
pps_decoder_dump_tree(pps_decoder_t *decoder, FILE *fp)
{
	pps_decoder_state_t state;

	pps_decoder_get_state(decoder, &state);
	pps_decoder_dump_collection(decoder, fp, 0);
	fprintf(fp, "\n");
	pps_decoder_set_state(decoder, &state);
}

static void
pps_decoder_dump_collection(pps_decoder_t *decoder, FILE *fp, int prefix)
{
	int len = pps_decoder_length(decoder);
	int i;
	int flags = pps_decoder_flags(decoder, ".");

	if ( flags ) {
		fprintf(fp,"(%s)", flags & PPS_DELETED ? "deleted" :
				flags & PPS_CREATED ? "created" : 
				flags & PPS_TRUNCATED ? "truncated" :
				flags & PPS_OVERFLOWED ? "overflowed" :
				flags & PPS_PURGED ? "purged" : "other");
		return;
	}

	(void)pps_decoder_goto_index(decoder, 0);
	if ( pps_decoder_type(decoder,".") == PPS_TYPE_OBJECT ) {
		fprintf(fp, "{\n");
		for ( i = 0; i < len; ++i ) {
			fprintf(fp,"%*s%s (%s) ", prefix+2, "", pps_decoder_name(decoder),
					pps_datatypes[pps_decoder_type(decoder,NULL)]);
			pps_decoder_dump_value(decoder, fp, prefix + 2);
		}
		fprintf(fp,"%*s}",prefix, "");
	}
	else {
		fprintf(fp, "[\n");
		for ( i = 0; i < len; ++i ) {
			fprintf(fp,"%*s%d (%s) ", prefix+2, "", i, 
					pps_datatypes[pps_decoder_type(decoder,NULL)]);
			pps_decoder_dump_value(decoder, fp, prefix + 2);
		}
		fprintf(fp,"%*s]", prefix, "");
	}
}

static void
pps_decoder_dump_value(pps_decoder_t *decoder, FILE *fp, int prefix)
{
	double dvalue;
	int64_t ivalue;
	bool bvalue;
	const char *svalue;

	switch ( pps_decoder_type(decoder,NULL) ) {
	case PPS_TYPE_NUMBER:
		if ( pps_decoder_is_integer(decoder, NULL) ) {
			(void)pps_decoder_get_int64(decoder, NULL, &ivalue);
			fprintf(fp, "%lld", ivalue);
		}
		else {
			(void)pps_decoder_get_double(decoder, NULL, &dvalue);
			fprintf(fp, "%lf", dvalue);
		}
		break;
	case PPS_TYPE_BOOL:
		(void)pps_decoder_get_bool(decoder, NULL, &bvalue);
		fprintf(fp, "%s", bvalue ? "true" : "false");
		break;
	case PPS_TYPE_STRING:
		(void)pps_decoder_get_string(decoder, NULL, &svalue);
		fprintf(fp, "%s", svalue);
		break;
	case PPS_TYPE_ARRAY:
	case PPS_TYPE_OBJECT:
		(void)pps_decoder_push(decoder, NULL);
		pps_decoder_dump_collection(decoder, fp, prefix);
		(void)pps_decoder_pop(decoder);
		break;
	case PPS_TYPE_NULL:
	default:
		(void)pps_decoder_next(decoder);
	}
	fprintf(fp,"\n");
}