Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
#include "Json.h"
2
#include <cstring>
3
#include <stdlib.h>
4
 
5
using namespace CJSON;
6
 
7
Json::Json() : root(nullptr) {}
8
Json::~Json()
9
{
10
    if (root)
11
        cJSON_Delete(root);
12
}
13
 
14
bool Json::parse(const String &text)
15
{
16
    if (root)
17
        cJSON_Delete(root);
18
    root = cJSON_Parse(text.c_str());
19
    return root != nullptr;
20
}
21
 
22
static void jsonEscapeString(const char* in, String& out) {
23
    if (!in) { out += ""; return; }
24
    out.reserve(out.length() + strlen(in) + 4);
25
    for (const char* p = in; *p; ++p) {
26
        char c = *p;
27
        switch (c) {
28
            case '"': out += "\\\""; break;
29
            case '\\': out += "\\\\"; break;
30
            case '\b': out += "\\b"; break;
31
            case '\f': out += "\\f"; break;
32
            case '\n': out += "\\n"; break;
33
            case '\r': out += "\\r"; break;
34
            case '\t': out += "\\t"; break;
35
            default:
36
                if ((unsigned char)c < 0x20) {
37
                    // Control chars -> skip or encode minimally
38
                    // Minimal approach: skip
39
                } else {
40
                    out += c;
41
                }
42
        }
43
    }
44
}
45
 
46
static void serializeNode(const cJSON* item, String& out, bool pretty, int indent);
47
 
48
static void addIndent(String& out, int indent) {
49
    for (int i = 0; i < indent; i++) out += "  ";
50
}
51
 
52
static void serializeArray(const cJSON* array, String& out, bool pretty, int indent) {
53
    out.reserve(out.length() + 64);
54
    out += '[';
55
    const cJSON* child = array->child;
56
    bool first = true;
57
    if (pretty && child) out += '\n';
58
 
59
    while (child) {
60
        if (!first) {
61
            out += ',';
62
            if (pretty) out += '\n';
63
        }
64
        first = false;
65
        if (pretty) addIndent(out, indent + 1);
66
        serializeNode(child, out, pretty, indent + 1);
67
        child = child->next;
68
    }
69
    if (pretty && array->child) {
70
        out += '\n';
71
        addIndent(out, indent);
72
    }
73
    out += ']';
74
}
75
 
76
static void serializeObject(const cJSON* obj, String& out, bool pretty, int indent) {
77
    out.reserve(out.length() + 64);
78
    out += '{';
79
    const cJSON* child = obj->child;
80
    bool first = true;
81
    if (pretty && child) out += '\n';
82
 
83
    while (child) {
84
        if (!first) {
85
            out += ',';
86
            if (pretty) out += '\n';
87
        }
88
        first = false;
89
        if (pretty) addIndent(out, indent + 1);
90
        out += '"';
91
        jsonEscapeString(child->string, out);
92
        out += '"';
93
        out += ':';
94
        if (pretty) out += ' ';
95
        serializeNode(child, out, pretty, indent + 1);
96
        child = child->next;
97
    }
98
    if (pretty && obj->child) {
99
        out += '\n';
100
        addIndent(out, indent);
101
    }
102
    out += '}';
103
}
104
 
105
static void serializeNumber(const cJSON* item, String& out) {
106
    // Prefer integer when representable
107
    double d = item->valuedouble;
108
    // Use valueint if it matches
109
    if ((double)item->valueint == d) {
110
        out += String(item->valueint);
111
        return;
112
    }
113
    // Fallback: limited precision to reduce code size
114
    // Using String(double, digits) avoids heavy printf linkage
115
    out += String(d, 6);
116
}
117
 
118
static void serializeNode(const cJSON* item, String& out, bool pretty, int indent) {
119
    if (!item) { out += "null"; return; }
120
    switch (item->type & 0xFF) {
121
        case cJSON_False: out += "false"; break;
122
        case cJSON_True: out += "true"; break;
123
        case cJSON_NULL: out += "null"; break;
124
        case cJSON_Number: serializeNumber(item, out); break;
125
        case cJSON_String:
126
            out += '"';
127
            jsonEscapeString(item->valuestring, out);
128
            out += '"';
129
            break;
130
        case cJSON_Array: serializeArray(item, out, pretty, indent); break;
131
        case cJSON_Object: serializeObject(item, out, pretty, indent); break;
132
        default: out += "null"; break;
133
    }
134
}
135
 
136
String Json::serialize(bool pretty) const
137
{
138
    if (!root)
139
        return String();
140
    String s;
141
    s.reserve(256);
142
    serializeNode(root, s, pretty, 0);
143
    return s;
144
}
145
 
