1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
#include "json_document.h" |
6 |
|
|
|
7 |
|
|
#include <cassert> |
8 |
|
|
#include <cstdlib> |
9 |
|
|
#include <cstring> |
10 |
|
|
|
11 |
|
|
#include "logging.h" |
12 |
|
|
#include "util/pointer.h" |
13 |
|
|
#include "util/string.h" |
14 |
|
|
|
15 |
|
|
using namespace std; // NOLINT |
16 |
|
|
|
17 |
|
7 |
bool ToJsonString(const JsonStringInput &input, std::string *output) { |
18 |
✗✓ |
7 |
if (!output) { |
19 |
|
|
return false; |
20 |
|
|
} |
21 |
|
|
|
22 |
|
7 |
output->clear(); |
23 |
|
7 |
*output = "{"; |
24 |
✓✓ |
23 |
for (size_t i = 0u; i < input.size(); ++i) { |
25 |
|
|
*output += |
26 |
|
16 |
std::string("\"") + input[i].first + "\":\"" + input[i].second + "\""; |
27 |
✓✓ |
16 |
if (i < input.size() - 1) { |
28 |
|
9 |
*output += ','; |
29 |
|
|
} |
30 |
|
|
} |
31 |
|
7 |
*output += std::string("}"); |
32 |
|
|
|
33 |
|
7 |
return true; |
34 |
|
|
} |
35 |
|
|
|
36 |
|
315 |
JsonDocument *JsonDocument::Create(const string &text) { |
37 |
|
315 |
UniquePtr<JsonDocument> json(new JsonDocument()); |
38 |
|
315 |
bool retval = json->Parse(text); |
39 |
✓✓ |
315 |
if (!retval) return NULL; |
40 |
|
282 |
return json.Release(); |
41 |
|
|
} |
42 |
|
|
|
43 |
|
14 |
string JsonDocument::EscapeString(const string &input) { |
44 |
|
14 |
string escaped; |
45 |
|
14 |
escaped.reserve(input.length()); |
46 |
|
|
|
47 |
✓✓ |
116 |
for (unsigned i = 0, s = input.length(); i < s; ++i) { |
48 |
✗✓ |
102 |
if (input[i] == '\\') { |
49 |
|
|
escaped.push_back('\\'); |
50 |
|
|
escaped.push_back('\\'); |
51 |
✓✓ |
102 |
} else if (input[i] == '"') { |
52 |
|
2 |
escaped.push_back('\\'); |
53 |
|
2 |
escaped.push_back('"'); |
54 |
|
|
} else { |
55 |
|
100 |
escaped.push_back(input[i]); |
56 |
|
|
} |
57 |
|
|
} |
58 |
|
14 |
return escaped; |
59 |
|
|
} |
60 |
|
|
|
61 |
|
315 |
JsonDocument::JsonDocument() |
62 |
|
315 |
: allocator_(kDefaultBlockSize), root_(NULL), raw_text_(NULL) {} |
63 |
|
|
|
64 |
|
315 |
JsonDocument::~JsonDocument() { |
65 |
✓✗ |
315 |
if (raw_text_) free(raw_text_); |
66 |
|
315 |
} |
67 |
|
|
|
68 |
|
|
/** |
69 |
|
|
* Parses a JSON string in text. |
70 |
|
|
* |
71 |
|
|
* @return true if parsing was successful |
72 |
|
|
*/ |
73 |
|
315 |
bool JsonDocument::Parse(const string &text) { |
74 |
✗✓ |
315 |
assert(root_ == NULL); |
75 |
|
|
|
76 |
|
|
// The used JSON library 'vjson' is a destructive parser and therefore |
77 |
|
|
// alters the content of the provided buffer. The buffer must persist as |
78 |
|
|
// name and string values from JSON nodes just point into it. |
79 |
|
315 |
raw_text_ = strdup(text.c_str()); |
80 |
|
|
|
81 |
|
315 |
char *error_pos = 0; |
82 |
|
315 |
char *error_desc = 0; |
83 |
|
315 |
int error_line = 0; |
84 |
|
|
JSON *root = |
85 |
|
315 |
json_parse(raw_text_, &error_pos, &error_desc, &error_line, &allocator_); |
86 |
|
|
|
87 |
|
|
// check if the json string was parsed successfully |
88 |
✓✓ |
315 |
if (!root) { |
89 |
|
|
LogCvmfs(kLogUtility, kLogDebug, |
90 |
|
|
"Failed to parse json string. Error at line %d: %s (%s)", |
91 |
|
33 |
error_line, error_desc, error_pos); |
92 |
|
33 |
return false; |
93 |
|
|
} |
94 |
|
|
|
95 |
|
282 |
root_ = root; |
96 |
|
282 |
return true; |
97 |
|
|
} |
98 |
|
|
|
99 |
|
3 |
string JsonDocument::PrintArray(JSON *first_child, PrintOptions print_options) { |
100 |
|
3 |
string result = "["; |
101 |
✗✓ |
3 |
if (print_options.with_whitespace) { |
102 |
|
|
result += "\n"; |
103 |
|
|
print_options.num_indent += 2; |
104 |
|
|
} |
105 |
|
3 |
JSON *value = first_child; |
106 |
✓✓ |
3 |
if (value != NULL) { |
107 |
|
2 |
result += PrintValue(value, print_options); |
108 |
|
2 |
value = value->next_sibling; |
109 |
|
|
} |
110 |
✓✓ |
16 |
while (value != NULL) { |
111 |
✗✓ |
10 |
result += print_options.with_whitespace ? ",\n" : ","; |
112 |
|
10 |
result += PrintValue(value, print_options); |
113 |
|
10 |
value = value->next_sibling; |
114 |
|
|
} |
115 |
✗✓ |
3 |
if (print_options.with_whitespace) { |
116 |
|
|
result += "\n"; |
117 |
|
|
for (unsigned i = 2; i < print_options.num_indent; ++i) |
118 |
|
|
result.push_back(' '); |
119 |
|
|
} |
120 |
|
3 |
return result + "]"; |
121 |
|
|
} |
122 |
|
|
|
123 |
|
|
/** |
124 |
|
|
* JSON string in a canonical format: |
125 |
|
|
* - No whitespaces |
126 |
|
|
* - Variable names and strings in quotes |
127 |
|
|
* |
128 |
|
|
* Can be used as a canonical representation to sign or encrypt a JSON text. |
129 |
|
|
*/ |
130 |
|
3 |
string JsonDocument::PrintCanonical() { |
131 |
✗✓ |
3 |
if (!root_) return ""; |
132 |
|
3 |
PrintOptions print_options; |
133 |
|
3 |
return PrintObject(root_->first_child, print_options); |
134 |
|
|
} |
135 |
|
|
|
136 |
|
7 |
string JsonDocument::PrintObject(JSON *first_child, |
137 |
|
|
PrintOptions print_options) { |
138 |
|
7 |
string result = "{"; |
139 |
✗✓ |
7 |
if (print_options.with_whitespace) { |
140 |
|
|
result += "\n"; |
141 |
|
|
print_options.num_indent += 2; |
142 |
|
|
} |
143 |
|
7 |
JSON *value = first_child; |
144 |
✓✓ |
7 |
if (value != NULL) { |
145 |
|
4 |
result += PrintValue(value, print_options); |
146 |
|
4 |
value = value->next_sibling; |
147 |
|
|
} |
148 |
✓✓ |
23 |
while (value != NULL) { |
149 |
✗✓ |
9 |
result += print_options.with_whitespace ? ",\n" : ","; |
150 |
|
9 |
result += PrintValue(value, print_options); |
151 |
|
9 |
value = value->next_sibling; |
152 |
|
|
} |
153 |
✗✓ |
7 |
if (print_options.with_whitespace) { |
154 |
|
|
result += "\n"; |
155 |
|
|
for (unsigned i = 2; i < print_options.num_indent; ++i) |
156 |
|
|
result.push_back(' '); |
157 |
|
|
} |
158 |
|
7 |
return result + "}"; |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
/** |
162 |
|
|
* JSON string for humans. |
163 |
|
|
*/ |
164 |
|
|
string JsonDocument::PrintPretty() { |
165 |
|
|
if (!root_) return ""; |
166 |
|
|
PrintOptions print_options; |
167 |
|
|
print_options.with_whitespace = true; |
168 |
|
|
return PrintObject(root_->first_child, print_options); |
169 |
|
|
} |
170 |
|
|
|
171 |
|
25 |
std::string JsonDocument::PrintValue(JSON *value, PrintOptions print_options) { |
172 |
✗✓ |
25 |
assert(value); |
173 |
|
|
|
174 |
|
25 |
string result; |
175 |
✗✓ |
25 |
for (unsigned i = 0; i < print_options.num_indent; ++i) result.push_back(' '); |
176 |
✓✓ |
25 |
if (value->name) { |
177 |
|
11 |
result += "\"" + EscapeString(value->name) + "\":"; |
178 |
✗✓ |
11 |
if (print_options.with_whitespace) result += " "; |
179 |
|
|
} |
180 |
✓✓✓✓ ✓✓✓✗
|
25 |
switch (value->type) { |
181 |
|
|
case JSON_NULL: |
182 |
|
2 |
result += "null"; |
183 |
|
2 |
break; |
184 |
|
|
case JSON_OBJECT: |
185 |
|
4 |
result += PrintObject(value->first_child, print_options); |
186 |
|
4 |
break; |
187 |
|
|
case JSON_ARRAY: |
188 |
|
3 |
result += PrintArray(value->first_child, print_options); |
189 |
|
3 |
break; |
190 |
|
|
case JSON_STRING: |
191 |
|
3 |
result += "\"" + EscapeString(value->string_value) + "\""; |
192 |
|
3 |
break; |
193 |
|
|
case JSON_INT: |
194 |
|
8 |
result += StringifyInt(value->int_value); |
195 |
|
8 |
break; |
196 |
|
|
case JSON_FLOAT: |
197 |
|
2 |
result += StringifyDouble(value->float_value); |
198 |
|
2 |
break; |
199 |
|
|
case JSON_BOOL: |
200 |
✓✓ |
3 |
result += value->int_value ? "true" : "false"; |
201 |
|
3 |
break; |
202 |
|
|
default: |
203 |
|
|
abort(); |
204 |
|
|
} |
205 |
|
25 |
return result; |
206 |
|
|
} |
207 |
|
|
|
208 |
|
1007 |
JSON *JsonDocument::SearchInObject(const JSON *json_object, const string &name, |
209 |
|
|
const json_type type) { |
210 |
✓✓✓✓
|
1007 |
if (!json_object || (json_object->type != JSON_OBJECT)) return NULL; |
211 |
|
|
|
212 |
|
1005 |
JSON *walker = json_object->first_child; |
213 |
✓✓ |
3223 |
while (walker != NULL) { |
214 |
✓✗✗✓
|
2037 |
if (string(walker->name) == name) { |
215 |
✓✓ |
824 |
return (walker->type == type) ? walker : NULL; |
216 |
|
|
} |
217 |
|
1213 |
walker = walker->next_sibling; |
218 |
|
|
} |
219 |
|
181 |
return NULL; |
220 |
|
|
} |