b3d2a3a1d7e096b0f97a2a5295d4fa610336de04 angie Fri Oct 12 11:02:37 2018 -0700 Adding jsonQuery, which extracts values from a jsonElement tree using a simple path notation e.g. "journals[publisher=Nature].articles[*].author[0].lastName". aside: slFreeList doesn't check for NULL. I think it should, but it's used in a zillion places so for now I'll just check before calling. diff --git src/lib/tests/jsonQueryTest.c src/lib/tests/jsonQueryTest.c new file mode 100644 index 0000000..74e326b --- /dev/null +++ src/lib/tests/jsonQueryTest.c @@ -0,0 +1,169 @@ +/* jsonQueryTest - Read in JSON and paths from files and print the results. */ + +/* Copyright (C) 2018 The Regents of the University of California + * See README in this or parent directory for licensing information. */ + +#include "common.h" +#include "errCatch.h" +#include "hash.h" +#include "linefile.h" +#include "options.h" +#include "jsonQuery.h" + +void usage() +/* Explain usage and exit. */ +{ +errAbort( + "jsonQueryTest - Read in JSON and paths from files and print the results.\n" + "usage:\n" + " jsonQueryTest jsonIn.txt pathIn.txt out.txt\n" + "jsonIn.txt contains one valid JSON string per line.\n" + "pathIn.txt contains one valid search path per line.\n" + "Resulting values will be printed, tab-separated, in out.txt.\n" + "\n" + ); +} + +static struct optionSpec options[] = { + {NULL, 0}, +}; + +static struct slName *lineFileToList(struct lineFile *lf) +/* Return a list of lines in lf. */ +{ +struct slName *list = NULL; +char *line; +int size; +while (lineFileNext(lf, &line, &size)) + slAddHead(&list, slNameNewN(line, size)); +slReverse(&list); +return list; +} + +void shallowPrintEl(FILE *f, struct jsonElement *el, char *name) +/* Print abbreviated version if el is an object or list, or the value if object is a scalar. */ +{ +if (name != NULL) + { + fprintf(f, "\"%s\": ", name); + } +switch (el->type) + { + case jsonObject: + { + struct hash *hash = jsonObjectVal(el, "el"); + struct hashEl *helList = hashElListHash(hash); + fprintf(f, "{"); + if (helList) + { + shallowPrintEl(f, helList->val, helList->name); + if (helList->next) + fputs(",...", f); + hashElFreeList(&helList); + } + fputc('}', f); + } + break; + case jsonList: + { + struct slRef *vals = jsonListVal(el, name); + fprintf(f, "["); + if (vals) + { + shallowPrintEl(f, vals->val, NULL); + if (vals->next) + fputs(",...", f); + } + fputc(']', f); + } + break; + case jsonString: + { + char *escaped = jsonStringEscape(el->val.jeString); + fprintf(f, "\"%s\"", escaped); + freez(&escaped); + } + break; + case jsonBoolean: + { + char *val = (el->val.jeBoolean ? "true" : "false"); + fprintf(f, "%s", val); + } + break; + case jsonNumber: + fprintf(f, "%ld", el->val.jeNumber); + break; + case jsonDouble: + fprintf(f, "%g", el->val.jeDouble); + break; + case jsonNull: + fprintf(f, "null"); + break; + default: + errAbort("shallowPrintEl: invalid type: %d", el->type); + break; + } +} + +#define MAX_JSON 50 + +static void jsonQueryTest(char *jsonFile, char *pathFile, char *outFile) +/* Read in JSON and paths from files and print the results. */ +{ +struct lineFile *jsonLf = lineFileOpen(jsonFile, TRUE); +struct lineFile *pathLf = lineFileOpen(pathFile, TRUE); +FILE *outF = mustOpen(outFile, "w"); +struct slName *paths = lineFileToList(pathLf); +char *line; +int size; +while (lineFileNext(jsonLf, &line, &size)) + { + if (size <= MAX_JSON) + fprintf(outF, "# JSON: '%s'\n", line); + else + fprintf(outF, "# JSON: '%-*s...'\n", MAX_JSON, line); + struct jsonElement *top = jsonParse(line); + struct slRef topRef; + topRef.next = NULL; + topRef.val = top; + struct slName *path; + for (path = paths; path != NULL; path = path->next) + { + fprintf(outF, "# path: '%s'\n", path->name); + struct errCatch *errCatch = errCatchNew(); + if (errCatchStart(errCatch)) + { + struct slRef *refResults = jsonQueryElementList(&topRef, "test JSON", path->name, NULL); + if (refResults == NULL) + fputs("NULL\n", outF); + else + { + struct slRef *ref; + for (ref = refResults; ref != NULL; ref = ref->next) + { + struct jsonElement *el = ref->val; + shallowPrintEl(outF, el, NULL); + if (ref->next) + fputc('\t', outF); + } + fputc('\n', outF); + } + } + errCatchEnd(errCatch); + if (errCatch->gotError) + fprintf(outF, "# failed: %s", errCatch->message->string); + errCatchFree(&errCatch); + } + fputc('\n', outF); + } +} + +int main(int argc, char *argv[]) +/* Process command line. */ +{ +optionInit(&argc, argv, options); +if (argc != 4) + usage(); +jsonQueryTest(argv[1], argv[2], argv[3]); +return 0; +}