146
// --------- Construction helpers for nested structures ---------
147
bool Json::createObject()
148
{
149
    if (root) cJSON_Delete(root);
150
    root = cJSON_CreateObject();
151
    return root != nullptr;
152
}
153
 
154
bool Json::createArray()
155
{
156
    if (root) cJSON_Delete(root);
157
    root = cJSON_CreateArray();
158
    return root != nullptr;
159
}
160
 
161
bool Json::add(const Json& child)
162
{
163
    if (!root || !cJSON_IsArray(root)) return false;
164
    // Deep copy child root into this array
165
    cJSON* copy = nullptr;
166
    if (child.root) {
167
        copy = cJSON_Duplicate(child.root, /*recurse*/1);
168
    } else {
169
        copy = cJSON_CreateNull();
170
    }
171
    if (!copy) return false;
172
    cJSON_AddItemToArray(root, copy);
173
    return true;
174
}
175
 
176
bool Json::set(const String& key, const Json& child)
177
{
178
    if (!root || !cJSON_IsObject(root)) return false;
179
    cJSON_DeleteItemFromObjectCaseSensitive(root, key.c_str());
180
    cJSON* copy = nullptr;
181
    if (child.root) {
182
        copy = cJSON_Duplicate(child.root, /*recurse*/1);
183
    } else {
184
        copy = cJSON_CreateNull();
185
    }
186
    if (!copy) return false;
187
    cJSON_AddItemToObject(root, key.c_str(), copy);
188
    return true;
189
}
190
 
191
bool Json::hasObject(const String &key) const
192
{
193
    if (!root)
194
        return false;
195
    cJSON *obj = cJSON_GetObjectItemCaseSensitive(root, key.c_str());
196
    return obj && cJSON_IsObject(obj);
197
}
198
 
199
void Json::ensureObject(const String &key)
200
{
201
    if (!root)
202
        root = cJSON_CreateObject();
203
    cJSON *obj = cJSON_GetObjectItemCaseSensitive(root, key.c_str());
204
    if (!(obj && cJSON_IsObject(obj)))
205
    {
206
        cJSON *n = cJSON_CreateObject();
207
        cJSON_AddItemToObject(root, key.c_str(), n);
208
    }
209
}
210
 
211
// --------- Top-level helpers ---------
212
 
213
bool Json::hasKey(const String &key) const
214
{
215
    if (!root) return false;
216
    cJSON *item = cJSON_GetObjectItemCaseSensitive(root, key.c_str());
217
    return item != nullptr;
218
}
219
 
220
 
221
bool Json::hasKey(const String &objName, const String &key) const
222
{
223
    if (!root)
224
        return false;
225
    cJSON *scope = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
226
    if (!scope)
227
        return false;
228
    cJSON *item = cJSON_GetObjectItemCaseSensitive(scope, key.c_str());
229
    return item != nullptr;
230
}
231
 
232
bool Json::setString(const String &key, const String &value)
233
{
234
    if (!root) root = cJSON_CreateObject();
235
    cJSON_DeleteItemFromObjectCaseSensitive(root, key.c_str());
236
    cJSON_AddItemToObject(root, key.c_str(), cJSON_CreateString(value.c_str()));
237
    return true;
238
}
239
 
240
bool Json::setNumber(const String &key, double value)
241
{
242
    if (!root) root = cJSON_CreateObject();
243
    cJSON_DeleteItemFromObjectCaseSensitive(root, key.c_str());
244
    cJSON_AddItemToObject(root, key.c_str(), cJSON_CreateNumber(value));
245
    return true;
246
}
247
 
248
bool Json::setBool(const String &key, bool value)
249
{
250
    if (!root) root = cJSON_CreateObject();
251
    cJSON_DeleteItemFromObjectCaseSensitive(root, key.c_str());
252
    cJSON_AddItemToObject(root, key.c_str(), cJSON_CreateBool(value));
253
    return true;
254
}
255
 
256
bool Json::setArray(const String &key, const std::vector<String> &values)
257
{
258
    if (!root) root = cJSON_CreateObject();
259
    cJSON *arr = cJSON_CreateArray();
260
    for (auto &v : values) cJSON_AddItemToArray(arr, cJSON_CreateString(v.c_str()));
261
    cJSON_DeleteItemFromObjectCaseSensitive(root, key.c_str());
262
    cJSON_AddItemToObject(root, key.c_str(), arr);
263
    return true;
264
}
265
 
266
 
