summaryrefslogtreecommitdiff
path: root/json.h
blob: 63b0250092b5149c049e3ce67c618ec3b8737264 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
  Copyright (C) 2010 Joseph A. Adams (joeyadams3.14159@gmail.com)
  All rights reserved.

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

#ifndef JSON_H
#define JSON_H

#include "postgres.h"

#include "access/heapam.h"
#include "access/htup.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"

typedef struct varlena jsontype;

#define DatumGetJSONP(X)	((jsontype *) PG_DETOAST_DATUM(X))
#define JSONPGetDatum(X)	PointerGetDatum(X)

#define PG_GETARG_JSON_P(n)	DatumGetJSONP(PG_GETARG_DATUM(n))
#define PG_RETURN_JSON_P(x)	PG_RETURN_POINTER(x)

/* Keep the order of these enum entries in sync with
 * enum_type_names[] in json_ops.c . */
typedef enum {
	JSON_NULL,
	JSON_STRING,
	JSON_NUMBER,
	JSON_BOOL,
	JSON_OBJECT,
	JSON_ARRAY,
	JSON_TYPE_COUNT = JSON_ARRAY + 1,
	
	JSON_INVALID
} json_type;

#define json_type_is_valid(type) ((type) >= 0 && (type) < JSON_TYPE_COUNT)

typedef struct json_node json_node;

struct json_node {
	json_type type;
	
	union {
		/* JSON_BOOL */
		bool v_bool;
		
		/* JSON_STRING */
		struct {
			char *str;
			size_t length;
		} string;
		
		/* JSON_NUMBER */
		char *number;
		
		/* JSON_ARRAY or JSON_OBJECT (children) */
		struct {
			json_node *head, *tail;
			size_t count;
		} children;
	} v;
	
	json_node *parent;
	json_node *prev, *next;
	
	char *key;
	size_t key_length;
	/* If node is a member of an object, key will be set.
	   Otherwise, key will be null. */
	
	struct json_node_orig {
		/* These only apply if this node is a member of an object. */
		struct {
			const char *start, *end;
		} key_left_space, key, key_right_space;

		struct {
			const char *start, *end;
		} left_space, value, right_space;
	} orig;

	/* Used by jp_set to indicate we should not visit this node again. */
	bool jp_changed;
};


bool json_validate(const char *str);
bool json_validate_liberal(const char *str);
json_node *json_decode(const char *str);

#define JSONOPT_USE_ORIG		1
#define JSONOPT_ESCAPE_UNICODE	2
#define JSONOPT_NO_TRIM			4
char *json_encode(json_node *node, int options);

/* Determines the type of a JSON string without fully decoding it.
 * Expects the given string to be valid JSON string.
 * Might return JSON_INVALID if something is wrong with the input. */
json_type json_text_type(const char *str, size_t nbytes);

/* Filter almost-JSON text to try to make it JSON.  On failure,
 * returns NULL.  On success, the text may or may not be valid JSON;
 * you still have to run it through json_validate or json_decode
 * to see.
 *
 * Free the result with free() when you're done with it. */
char *json_cleanup(const char *str);

/*
 * Free a JSON node and all its descendants.
 *
 * Do not use this function if you have performed json_replace_value on
 * a descendant, as this function relies on each node's ->parent field
 * being trustworthy.
 */
void json_delete(json_node *node);

#define json_foreach(child, parent) \
	for ((child) = json_head(parent); (child) != NULL; (child) = (child)->next)

static inline json_node *json_head(json_node *parent)
{
	switch (parent->type) {
		case JSON_ARRAY:
		case JSON_OBJECT:
			return parent->v.children.head;
		default:
			return NULL;
	}
}


/*
 * Decodes a JSON-encoded string literal
 *    (If you're interested in the decoding JSON in general, see json_decode).
 * If strict is true, string must be double-quoted,
 * as is required by the JSON RFC.
 * Otherwise, the string may be single- or double-quoted.
 * Also, no whitespace skipping is done, so the caller should only
 * call this function when it expects **sp to be either " or '
 *
 * On success, returns the decoded string and passes that string's length
 * through *length (which must not be NULL).  On failure (parse error),
 * returns NULL and leaves *length untouched.
 */
char *json_decode_string(const char **sp, size_t *length, bool strict);

/*
 * Encodes a string literal JSON-style using the given quote character,
 * only escaping characters when necessary
 *    (If you're interested in encoding JSON in general, see json_encode).
 * Note that using anything but '"' as the quote character will result in
 * invalid JSON.
 *
 * Returns NULL if input is invalid UTF-8 or if an invalid quote character
 * (such as backslash) is given.
 */
char *json_encode_string(const char *str, size_t length, char quote, bool escape_unicode);

/* Add child to parent, putting it at the end. */
void json_append(json_node *parent, json_node *child);

/* Remove node from its parent. */
void json_remove(json_node *node);

/* Update the value of a node, preserving position and key information. */
void json_replace_value(json_node *node, json_node *replacement);

/* Note that the factory functions and get/set functions do not validate input.
 * However, json_encode validates node contents to avoid producing
 * invalid JSON. */

/* Node factory functions */
json_node *json_mknode(json_type type);
json_node *json_mkbool(bool v_bool);
json_node *json_mkstring(const char *str, size_t length);
json_node *json_mknumber(const char *number, size_t length);
static inline json_node *json_mkarray(void) {
	return json_mknode(JSON_ARRAY);
}
static inline json_node *json_mkobject(void) {
	return json_mknode(JSON_OBJECT);
}

void json_touch_value(json_node *node);

/* Node value get/set functions. */
static inline bool json_get_bool(json_node *node) {
	Assert(node->type == JSON_BOOL);
	return node->v.v_bool;
}
static inline void json_set_bool(json_node *node, bool v_bool) {
	Assert(node->type == JSON_BOOL);
	node->v.v_bool = v_bool;
	json_touch_value(node);
}
const char *json_get_string(json_node *node, size_t *length_out);
void json_set_string(json_node *node, const char *str, size_t length);
const char *json_get_number(json_node *node);
void json_set_number(json_node *node, const char *number, size_t length);

/* Utility function used by json_get to automatically apply from_json to its result. */
const char *from_json_cstring(const char *input, const char *funcname);

#endif