93943b9bd3f5d6d9ef5a51e636636449b0c607b1 kent Mon Dec 9 11:10:26 2013 -0800 Separating out generic portable JSON parser from Genome Browser specific stuff in jsHelper module, creating jsonParse module. diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c index 1271019..cade588 100644 --- src/hg/lib/jsHelper.c +++ src/hg/lib/jsHelper.c @@ -1,1338 +1,656 @@ // jsHelper.c - helper routines for interface between CGIs and client-side javascript #include "common.h" #include #include "dystring.h" #include "cheapcgi.h" #include "cart.h" #include "hPrint.h" #include "hash.h" #include "jsHelper.h" #include "web.h" #include "hui.h" #include "hgConfig.h" #include "portable.h" static boolean jsInited = FALSE; -struct jsonElement *jsonGlobalsHash = NULL; - /* mainForm/hiddenForm code supports the following: when the user selects * something from a pull-down, it will go hit the server to * figure out how to reload other control options based on the choice. * (For instance if they change the group, which items in the track * drop-down need to change). * * We accomplish this by maintaining two forms - a mainForm and a * hiddenForm. The hiddenForm maintains echo's of all the variables * in the main form, which get updated onChange of controls that need * to 'ripple' to other controls. The onChange also submits the * control. */ void jsInit() /* If this is the first call, set window.onload to the operations * performed upon loading a page and print supporting javascript. * Currently this just sets the page vertical position if specified on * CGI, and also calls jsWriteFunctions. * Subsequent calls do nothing, so this can be called many times. */ { if (! jsInited) { puts( "\n" "\n"); int pos = cgiOptionalInt("jsh_pageVertPos", 0); if (pos > 0) printf("\n\n", pos); jsWriteFunctions(); jsInited = TRUE; } } void jsWriteFunctions() /* Write out Javascript functions. */ { hPrintf("\n\n"); } struct dyString *jsOnChangeStart() /* Start up an onChange string */ { struct dyString *dy = dyStringNew(1024); dyStringAppend(dy, "onChange=\""); return dy; } char *jsOnChangeEnd(struct dyString **pDy) /* Finish up javascript onChange command. */ { dyStringAppend(*pDy, "document.hiddenForm.submit();\""); return dyStringCannibalize(pDy); } void jsDropDownCarryOver(struct dyString *dy, char *var) /* Add statement to carry-over drop-down item to dy. */ { dyStringPrintf(dy, "document.hiddenForm.%s.value=", var); dyStringPrintf(dy, "document.mainForm.%s.options", var); dyStringPrintf(dy, "[document.mainForm.%s.selectedIndex].value; ", var); } void jsTextCarryOver(struct dyString *dy, char *var) /* Add statement to carry-over text item to dy. */ { dyStringPrintf(dy, "document.hiddenForm.%s.value=document.mainForm.%s.value; ", var, var); } void jsTrackingVar(char *jsVar, char *val) /* Emit a little Javascript to keep track of a variable. * This helps especially with radio buttons. */ { hPrintf("\n"); } void jsMakeTrackingRadioButton(char *cgiVar, char *jsVar, char *val, char *selVal) /* Make a radio button that also sets tracking variable * in javascript. */ { hPrintf(""); } void jsMakeTrackingCheckBox(struct cart *cart, char *cgiVar, char *jsVar, boolean usualVal) /* Make a check box filling in with existing value and * putting a javascript tracking variable on it. */ { char buf[256]; boolean oldVal = cartUsualBoolean(cart, cgiVar, usualVal); hPrintf("\n", jsVar, oldVal); hPrintf(""); safef(buf, sizeof(buf), "%s%s", cgiBooleanShadowPrefix(), cgiVar); cgiMakeHiddenVar(buf, "0"); } void jsTrackedVarCarryOver(struct dyString *dy, char *cgiVar, char *jsVar) /* Carry over tracked variable (radio button?) to hidden form. */ { dyStringPrintf(dy, "document.hiddenForm.%s.value=%s; ", cgiVar, jsVar); } char *jsRadioUpdate(char *cgiVar, char *jsVar, char *val) /* Make a little javascript to check and uncheck radio buttons * according to new value. To use this you must have called * jsWriteFunctions somewhere, and also must use jsMakeTrackingRadioButton * to make the buttons. */ { static char buf[256]; safef(buf, sizeof(buf), "setRadioCheck('%s', '%s'); %s='%s'", cgiVar, val, jsVar, val); return buf; } void jsCreateHiddenForm(struct cart *cart, char *scriptName, char **vars, int varCount) /* Create a hidden form with the given variables */ { int i; hPrintf( "
\n", scriptName); cartSaveSession(cart); for (i=0; i\n", vars[i]); puts(""); } char *jsSetVerticalPosition(char *form) /* Returns a javascript statement for storing the vertical position of the * page; typically this would go just before a document submit. * jsInit must be called first. * Do not free return value! */ { if (! jsInited) errAbort("jsSetVerticalPosition: jsInit must be called first."); static char vertPosSet[2048]; safef(vertPosSet, sizeof(vertPosSet), "document.%s.jsh_pageVertPos.value = f_scrollTop(); ", form); return vertPosSet; } void jsMakeSetClearButton(struct cart *cart, char *form, char *buttonVar, char *buttonLabel, char *cartVarPrefix, struct slName *cartVarSuffixList, char *anchor, boolean currentPos, boolean isSet) /* Make a button for setting or clearing all of a list of boolean * cart variables (i.e. checkboxes). If this button was just pressed, * set or clear those cart variables. * Optional html anchor is appended to the form's action if given. * If currentPos, anchor is ignored and jsSetVerticalPosition is used so * that the new page gets the same vertical offset as the current page. */ { struct slName *suffix; char javascript[2048]; char *vertPosJs = ""; if (currentPos) { anchor = NULL; jsInit(); vertPosJs = jsSetVerticalPosition(form); } cgiMakeHiddenVar(buttonVar, ""); safef(javascript, sizeof javascript, "document.%s.action = '%s%s%s'; document.%s.%s.value='%s'; %s" "document.%s.submit();", form, cgiScriptName(), (isNotEmpty(anchor) ? "#" : ""), (isNotEmpty(anchor) ? anchor : ""), form, buttonVar, buttonLabel, vertPosJs, form); cgiMakeOnClickButton(javascript, buttonLabel); if (isNotEmpty(cgiOptionalString(buttonVar))) { char option[1024]; if (cartVarPrefix == NULL) cartVarPrefix = ""; for (suffix = cartVarSuffixList; suffix != NULL; suffix = suffix->next) { safef(option, sizeof(option), "%s%s", cartVarPrefix, suffix->name); cartSetBoolean(cart, option, isSet); } } } void jsMakeCheckboxGroupSetClearButton(char *buttonVar, boolean isSet) /* Make a button for setting or clearing a set of checkboxes with the same name. * Uses only javascript to change the checkboxes, no resubmit. */ { char javascript[256]; safef(javascript, sizeof(javascript), "var list = document.getElementsByName('%s'); " "for (var ix = 0; ix < list.length; ix++) {list[ix].checked = %s}", buttonVar, isSet ? "true" : "false"); cgiMakeOnClickButton(javascript, isSet ? JS_SET_ALL_BUTTON_LABEL : JS_CLEAR_ALL_BUTTON_LABEL); } char *jsPressOnEnter(char *button) /* Returns a javascript statement that clicks button when the Enter key * has been pressed; typically this would go in a text input. * jsInit must be called first. * Do not free return value! */ { if (! jsInited) errAbort("jsPressOnEnter: jsInit must be called first."); static char poe[2048]; safef(poe, sizeof(poe), "return pressOnEnter(event, %s);", button); return poe; } void jsIncludeFile(char *fileName, char *noScriptMsg) { /* Prints out html to include given javascript file from the js directory; suppresses redundant * \n", cartSidUrlString(cart), cgiScriptName(), cartSidUrlString(cart)); } -static struct jsonElement *newJsonElement(jsonElementType type) -// generic constructor for a jsonElement; callers fill in the appropriate value -{ -struct jsonElement *ele; -AllocVar(ele); -ele->type = type; -return ele; -} - -struct jsonElement *newJsonString(char *str) -{ -struct jsonElement *ele = newJsonElement(jsonString); -ele->val.jeString = cloneString(str); -return ele; -} - -struct jsonElement *newJsonBoolean(boolean val) -{ -struct jsonElement *ele = newJsonElement(jsonBoolean); -ele->val.jeBoolean = val; -return ele; -} - -struct jsonElement *newJsonNumber(long val) -{ -struct jsonElement *ele = newJsonElement(jsonNumber); -ele->val.jeNumber = val; -return ele; -} - -struct jsonElement *newJsonDouble(double val) -{ -struct jsonElement *ele = newJsonElement(jsonDouble); -ele->val.jeDouble = val; -return ele; -} - -struct jsonElement *newJsonObject(struct hash *h) -{ -struct jsonElement *ele = newJsonElement(jsonObject); -ele->val.jeHash = h; -return ele; -} - -struct jsonElement *newJsonList(struct slRef *list) -{ -struct jsonElement *ele = newJsonElement(jsonList); -ele->val.jeList = list; -return ele; -} - -void jsonObjectAdd(struct jsonElement *h, char *name, struct jsonElement *ele) -// Add a new element to a jsonObject; existing values are replaced. -// NOTE: Adding to a NULL hash will add to the global "common" hash printed with jsonPrintGlobals(); -{ -if (h == NULL) // If hash isn't provided, assume global - { - if (jsonGlobalsHash == NULL) - jsonGlobalsHash = newJsonObject(newHash(5)); - h = jsonGlobalsHash; - } -if(h->type != jsonObject) - errAbort("jsonObjectAdd called on element with incorrect type (%d)", h->type); -hashReplace(h->val.jeHash, name, ele); -} - -void jsonListAdd(struct jsonElement *list, struct jsonElement *ele) -{ -if(list->type != jsonList) - errAbort("jsonListAdd called on element with incorrect type (%d)", list->type); -slAddHead(&list->val.jeList, ele); -} - static char *makeIndentBuf(int indentLevel) { if (indentLevel < 0) return ""; char *indentBuf; indentBuf = needMem(indentLevel + 1); memset(indentBuf, '\t', indentLevel); indentBuf[indentLevel] = 0; return indentBuf; } static void jsonPrintRecurse(struct jsonElement *ele, int indentLevel) { if (indentLevel >= -1) // Note that < -1 will result in no indenting indentLevel++; char *tab = "\t"; char *nl = "\n"; if (indentLevel < 0) { tab = ""; nl = ""; } char *indentBuf = makeIndentBuf(indentLevel); switch (ele->type) { case jsonObject: { hPrintf("{%s",nl); if(hashNumEntries(ele->val.jeHash)) { struct hashEl *el, *list = hashElListHash(ele->val.jeHash); slSort(&list, hashElCmp); for (el = list; el != NULL; el = el->next) { struct jsonElement *val = el->val; hPrintf("%s%s\"%s\": ", indentBuf, tab, el->name); jsonPrintRecurse(val, indentLevel); hPrintf("%s%s", el->next == NULL ? "" : ",",nl); } hashElFreeList(&list); } hPrintf("%s}", indentBuf); break; } case jsonList: { struct slRef *el; hPrintf("[%s",nl); if(ele->val.jeList) { for (el = ele->val.jeList; el != NULL; el = el->next) { struct jsonElement *val = el->val; hPrintf("%s%s", indentBuf,tab); jsonPrintRecurse(val, indentLevel); hPrintf("%s%s", el->next == NULL ? "" : ",",nl); } } hPrintf("%s]", indentBuf); break; } case jsonString: { hPrintf("\"%s\"", jsonStringEscape(ele->val.jeString)); break; } case jsonBoolean: { hPrintf("%s", ele->val.jeBoolean ? "true" : "false"); break; } case jsonNumber: { char buf[256]; safef(buf, sizeof(buf), "%ld", ele->val.jeNumber); hPrintf("%s", buf); break; } case jsonDouble: { char buf[256]; safef(buf, sizeof(buf), "%g", ele->val.jeDouble); hPrintf("%s", buf); break; } default: { errAbort("jsonPrintRecurse; invalid type: %d", ele->type); break; } } if (indentLevel >= 0) freez(&indentBuf); } void jsonPrint(struct jsonElement *json, char *name, int indentLevel) { // print out a jsonElement, indentLevel -1 means no indenting char *indentBuf = makeIndentBuf(indentLevel); if(name != NULL) { if (indentLevel >= 0 ) hPrintf("// START %s\n%s", name, indentBuf); hPrintf("var %s = ", name); } jsonPrintRecurse(json, (indentLevel - 1)); // will increment back to indentLevel if(name != NULL) { hPrintf("%s;\n", indentBuf); if (indentLevel >= 0 ) hPrintf("// END %s\n", name); } if (indentLevel >= 0) freez(&indentBuf); } void jsonPrintGlobals(boolean wrapWithScriptTags) // prints out the "common" globals json hash // This hash is the one utils.js and therefore all CGIs know about { if (jsonGlobalsHash != NULL) { if (wrapWithScriptTags) printf("\n"); } } void jsonErrPrintf(struct dyString *ds, char *format, ...) // Printf a json error to a dyString for communicating with ajax code; format is: // {"error": error message here} { va_list args; va_start(args, format); dyStringPrintf(ds, "{\"error\": \""); struct dyString *buf = newDyString(1000); dyStringVaPrintf(buf, format, args); dyStringAppend(ds, jsonStringEscape(dyStringCannibalize(&buf))); dyStringPrintf(ds, "\"}"); va_end(args); } -static void skipLeadingSpacesWithPos(char *s, int *posPtr) -/* skip leading white space. */ -{ -for (;;) - { - char c = s[*posPtr]; - if (!isspace(c)) - return; - (*posPtr)++; - } -} - -static void getSpecificChar(char c, char *str, int *posPtr) -{ -// get specified char from string or errAbort -if(str[*posPtr] != c) - errAbort("Unexpected character '%c' (expected '%c') - string position %d\n", str[*posPtr], c, *posPtr); -(*posPtr)++; -} - -static char *getString(char *str, int *posPtr) -{ -// read a double-quote delimited string; we handle backslash escaping. -// returns allocated string. -boolean escapeMode = FALSE; -int i; -struct dyString *ds = dyStringNew(1024); -getSpecificChar('"', str, posPtr); -for(i = 0;; i++) - { - char c = str[*posPtr + i]; - if(!c) - errAbort("Premature end of string (missing trailing double-quote); string position '%d'", *posPtr); - else if(escapeMode) - { - // We support escape sequences listed in http://www.json.org, - // except for Unicode which we cannot support in C-strings - switch(c) - { - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'u': - errAbort("Unicode in JSON is unsupported"); - break; - default: - // we don't need to convert \,/ or " - break; - } - dyStringAppendC(ds, c); - escapeMode = FALSE; - } - else if(c == '"') - break; - else if(c == '\\') - escapeMode = TRUE; - else - { - dyStringAppendC(ds, c); - escapeMode = FALSE; - } - } -*posPtr += i; -getSpecificChar('"', str, posPtr); -return dyStringCannibalize(&ds); -} - -static struct jsonElement *jsonParseExpression(char *str, int *posPtr); - -static struct jsonElement *jsonParseObject(char *str, int *posPtr) -{ -struct hash *h = newHash(5); -getSpecificChar('{', str, posPtr); -while(str[*posPtr] != '}') - { - // parse out a name : val pair - skipLeadingSpacesWithPos(str, posPtr); - char *name = getString(str, posPtr); - skipLeadingSpacesWithPos(str, posPtr); - getSpecificChar(':', str, posPtr); - skipLeadingSpacesWithPos(str, posPtr); - hashAdd(h, name, jsonParseExpression(str, posPtr)); - skipLeadingSpacesWithPos(str, posPtr); - if(str[*posPtr] == ',') - (*posPtr)++; - else - break; - } -skipLeadingSpacesWithPos(str, posPtr); -getSpecificChar('}', str, posPtr); -return newJsonObject(h); -} - -static struct jsonElement *jsonParseList(char *str, int *posPtr) -{ -struct slRef *list = NULL; -getSpecificChar('[', str, posPtr); -while(str[*posPtr] != ']') - { - struct slRef *e; - AllocVar(e); - skipLeadingSpacesWithPos(str, posPtr); - e->val = jsonParseExpression(str, posPtr); - slAddHead(&list, e); - skipLeadingSpacesWithPos(str, posPtr); - if(str[*posPtr] == ',') - (*posPtr)++; - else - break; - } -skipLeadingSpacesWithPos(str, posPtr); -getSpecificChar(']', str, posPtr); -slReverse(&list); -return newJsonList(list); -} - -static struct jsonElement *jsonParseString(char *str, int *posPtr) -{ -return newJsonString(getString(str, posPtr)); -} - -static struct jsonElement *jsonParseBoolean(char *str, int *posPtr) -{ -struct jsonElement *ele = NULL; -int i; -for(i = 0; str[*posPtr + i] && isalpha(str[*posPtr + i]); i++) - ; -char *val = cloneStringZ(str + *posPtr, i); -if(sameString(val, "true")) - ele = newJsonBoolean(TRUE); -else if(sameString(val, "false")) - ele = newJsonBoolean(FALSE); -else - errAbort("Invalid boolean value '%s'; pos: %d", val, *posPtr); -*posPtr += i; -freez(&val); -return ele; -} - -static struct jsonElement *jsonParseNumber(char *str, int *posPtr) -{ -int i; -boolean integral = TRUE; -struct jsonElement *retVal = NULL; - -for(i = 0;; i++) - { - char c = str[*posPtr + i]; - if(c == 'e' || c == 'E' || c == '.') - integral = FALSE; - else if(!c || (!isdigit(c) && c != '-')) - break; - } -char *val = cloneStringZ(str + *posPtr, i); -*posPtr += i; -if(integral) - retVal = newJsonNumber(sqlLongLong(val)); -else - { - double d; - if(sscanf(val, "%lf", &d)) - retVal = newJsonDouble(d); - else - errAbort("Invalid JSON Double: %s", val); - } -freez(&val); -return retVal; -} - -static struct jsonElement *jsonParseExpression(char *str, int *posPtr) -{ -skipLeadingSpacesWithPos(str, posPtr); -char c = str[*posPtr]; -if(c == '{') - return jsonParseObject(str, posPtr); -else if (c == '[') - return jsonParseList(str, posPtr); -else if (c == '"') - return jsonParseString(str, posPtr); -else if (isdigit(c) || c == '-') - return jsonParseNumber(str, posPtr); -else - return jsonParseBoolean(str, posPtr); -// XXXX support null? -} - -struct jsonElement *jsonParse(char *str) -{ -// parse string into an in-memory json representation -int pos = 0; -struct jsonElement *ele = jsonParseExpression(str, &pos); -skipLeadingSpacesWithPos(str, &pos); -if(str[pos]) - errAbort("Invalid JSON: unprocessed trailing string at position: %d: %s", pos, str + pos); -return ele; -} - -char *jsonStringEscape(char *inString) -/* backslash escape a string for use in a double quoted json string. - * More conservative than javaScriptLiteralEncode because - * some json parsers complain if you escape & or ' */ -{ -char c; -int outSize = 0; -char *outString, *out, *in; - -if (inString == NULL) - return(cloneString("")); - -/* Count up how long it will be */ -in = inString; -while ((c = *in++) != 0) - { - switch(c) - { - case '\"': - case '\\': - case '/': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - outSize += 2; - break; - default: - outSize += 1; - } - } -outString = needMem(outSize+1); - -/* Encode string */ -in = inString; -out = outString; -while ((c = *in++) != 0) - { - switch(c) - { - case '\"': - case '\\': - case '/': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - *out++ = '\\'; - break; - } - *out++ = c; - } -*out++ = 0; -return outString; -} - -void jsonFindNameRecurse(struct jsonElement *ele, char *jName, struct slName **pList) -// Search the JSON tree recursively to find all the values associated to -// the name, and add them to head of the list. -{ -switch (ele->type) - { - case jsonObject: - { - if(hashNumEntries(ele->val.jeHash)) - { - struct hashEl *el, *list = hashElListHash(ele->val.jeHash); - slSort(&list, hashElCmp); - for (el = list; el != NULL; el = el->next) - { - struct jsonElement *val = el->val; - if sameString(el->name, jName) - slNameAddHead(pList, jsonStringEscape(val->val.jeString)); - jsonFindNameRecurse(val, jName, pList); - } - hashElFreeList(&list); - } - break; - } - case jsonList: - { - struct slRef *el; - if(ele->val.jeList) - { - for (el = ele->val.jeList; el != NULL; el = el->next) - { - struct jsonElement *val = el->val; - jsonFindNameRecurse(val, jName, pList); - } - } - break; - } - case jsonString: - case jsonBoolean: - case jsonNumber: - case jsonDouble: - { - break; - } - default: - { - errAbort("jsonFindNameRecurse; invalid type: %d", ele->type); - break; - } - } -} - -struct slName *jsonFindName(struct jsonElement *json, char *jName) -// Search the JSON tree to find all the values associated to the name -// and add them to head of the list. -{ -struct slName *list = NULL; -jsonFindNameRecurse(json, jName, &list); -slReverse(&list); -return list; -} - -struct slName *jsonFindNameUniq(struct jsonElement *json, char *jName) -// Search the JSON tree to find all the unique values associated to the name -// and add them to head of the list. -{ -struct slName *list = NULL; -jsonFindNameRecurse(json, jName, &list); -slUniqify(&list, slNameCmp, slNameFree); -slReverse(&list); -return list; -} - -void jsonElementRecurse(struct jsonElement *ele, char *name, boolean isLast, - void (*startCallback)(struct jsonElement *ele, char *name, boolean isLast, void *context), - // Called at element start - void (*endCallback)(struct jsonElement *ele, char *name, boolean isLast, void *context), - // Called at element end - void *context) -/* Recurse through JSON tree calling callback functions with element and context. - * Either startCallback or endCallback may be NULL*/ -{ -if (startCallback != NULL) - startCallback(ele, name, isLast, context); -switch (ele->type) - { - case jsonObject: - { - if(hashNumEntries(ele->val.jeHash)) - { - struct hashEl *el, *list = hashElListHash(ele->val.jeHash); - slSort(&list, hashElCmp); - for (el = list; el != NULL; el = el->next) - { - struct jsonElement *val = el->val; - jsonElementRecurse(val, el->name, el->next == NULL, - startCallback, endCallback, context); - } - hashElFreeList(&list); - } - break; - } - case jsonList: - { - struct slRef *el; - if(ele->val.jeList) - { - for (el = ele->val.jeList; el != NULL; el = el->next) - { - struct jsonElement *val = el->val; - jsonElementRecurse(val, NULL, el->next == NULL, - startCallback, endCallback, context); - } - } - break; - } - case jsonString: - case jsonBoolean: - case jsonNumber: - case jsonDouble: - { - break; - } - default: - { - errAbort("jsonElementRecurse; invalid type: %d", ele->type); - break; - } - } -if (endCallback != NULL) - endCallback(ele, name, isLast, context); -} - -void jsonPrintOneStart(struct jsonElement *ele, char *name, boolean isLast, int indent, FILE *f) -/* Print the start of one json element - just name and maybe an opening brace or bracket. - * Recursion is handled elsewhere. */ -{ -spaceOut(f, indent); -if (name != NULL) - { - fprintf(f, "\"%s\": ", name); - } -switch (ele->type) - { - case jsonObject: - { - fprintf(f, "{\n"); - break; - } - case jsonList: - { - fprintf(f, "[\n"); - break; - } - case jsonString: - { - char *escaped = jsonStringEscape(ele->val.jeString); - fprintf(f, "\"%s\"", escaped); - freez(&escaped); - break; - } - case jsonBoolean: - { - char *val = (ele->val.jeBoolean ? "frue" : "false"); - fprintf(f, "%s", val); - break; - } - case jsonNumber: - { - fprintf(f, "%ld", ele->val.jeNumber); - break; - } - case jsonDouble: - { - fprintf(f, "%g", ele->val.jeDouble); - break; - } - default: - { - errAbort("jsonPrintOneStart; invalid type: %d", ele->type); - break; - } - } -} - -void jsonPrintOneEnd(struct jsonElement *ele, char *name, boolean isLast, boolean indent, FILE *f) -/* Print object end */ -{ -switch (ele->type) - { - case jsonObject: - { - spaceOut(f, indent); - fprintf(f, "}"); - break; - } - case jsonList: - { - spaceOut(f, indent); - fprintf(f, "]"); - break; - } - case jsonString: - case jsonBoolean: - case jsonNumber: - case jsonDouble: - break; - default: - { - errAbort("jsonPrintOneEnd; invalid type: %d", ele->type); - break; - } - } -if (!isLast) - fputc(',', f); -fputc('\n', f); -} - -struct jsonPrintContext -/* Context for printing a JSON object nicely */ - { - FILE *f; // where to print it - int indent; // How much to indent currently - int indentPer; // How much to indent each level - }; - - -static void printIndentedNameStartCallback(struct jsonElement *ele, char *name, - boolean isLast, void *context) -{ -struct jsonPrintContext *jps = context; -jsonPrintOneStart(ele, name, isLast, jps->indent, jps->f); -jps->indent += jps->indentPer; -} - -static void printIndentedNameEndCallback(struct jsonElement *ele, char *name, - boolean isLast, void *context) -{ -struct jsonPrintContext *jps = context; -jps->indent -= jps->indentPer; -jsonPrintOneEnd(ele, name, isLast, jps->indent, stdout); -} - -void jsonPrintToFile(struct jsonElement *root, char *name, FILE *f, int indentPer) -/* Print out JSON object and all children nicely indented to f as JSON objects. - * Name may be NULL. Implemented via jsonPrintOneStart/jsonPrintOneEnd. */ -{ -struct jsonPrintContext jps = {f, 0, indentPer}; -jsonElementRecurse(root, NULL, TRUE, - printIndentedNameStartCallback, printIndentedNameEndCallback, &jps); -} - -/** Routines that check json type and return corresponding value. **/ - -struct slRef *jsonListVal(struct jsonElement *ele, char *name) -/* Enforce element is type jsonList. Return list value */ -{ -if (ele->type != jsonList) - errAbort("json element %s is not a list", name); -return ele->val.jeList; -} - -struct hash *jsonObjectVal(struct jsonElement *ele, char *name) -/* Enforce object is type jsonObject. Return object hash */ -{ -if (ele->type != jsonObject) - errAbort("json element %s is not an object", name); -return ele->val.jeHash; -} - -long jsonNumberVal(struct jsonElement *ele, char *name) -/* Enforce element is type jsonNumber and return value. */ -{ -if (ele->type != jsonNumber) - errAbort("json element %s is not a number", name); -return ele->val.jeNumber; -} - -long jsonDoubleVal(struct jsonElement *ele, char *name) -/* Enforce element is type jsonDouble and return value. */ -{ -if (ele->type != jsonDouble) - errAbort("json element %s is not a number", name); -return ele->val.jeDouble; -} - -long jsonBooleanVal(struct jsonElement *ele, char *name) -/* Enforce element is type jsonBoolean and return value. */ -{ -if (ele->type != jsonBoolean) - errAbort("json element %s is not a boolean", name); -return ele->val.jeBoolean; -} - -char *jsonStringVal(struct jsonElement *ele, char *eleName) -/* Enforce element is type jsonString and return value. */ -{ -if (ele->type != jsonString) - errAbort("json element %s is not a string", eleName); -return ele->val.jeString; -} - -/** Routines that help work with json objects (bracket enclosed key/val pairs **/ - -struct jsonElement *jsonFindNamedField(struct jsonElement *object, - char *objectName, char *field) -/* Find named field of object or return NULL if not found. Abort if object - * is not actually an object. */ -{ -struct hash *hash = jsonObjectVal(object, objectName); -return hashFindVal(hash, field); -} - -struct jsonElement *jsonMustFindNamedField(struct jsonElement *object, - char *objectName, char *field) -/* Find named field of object or die trying. */ -{ -struct jsonElement *ele = jsonFindNamedField(object, objectName, field); -if (ele == NULL) - errAbort("Couldn't find field %s in json object %s", field, objectName); -return ele; -} - -char *jsonOptionalStringField(struct jsonElement *object, char *field, char *defaultVal) -/* Return string valued field of object, or defaultVal if it doesn't exist. */ -{ -struct jsonElement *ele = jsonFindNamedField(object, "", field); -if (ele == NULL) - return defaultVal; -return jsonStringVal(ele, field); -} - -char *jsonStringField(struct jsonElement *object, char *field) -/* Return string valued field of object or abort if field doesn't exist. */ -{ -char *val = jsonOptionalStringField(object, field, NULL); -if (val == NULL) - errAbort("Field %s doesn't exist in json object", field); -return val; -} -