267
bool Json::setString(const String &objName, const String &key, const String &value)
268
{
269
    ensureObject(objName);
270
    cJSON *target = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
271
    if (!target)
272
        return false;
273
    cJSON_DeleteItemFromObjectCaseSensitive(target, key.c_str());
274
    cJSON_AddItemToObject(target, key.c_str(), cJSON_CreateString(value.c_str()));
275
    return true;
276
}
277
 
278
bool Json::setNumber(const String &objName, const String &key, double value)
279
{
280
    ensureObject(objName);
281
    cJSON *target = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
282
    if (!target)
283
        return false;
284
    cJSON_DeleteItemFromObjectCaseSensitive(target, key.c_str());
285
    cJSON_AddItemToObject(target, key.c_str(), cJSON_CreateNumber(value));
286
    return true;
287
}
288
 
289
bool Json::setBool(const String &objName, const String &key, bool value)
290
{
291
    ensureObject(objName);
292
    cJSON *target = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
293
    if (!target)
294
        return false;
295
    cJSON_DeleteItemFromObjectCaseSensitive(target, key.c_str());
296
    cJSON_AddItemToObject(target, key.c_str(), cJSON_CreateBool(value));
297
    return true;
298
}
299
 
300
bool Json::setArray(const String &objName, const String &key, const std::vector<String> &values)
301
{
302
    ensureObject(objName);
303
    cJSON *target = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
304
    if (!target)
305
        return false;
306
    cJSON *arr = cJSON_CreateArray();
307
    for (auto &v : values)
308
        cJSON_AddItemToArray(arr, cJSON_CreateString(v.c_str()));
309
    cJSON_DeleteItemFromObjectCaseSensitive(target, key.c_str());
310
    cJSON_AddItemToObject(target, key.c_str(), arr);
311
    return true;
312
}
313
 
314
bool Json::getString(const String &key, String &out) const
315
{
316
    if (!root) return false;
317
    cJSON *item = cJSON_GetObjectItemCaseSensitive(root, key.c_str());
318
    if (!item || !cJSON_IsString(item)) return false;
319
    out = item->valuestring ? String(item->valuestring) : String();
320
    return true;
321
}
322
 
323
bool Json::getBool(const String& key, bool& out) const {
324
    if (!root) return false;
325
    cJSON *item = cJSON_GetObjectItemCaseSensitive(root, key.c_str());
326
    if (!item) return false;
327
    if (cJSON_IsBool(item)) {
328
        out = cJSON_IsTrue(item);
329
        return true;
330
    }
331
    if (cJSON_IsNumber(item)) {
332
        out = (item->valuedouble != 0.0);
333
        return true;
334
    }
335
    return false;
336
}
337
 
338
bool Json::getNumber(const String &key, double &out) const
339
{
340
    if (!root) return false;
341
    cJSON *item = cJSON_GetObjectItemCaseSensitive(root, key.c_str());
342
    if (!item) return false;
343
    if (cJSON_IsNumber(item)) {
344
        out = item->valuedouble;
345
        return true;
346
    }
347
    if (cJSON_IsString(item) && item->valuestring) {
348
        out = atof(item->valuestring);
349
        return true;
350
    }
351
    return false;
352
}
353
 
354
 
355
// Object-scoped key helpers
356
bool Json::getString(const String &objName, const String &key, String &out) const
357
{
358
    if (!root)
359
        return false;
360
    cJSON *scope = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
361
    if (!scope)
362
        return false;
363
    cJSON *item = cJSON_GetObjectItemCaseSensitive(scope, key.c_str());
364
    if (!item || !cJSON_IsString(item))
365
        return false;
366
    out = item->valuestring ? String(item->valuestring) : String();
367
    return true;
368
}
369
 
370
bool Json::getNumber(const String &objName, const String &key, double &out) const
371
{
372
    if (!root)
373
        return false;
374
    cJSON *scope = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
375
    if (!scope)
376
        return false;
377
    cJSON *item = cJSON_GetObjectItemCaseSensitive(scope, key.c_str());
378
    if (!item)
379
        return false;
380
    out = item->valuedouble;
381
    return true;
382
}
383
 
384
bool Json::getBool(const String &objName, const String &key, bool &out) const
385
{
386
    if (!root)
387
        return false;
388
    cJSON *scope = cJSON_GetObjectItemCaseSensitive(root, objName.c_str());
389
    if (!scope)
390
        return false;
391
    cJSON *item = cJSON_GetObjectItemCaseSensitive(scope, key.c_str());
392
    if (!item)
393
        return false;
394
    if (cJSON_IsBool(item)) {
395
        out = cJSON_IsTrue(item);
396
        return true;
397
    }
398
    if (cJSON_IsNumber(item)) {
399
        out = (item->valuedouble != 0.0);
400
        return true;
401
    }
402
    return false;
403
}
404
 
405