/* * * json_writer.c * * pgpool: a language independent connection pool server for PostgreSQL * written by Tatsuo Ishii * * Copyright (c) 2003-2015 PgPool Global Development Group * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that copyright notice and this permission * notice appear in supporting documentation, and that the name of the * author not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. The author makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * */ #include #include "parser/stringinfo.h" #include "utils/palloc.h" #include "utils/json_writer.h" static void jw_put_string_escape(JsonNode * jNode, char *string); static inline int jw_get_current_element_count(JsonNode * jNode); static inline void jw_inc_current_element_count(JsonNode * jNode); static inline JWElementType jw_get_current_element_type(JsonNode * jNode); JsonNode * jw_create(JWElementType rootElement, bool pretty_output) { JsonNode *jNode = palloc(sizeof(JsonNode)); jNode->buf = makeStringInfo(); jNode->stack_ptr = 0; jNode->pretty = pretty_output; jw_start_element(jNode, rootElement, NULL); return jNode; } JsonNode * jw_create_with_array(bool pretty_output) { return jw_create(JWARRAY, pretty_output); } JsonNode * jw_create_with_object(bool pretty_output) { return jw_create(JWOBJECT, pretty_output); } bool jw_put_string(JsonNode * jNode, char *key, char *value) { if (key == NULL || value == NULL) return false; if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWOBJECT) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); jw_put_string_escape(jNode, key); appendStringInfoChar(jNode->buf, ':'); jw_put_string_escape(jNode, value); jw_inc_current_element_count(jNode); return true; } static void jw_put_string_escape(JsonNode * jNode, char *string) { int i; appendStringInfoChar(jNode->buf, '"'); for (i = 0; string[i] != '\0'; i++) { switch (string[i]) { case '\"': appendStringInfo(jNode->buf, "\\\""); break; case '\\': appendStringInfo(jNode->buf, "\\\\"); break; case '\b': appendStringInfo(jNode->buf, "\\b"); break; case '\f': appendStringInfo(jNode->buf, "\\f"); break; case '\n': appendStringInfo(jNode->buf, "\\n"); break; case '\r': appendStringInfo(jNode->buf, "\\r"); break; case '\t': appendStringInfo(jNode->buf, "\\t"); break; default: appendStringInfoChar(jNode->buf, string[i]); break; } } appendStringInfoChar(jNode->buf, '"'); } /* for compatibility reasons we pack bool in int*/ bool jw_put_bool(JsonNode * jNode, char *key, bool value) { return jw_put_int(jNode, key, value ? 1 : 0); } bool jw_put_int(JsonNode * jNode, char *key, int value) { if (key == NULL) return false; if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWOBJECT) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); appendStringInfo(jNode->buf, "\"%s\":%d", key, value); jw_inc_current_element_count(jNode); return true; } bool jw_put_long(JsonNode * jNode, char *key, long value) { if (key == NULL) return false; if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWOBJECT) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); appendStringInfo(jNode->buf, "\"%s\":%ld", key, value); jw_inc_current_element_count(jNode); return true; } bool jw_put_null(JsonNode * jNode, char *key) { if (key == NULL) return false; if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWOBJECT) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); appendStringInfo(jNode->buf, "\"%s\":null", key); jw_inc_current_element_count(jNode); return true; } bool jw_put_string_value(JsonNode * jNode, char *value) { if (value == NULL) return false; if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWARRAY) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); appendStringInfo(jNode->buf, "\"%s\"", value); jw_inc_current_element_count(jNode); return true; } bool jw_put_bool_value(JsonNode * jNode, bool value) { return jw_put_int_value(jNode, value ? 1 : 0); } bool jw_put_int_value(JsonNode * jNode, int value) { if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWARRAY) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); appendStringInfo(jNode->buf, "%d", value); jw_inc_current_element_count(jNode); return true; } bool jw_put_long_value(JsonNode * jNode, long value) { if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWARRAY) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); appendStringInfo(jNode->buf, "%ld", value); jw_inc_current_element_count(jNode); return true; } bool jw_put_null_value(JsonNode * jNode) { if (jw_get_current_element_count(jNode) < 0) return false; if (jw_get_current_element_type(jNode) != JWARRAY) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); appendStringInfoString(jNode->buf, "null"); jw_inc_current_element_count(jNode); return true; } bool jw_start_element(JsonNode * jNode, JWElementType element, char *key) { char ch; if (jNode->stack_ptr >= MAX_STACK_DEPTH) return false; if (jw_get_current_element_count(jNode) > 0) appendStringInfoChar(jNode->buf, ','); if (element == JWOBJECT) { ch = '{'; } else if (element == JWARRAY) { ch = '['; } else return false; if (key == NULL) appendStringInfoChar(jNode->buf, ch); else appendStringInfo(jNode->buf, "\"%s\": %c", key, ch); jw_inc_current_element_count(jNode); jNode->stack[jNode->stack_ptr].elementCount = 0; jNode->stack[jNode->stack_ptr++].elementType = element; return true; } bool jw_start_array(JsonNode * jNode, char *key) { if (jw_get_current_element_type(jNode) == JWARRAY) return jw_start_element(jNode, JWARRAY, NULL); return jw_start_element(jNode, JWARRAY, key); } bool jw_start_object(JsonNode * jNode, char *key) { if (jw_get_current_element_type(jNode) == JWARRAY) return jw_start_element(jNode, JWOBJECT, NULL); return jw_start_element(jNode, JWOBJECT, key); } bool jw_end_element(JsonNode * jNode) { char ch; JWElementType element; if (jNode->stack_ptr <= 0) return false; /* pop the element from stack */ element = jNode->stack[--jNode->stack_ptr].elementType; if (element == JWOBJECT) ch = '}'; else if (element == JWARRAY) ch = ']'; else return false; appendStringInfoChar(jNode->buf, ch); return true; } bool jw_finish_document(JsonNode * jNode) { while (jNode->stack_ptr > 0) { if (!jw_end_element(jNode)) return false; } return true; } char * jw_get_json_string(JsonNode * jNode) { return jNode->buf->data; } int jw_get_json_length(JsonNode * jNode) { return jNode->buf->len; } void jw_destroy(JsonNode * jNode) { pfree(jNode->buf->data); pfree(jNode->buf); pfree(jNode); } static inline int jw_get_current_element_count(JsonNode * jNode) { if (jNode->stack_ptr <= 0) return -1; return jNode->stack[jNode->stack_ptr - 1].elementCount; } static inline void jw_inc_current_element_count(JsonNode * jNode) { if (jNode->stack_ptr <= 0) return; jNode->stack[jNode->stack_ptr - 1].elementCount++; } static inline JWElementType jw_get_current_element_type(JsonNode * jNode) { if (jNode->stack_ptr <= 0) return -1; return jNode->stack[jNode->stack_ptr - 1].elementType; }