Reputation: 117
I have a simple webpage that displays a json object in the <body>
. I simplified the example here, as each element has a lot of data to it and it isn't necessary to print it out in this question. I kept the format, however, including the carriage returns.
<body>
callBack({
"resSet":[
{
"results":[
{^M
"res":{"data inside res",^M
"more data",^M
{"and some more"}^M
},^M
{^M
"res":{"data inside res",^M
"more data",^M
{"and some more"}^M
},^M
{^M
"res":{"data inside res",^M
"more data",^M
{"and some more"}^M
}],^M
"other resSet data"^M
}^M
],
})^M
</body>
I'm using libcurl in C to read this data, as a string, into memory. I am using C and cURL to maintain portability and memory control. What I want to do is separate the elements of the "results"
array so I can reorder them however I choose. After sorting, the json is written to a file for other programs.
Please note that this may have the structure of a JSON object, but it is treated as a string in memory.
Here is my code.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
// curl callback function (found on
https://curl.haxx.se/libcurl/c/getinmemory.html)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(ptr == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
int main(int argc, char *argv[]) { // pass query and collection
curl_global_init(CURL_GLOBAL_ALL);
CURL *curl;
CURLcode res;
struct MemoryStruct chunk;
char *url = calloc(1024, 1); // url to maxxcat4.astm.org/query4.cgi, plenty of space for query and collection parameters
char *query = calloc(strlen(argv[1])+1, 1); // +1 so | can be placed in function FetchLine
char *collection = calloc(strlen(argv[2]), 1);
char *order; // not allocated, points to return from function FetchLine
sprintf(query, "%s", argv[1]);
sprintf(collection, "%s", argv[2]);
sprintf(url, "http://maxxcat4.astm.org/query4.cgi?query=%s&collection=%s", query, collection); // query then collection
chunk.memory = malloc(1); // currently using realloc, should change to calloc
chunk.size = 0; // nothing, initially
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url); // fetch data from url
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,WriteMemoryCallback); // send the data to the function WriteMemoryCallback (found on https://curl.haxx.se/libcurl/c/getinmemory.html)
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); // pass the MemoryStruct chunk to the function
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); // set a user agent if the server requests for one
res = curl_easy_perform(curl); // retrieve data
if(res != CURLE_OK) { // make sure things worked
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
/*
Sort the data while in memory according to how the file is shuffled
- read file into memory
- take one line at a time, breaking on delimiter (if needed)
- shuffle each res
*/
// order = FetchLine(query);
FILE *fp;
fp = fopen("maxxcat4_data.json", "w");
/* seperate results array elements here */
fclose(fp);
printf("%lu bytes retrieved\n", (unsigned long)chunk.size);
}
/* always cleanup */
curl_easy_cleanup(curl); // clean up handle
free(chunk.memory);
free(url);
free(query);
free(collection);
free(order);
curl_global_cleanup(); // clean up libcurl
}
return 0;
}
My first idea was to use strtok()
but I don't know of a way to delimit on a string, rather than a single character from a set of delimiters. I've read about json-c, but I want to avoid dependencies like this if at all possible. How can I separate the elements?
Upvotes: 1
Views: 617
Reputation: 19395
I kept the format, however, including the carriage returns.
If you know that exact format, you can take advantage of this knowledge and simplify the reading - e. g. each element of the results ends with }
at the beginning of a line.
The following snippet separates the string at chunk.memory
into head
(the part before the result elements), res[nres]
(an array of nres
elements) and tail
(the part after the result elements, after the closing ]
); comments are inline with the code.
char c, *head, **res = NULL, *tail; // pointers to parts
int nres = 0; // number of results
head = chunk.memory; // it begins here
const char results[] = "\"results\":[";
char *cp = strstr(head, results); // find the results
if (!cp) printf("%s not found\n", results), exit(1);
cp += strlen(results); // skip to the \n
*cp++ = '\0'; // delimit the head
do
{
res = realloc(res, sizeof *res * ++nres);
if (!res) puts("out of memory"), exit(1);
res[nres-1] = cp; // store the result
cp = strstr(cp, "\n}"); // find end of result
if (!cp) puts("} not found"), exit(1);
cp += 2; // skip over the }
c = *cp; // character after } is , or ]
*cp++ = '\0'; // delimit the string
} while (c == ',');
if (c != ']') puts("] not found"), exit(1);
tail = cp; // rest follows here
// output the parts with results rearranged, e. g. backwards
printf("%s\n", head);
while (nres--) printf("%s%c", res[nres], nres ? ',' : ']');
free(res);
printf("%s", tail);
Upvotes: 1