/* * JsonbToCString * Converts jsonb value to a C-string. * * If 'out' argument is non-null, the resulting C-string is stored inside the * StringBuffer. The resulting string is always returned. * * A typical case for passing the StringInfo in rather than NULL is where the * caller wants access to the len attribute without having to call strlen, e.g. * if they are converting it to a text* object. */ char * JsonbToCString(StringInfo out, JsonbSuperHeader in, int estimated_len) { bool first = true; JsonbIterator *it; int type = 0; JsonbValue v; int level = 0; bool redo_switch = false; if (out == NULL) out = makeStringInfo(); enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64); it = JsonbIteratorInit(in); while (redo_switch || ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)) { redo_switch = false; switch (type) { case WJB_BEGIN_ARRAY: if (!first) appendBinaryStringInfo(out, ", ", 2); first = true; if (!v.val.array.rawScalar) appendStringInfoChar(out, '['); level++; break; case WJB_BEGIN_OBJECT: if (!first) appendBinaryStringInfo(out, ", ", 2); first = true; appendStringInfoCharMacro(out, '{'); level++; break; case WJB_KEY: if (!first) appendBinaryStringInfo(out, ", ", 2); first = true; /* json rules guarantee this is a string */ jsonb_put_escaped_value(out, &v); appendBinaryStringInfo(out, ": ", 2); type = JsonbIteratorNext(&it, &v, false); if (type == WJB_VALUE) { first = false; jsonb_put_escaped_value(out, &v); } else { Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY); /* * We need to rerun the current switch() since we need to * output the object which we just got from the iterator * before calling the iterator again. */ redo_switch = true; } break; case WJB_ELEM: if (!first) appendBinaryStringInfo(out, ", ", 2); else first = false; jsonb_put_escaped_value(out, &v); break; case WJB_END_ARRAY: level--; if (!v.val.array.rawScalar) appendStringInfoChar(out, ']'); first = false; break; case WJB_END_OBJECT: level--; appendStringInfoCharMacro(out, '}'); first = false; break; default: elog(ERROR, "unknown flag of jsonb iterator"); } } Assert(level == 0); return out->data; }
/* * common worker for above two functions */ static char * JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent) { bool first = true; JsonbIterator *it; JsonbValue v; JsonbIteratorToken type = WJB_DONE; int level = 0; bool redo_switch = false; /* If we are indenting, don't add a space after a comma */ int ispaces = indent ? 1 : 2; /* * Don't indent the very first item. This gets set to the indent flag at * the bottom of the loop. */ bool use_indent = false; bool raw_scalar = false; bool last_was_key = false; if (out == NULL) out = makeStringInfo(); enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64); it = JsonbIteratorInit(in); while (redo_switch || ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)) { redo_switch = false; switch (type) { case WJB_BEGIN_ARRAY: if (!first) appendBinaryStringInfo(out, ", ", ispaces); if (!v.val.array.rawScalar) { add_indent(out, use_indent && !last_was_key, level); appendStringInfoCharMacro(out, '['); } else raw_scalar = true; first = true; level++; break; case WJB_BEGIN_OBJECT: if (!first) appendBinaryStringInfo(out, ", ", ispaces); add_indent(out, use_indent && !last_was_key, level); appendStringInfoCharMacro(out, '{'); first = true; level++; break; case WJB_KEY: if (!first) appendBinaryStringInfo(out, ", ", ispaces); first = true; add_indent(out, use_indent, level); /* json rules guarantee this is a string */ jsonb_put_escaped_value(out, &v); appendBinaryStringInfo(out, ": ", 2); type = JsonbIteratorNext(&it, &v, false); if (type == WJB_VALUE) { first = false; jsonb_put_escaped_value(out, &v); } else { Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY); /* * We need to rerun the current switch() since we need to * output the object which we just got from the iterator * before calling the iterator again. */ redo_switch = true; } break; case WJB_ELEM: if (!first) appendBinaryStringInfo(out, ", ", ispaces); first = false; if (!raw_scalar) add_indent(out, use_indent, level); jsonb_put_escaped_value(out, &v); break; case WJB_END_ARRAY: level--; if (!raw_scalar) { add_indent(out, use_indent, level); appendStringInfoCharMacro(out, ']'); } first = false; break; case WJB_END_OBJECT: level--; add_indent(out, use_indent, level); appendStringInfoCharMacro(out, '}'); first = false; break; default: elog(ERROR, "unknown jsonb iterator token type"); } use_indent = indent; last_was_key = redo_switch; } Assert(level == 0); return out->data; }