a4311d22ec503f936c0b624c13b1349c590a01ea
angie
  Wed Apr 20 12:01:26 2016 -0700
Added an errCatch to cartJsonExecute so that instead of returning invalid JSON where there's an errAbort, we can close out the JSON-in-progress (if any) and add an error message to the JSON response -- then the user will at least see the error message instead of a parser error alert.  This required extending jsonWrite's objStack so that in addition to tracking whether a comma is needed, it now tracks whether each stack item is an object or a list.  Then, when necessary we can tell jsonWrite to pop up to a certain level (in this case, level 1 so we can include an error string in the response object) by closing out any lists or objects that are deeper than that level.  The new flag also allows us to test for errors like ending a list when we need to end an object or vice versa.

diff --git src/lib/jsonWrite.c src/lib/jsonWrite.c
index ce342fc..5be896f 100644
--- src/lib/jsonWrite.c
+++ src/lib/jsonWrite.c
@@ -24,56 +24,62 @@
 jw->dy = dyStringNew(0);
 return jw;
 }
 
 void jsonWriteFree(struct jsonWrite **pJw)
 /* Free up a jsonWrite object. */
 {
 struct jsonWrite *jw = *pJw;
 if (jw != NULL)
     {
     dyStringFree(&jw->dy);
     freez(pJw);
     }
 }
 
-static void jsonWritePushObjStack(struct jsonWrite *jw, bool val)
-/* Push val on stack */
+static void jsonWritePushObjStack(struct jsonWrite *jw, bool isNotEmpty, bool isObject)
+/* Push a new object or list on stack */
 {
 int stackIx = jw->stackIx + 1;
 if (stackIx >= ArraySize(jw->objStack))
     errAbort("Stack overflow in jsonWritePush");
-jw->objStack[stackIx] = val;
+jw->objStack[stackIx].isNotEmpty = isNotEmpty;
+jw->objStack[stackIx].isObject = isObject;
 jw->stackIx = stackIx;
 }
 
-static void jsonWritePopObjStack(struct jsonWrite *jw)
+static void jsonWritePopObjStack(struct jsonWrite *jw, bool isObject)
 /* pop object stack and just discard val. */
 {
+boolean topIsObject = jw->objStack[jw->stackIx].isObject;
+if (topIsObject != isObject)
+    errAbort("jsonWrite: expected to close %s but was told to close %s",
+             topIsObject ? "object" : "list",
+             isObject ? "object" : "list");
 int stackIx = jw->stackIx - 1;
 if (stackIx < 0)
     errAbort("Stack underflow in jsonWritePopObjStack");
 jw->stackIx = stackIx;
 }
 
 INLINE void jsonWriteMaybeComma(struct jsonWrite *jw)
 /* If this is not the first item added to an object or list, write a comma. */
 {
-if (jw->objStack[jw->stackIx] != 0)
+if (jw->objStack[jw->stackIx].isNotEmpty)
     dyStringAppend(jw->dy, ","JW_SEP);
 else
-    jw->objStack[jw->stackIx] = 1;
+    jw->objStack[jw->stackIx].isNotEmpty = TRUE;
 }
 
 void jsonWriteTag(struct jsonWrite *jw, char *var)
 /* Print out preceding comma if necessary, and if var is non-NULL, quoted tag followed by colon. */
 {
 jsonWriteMaybeComma(jw);
 if (var != NULL)
     dyStringPrintf(jw->dy, "\"%s\": ", var);
 }
 
 void jsonWriteString(struct jsonWrite *jw, char *var, char *string)
 /* Print out "var": "val".  If var is NULL, print val only.  If string is NULL, "var": null . */
 {
 jsonWriteTag(jw, var);
 if (string)
@@ -122,56 +128,56 @@
 void jsonWriteLinkNum(struct jsonWrite *jw, char *var, char *objRoot, long long id)
 /* Print out the jsony type link to another object with a numerical id.  objRoot will start 
  * and end with a '/' and may have additional slashes in this usage. Var may be NULL */
 {
 struct dyString *dy = jw->dy;
 jsonWriteTag(jw, var);
 dyStringPrintf(dy, "\"%s%lld\"", objRoot, id);
 }
 
 void jsonWriteListStart(struct jsonWrite *jw, char *var)
 /* Start an array in JSON. Var may be NULL */
 {
 struct dyString *dy = jw->dy;
 jsonWriteTag(jw, var);
 dyStringAppend(dy, "["JW_SEP);
-jsonWritePushObjStack(jw, FALSE);
+jsonWritePushObjStack(jw, FALSE, FALSE);
 }
 
 void jsonWriteListEnd(struct jsonWrite *jw)
 /* End an array in JSON */
 {
 struct dyString *dy = jw->dy;
 dyStringAppend(dy, "]"JW_SEP);
-jsonWritePopObjStack(jw);
+jsonWritePopObjStack(jw, FALSE);
 }
 
 void jsonWriteObjectStart(struct jsonWrite *jw, char *var)
 /* Print start of object, preceded by tag if var is non-NULL. */
 {
 jsonWriteTag(jw, var);
 struct dyString *dy = jw->dy;
 dyStringAppend(dy, "{"JW_SEP);
-jsonWritePushObjStack(jw, FALSE);
+jsonWritePushObjStack(jw, FALSE, TRUE);
 }
 
 void jsonWriteObjectEnd(struct jsonWrite *jw)
 /* End object in JSON */
 {
 struct dyString *dy = jw->dy;
 dyStringAppend(dy, "}"JW_SEP);
-jsonWritePopObjStack(jw);
+jsonWritePopObjStack(jw, TRUE);
 }
 
 void jsonWriteStringf(struct jsonWrite *jw, char *var, char *format, ...)
 /* Write "var": "val" where val is jsonStringEscape'd formatted string. */
 {
 // Since we're using jsonStringEscape(), we need to use a temporary dyString
 // instead of jw->dy.
 struct dyString *tmpDy = dyStringNew(0);
 va_list args;
 va_start(args, format);
 dyStringVaPrintf(tmpDy, format, args);
 va_end(args);
 char *escaped = jsonStringEscape(tmpDy->string);
 jsonWriteString(jw, var, escaped);
 freeMem(escaped);
@@ -212,15 +218,31 @@
 
 void jsonWriteAppend(struct jsonWrite *jwA, char *var, struct jsonWrite *jwB)
 /* Append jwB's contents to jwA's.  If jwB is non-NULL, it must be fully closed (no unclosed
  * list or object).  If var is non-NULL, write it out as a tag before appending.
  * If both var and jwB are NULL, leave jwA unchanged. */
 {
 if (jwB && jwB->stackIx)
     errAbort("jsonWriteAppend: second argument must be fully closed but its stackIx is %d not 0",
              jwB->stackIx);
 jsonWriteTag(jwA, var);
 if (jwB)
     dyStringAppendN(jwA->dy, jwB->dy->string, jwB->dy->stringSize);
 else if (var)
     dyStringAppend(jwA->dy, "null");
 }
+
+int jsonWritePopToLevel(struct jsonWrite *jw, uint level)
+/* Close out the objects and lists that are deeper than level, so we end up at level ready to
+ * add new items.  Return the level that we end up with, which may not be the same as level,
+ * if level is deeper than the current stack. */
+{
+int i;
+for (i = jw->stackIx;  i > level;  i--)
+    {
+    if (jw->objStack[i].isObject)
+        jsonWriteObjectEnd(jw);
+    else
+        jsonWriteListEnd(jw);
+    }
+return jw->stackIx;
+}