cc9a6a6dcdca9811dfeb5ac6d9f588e836407f01 larrym Tue Apr 17 23:16:38 2012 -0700 add jsonParse; refactor some existing code to accomodate parsed data diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c index 5720a2e..dcf2124 100644 --- src/hg/lib/jsHelper.c +++ src/hg/lib/jsHelper.c @@ -467,30 +467,57 @@ void jsEndCollapsibleSection() /* End the collapsible . */ { puts(""); } struct jsonStringElement *newJsonString(char *str) { struct jsonStringElement *ele; AllocVar(ele); ele->str = cloneString(str); ele->type = jsonString; return ele; } +struct jsonBooleanElement *newJsonBoolean(boolean val) +{ +struct jsonBooleanElement *ele; +AllocVar(ele); +ele->val = val; +ele->type = jsonBoolean; +return ele; +} + +struct jsonNumberElement *newJsonNumber(long val) +{ +struct jsonNumberElement *ele; +AllocVar(ele); +ele->val = val; +ele->type = jsonNumber; +return ele; +} + +struct jsonDoubleElement *newJsonDouble(double val) +{ +struct jsonDoubleElement *ele; +AllocVar(ele); +ele->val = val; +ele->type = jsonDouble; +return ele; +} + struct jsonHashElement *newJsonHash(struct hash *h) { struct jsonHashElement *ele; AllocVar(ele); ele->type = jsonHash; ele->hash = h; return ele; } struct jsonListElement *newJsonList(struct slRef *list) { struct jsonListElement *ele; AllocVar(ele); ele->type = jsonList; ele->list = list; @@ -500,91 +527,75 @@ void jsonHashAdd(struct jsonHashElement *h, char *name, struct jsonElement *ele) { if (h == NULL) // If hash isn't provided, assume global { if (jsonGlobalsHash == NULL) jsonGlobalsHash = newJsonHash(newHash(8)); h = jsonGlobalsHash; } hashReplace(h->hash, name, ele); } void jsonHashAddString(struct jsonHashElement *h, char *name, char *val) { // Add a string to a hash which will be used to print a javascript object; // existing values are replaced. -val = javaScriptLiteralEncode(val); -char *str = needMem(strlen(val) + 3); -sprintf(str, "\"%s\"", val); -freez(&val); -jsonHashAdd(h, name, (struct jsonElement *) newJsonString(str)); +jsonHashAdd(h, name, (struct jsonElement *) newJsonString(val)); } void jsonHashAddNumber(struct jsonHashElement *h, char *name, long val) { // Add a number to a hash which will be used to print a javascript object; // existing values are replaced. -char buf[256]; -safef(buf, sizeof(buf), "%ld", val); -jsonHashAdd(h, name, (struct jsonElement *) newJsonString(buf)); +jsonHashAdd(h, name, (struct jsonElement *) newJsonNumber(val)); } void jsonHashAddDouble(struct jsonHashElement *h, char *name, double val) { // Add a number to a hash which will be used to print a javascript object; // existing values are replaced. -char buf[256]; -safef(buf, sizeof(buf), "%.10f", val); -jsonHashAdd(h, name, (struct jsonElement *) newJsonString(buf)); +jsonHashAdd(h, name, (struct jsonElement *) newJsonDouble(val)); } void jsonHashAddBoolean(struct jsonHashElement *h, char *name, boolean val) { // Add a boolean to a hash which will be used to print a javascript object; // existing values are replaced. -jsonHashAdd(h, name, (struct jsonElement *) newJsonString(val ? "true" : "false")); +jsonHashAdd(h, name, (struct jsonElement *) newJsonBoolean(val)); } void jsonListAdd(struct slRef **list, struct jsonElement *ele) { struct slRef *e; AllocVar(e); e->val = ele; slAddHead(list, e); } void jsonListAddString(struct slRef **list, char *val) { -val = javaScriptLiteralEncode(val); -char *str = needMem(strlen(val) + 3); -sprintf(str, "'%s'", val); -freez(&val); -jsonListAdd(list, (struct jsonElement *) newJsonString(str)); +jsonListAdd(list, (struct jsonElement *) newJsonString(val)); } void jsonListAddNumber(struct slRef **list, long val) { -char buf[256]; -safef(buf, sizeof(buf), "%ld", val); -jsonListAdd(list, (struct jsonElement *) newJsonString(buf)); +jsonListAdd(list, (struct jsonElement *) newJsonNumber(val)); } void jsonListAddDouble(struct slRef **list, double val) { -char buf[256]; -safef(buf, sizeof(buf), "%.10f", val); -jsonListAdd(list, (struct jsonElement *) newJsonString(buf)); +jsonListAdd(list, (struct jsonElement *) newJsonDouble(val)); } void jsonListAddBoolean(struct slRef **list, boolean val) { jsonListAdd(list, (struct jsonElement *) newJsonString(val ? "true" : "false")); } static char *makeIndentBuf(int indentLevel) { if (indentLevel < 0) return ""; char *indentBuf; indentBuf = needMem(indentLevel + 1); memset(indentBuf, '\t', indentLevel); indentBuf[indentLevel] = 0; @@ -633,31 +644,51 @@ if(rec->list) { for (el = rec->list; el != NULL; el = el->next) { struct jsonElement *val = (struct jsonElement *) 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", ((struct jsonStringElement *) json)->str); + char *val = javaScriptLiteralEncode(((struct jsonStringElement *) json)->str); + hPrintf("\"%s\"", val); + break; + } + case jsonBoolean: + { + hPrintf("%s", ((struct jsonBooleanElement *) json)->val ? "true" : "false"); + break; + } + case jsonNumber: + { + char buf[256]; + safef(buf, sizeof(buf), "%ld", ((struct jsonNumberElement *) json)->val); + hPrintf("%s", buf); + break; + } + case jsonDouble: + { + char buf[256]; + safef(buf, sizeof(buf), "%.10f", ((struct jsonDoubleElement *) json)->val); + hPrintf("%s", buf); break; } default: { errAbort("jsonPrintRecurse; invalid type: %d", json->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 @@ -695,15 +726,165 @@ } 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, javaScriptLiteralEncode(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 struct jsonElement *jsonParseExpression(char *str, int *posPtr); + +static struct jsonElement *jsonParseObject(char *str, int *posPtr) +{ +struct hash *h = newHash(0); +getSpecificChar('{', str, posPtr); +while(str[*posPtr] != '}') + { + int i; + skipLeadingSpacesWithPos(str, posPtr); + getSpecificChar('"', str, posPtr); + for(i = 0; str[*posPtr + i] && str[*posPtr + i] != '"'; i++) + ; + char *name = cloneStringZ(str + *posPtr, i); + *posPtr += i; + getSpecificChar('"', 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 (struct jsonElement *) newJsonHash(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 (struct jsonElement *) newJsonList(list); +} + +static struct jsonElement *jsonParseString(char *str, int *posPtr) +{ +int i; +getSpecificChar('"', str, posPtr); +for(i = 0; str[*posPtr + i] && str[*posPtr + i] != '"'; i++) + ; +char *val = cloneStringZ(str + *posPtr, i); +*posPtr += i; +getSpecificChar('"', str, posPtr); +return (struct jsonElement *) newJsonString(val); +} + +static struct jsonElement *jsonParseBoolean(char *str, int *posPtr) +{ +struct jsonBooleanElement *ele; +int i; +for(i = 0; str[*posPtr + i] && isalpha(str[*posPtr + i]); i++); + ; +char *val = cloneStringZ(str + *posPtr, i); +if(sameWord(val, "true")) + ele = newJsonBoolean(TRUE); +else if(sameWord(val, "false")) + ele = newJsonBoolean(FALSE); +else + errAbort("Invalid boolean value '%s'; pos: %d", val, *posPtr); +*posPtr += i; +return (struct jsonElement *) ele; +} + +static struct jsonElement *jsonParseNumber(char *str, int *posPtr) +{ +int i; +for(i = 0;; i++) + { + char c = str[*posPtr + i]; + if(!(c && (isdigit(c) || c == '.' || c == '-' || c == '+'))) + break; + } +char *val = cloneStringZ(str + *posPtr, i); +*posPtr += i; +if(strchr(val, '.') == NULL) + return (struct jsonElement *) newJsonNumber(sqlLongLong(val)); +else + return (struct jsonElement *) newJsonDouble(sqlDouble(val)); +} + +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; +}