aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/jsonutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dutil/jsonutil.cpp')
-rw-r--r--src/dutil/jsonutil.cpp672
1 files changed, 672 insertions, 0 deletions
diff --git a/src/dutil/jsonutil.cpp b/src/dutil/jsonutil.cpp
new file mode 100644
index 00000000..ba088705
--- /dev/null
+++ b/src/dutil/jsonutil.cpp
@@ -0,0 +1,672 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5const DWORD JSON_STACK_INCREMENT = 5;
6
7// Prototypes
8static HRESULT DoStart(
9 __in JSON_WRITER* pWriter,
10 __in JSON_TOKEN tokenStart,
11 __in_z LPCWSTR wzStartString
12 );
13static HRESULT DoEnd(
14 __in JSON_WRITER* pWriter,
15 __in JSON_TOKEN tokenEnd,
16 __in_z LPCWSTR wzEndString
17 );
18static HRESULT DoKey(
19 __in JSON_WRITER* pWriter,
20 __in_z LPCWSTR wzKey
21 );
22static HRESULT DoValue(
23 __in JSON_WRITER* pWriter,
24 __in_z_opt LPCWSTR wzValue
25 );
26static HRESULT EnsureTokenStack(
27 __in JSON_WRITER* pWriter
28 );
29static HRESULT SerializeJsonString(
30 __out_z LPWSTR* psczJsonString,
31 __in_z LPCWSTR wzString
32 );
33
34
35
36DAPI_(HRESULT) JsonInitializeReader(
37 __in_z LPCWSTR wzJson,
38 __in JSON_READER* pReader
39 )
40{
41 HRESULT hr = S_OK;
42
43 memset(pReader, 0, sizeof(JSON_READER));
44 ::InitializeCriticalSection(&pReader->cs);
45
46 hr = StrAllocString(&pReader->sczJson, wzJson, 0);
47 ExitOnFailure(hr, "Failed to allocate json string.");
48
49 pReader->pwz = pReader->sczJson;
50
51LExit:
52 return hr;
53}
54
55
56DAPI_(void) JsonUninitializeReader(
57 __in JSON_READER* pReader
58 )
59{
60 ReleaseStr(pReader->sczJson);
61
62 ::DeleteCriticalSection(&pReader->cs);
63 memset(pReader, 0, sizeof(JSON_READER));
64}
65
66
67static HRESULT NextToken(
68 __in JSON_READER* pReader,
69 __out JSON_TOKEN* pToken
70 )
71{
72 HRESULT hr = S_OK;
73
74 // Skip whitespace.
75 while (L' ' == *pReader->pwz ||
76 L'\t' == *pReader->pwz ||
77 L'\r' == *pReader->pwz ||
78 L'\n' == *pReader->pwz ||
79 L',' == *pReader->pwz)
80 {
81 ++pReader->pwz;
82 }
83
84 switch (*pReader->pwz)
85 {
86 case L'{':
87 *pToken = JSON_TOKEN_OBJECT_START;
88 break;
89
90 case L':': // begin object value
91 *pToken = JSON_TOKEN_OBJECT_VALUE;
92 break;
93
94 case L'}': // end object
95 *pToken = JSON_TOKEN_OBJECT_END;
96 break;
97
98 case L'[': // begin array
99 *pToken = JSON_TOKEN_ARRAY_START;
100 break;
101
102 case L']': // end array
103 *pToken = JSON_TOKEN_ARRAY_END;
104 break;
105
106 case L'"': // string
107 *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY;
108 break;
109
110 case L't': // true
111 case L'f': // false
112 case L'n': // null
113 case L'-': // number
114 case L'0':
115 case L'1':
116 case L'2':
117 case L'3':
118 case L'4':
119 case L'5':
120 case L'6':
121 case L'7':
122 case L'8':
123 case L'9':
124 *pToken = JSON_TOKEN_VALUE;
125 break;
126
127 case L'\0':
128 hr = E_NOMOREITEMS;
129 break;
130
131 default:
132 hr = E_INVALIDSTATE;
133 }
134
135 return hr;
136}
137
138
139DAPI_(HRESULT) JsonReadNext(
140 __in JSON_READER* pReader,
141 __out JSON_TOKEN* pToken,
142 __out JSON_VALUE* pValue
143 )
144{
145 HRESULT hr = S_OK;
146 //WCHAR wz;
147 //JSON_TOKEN token = JSON_TOKEN_NONE;
148
149 ::EnterCriticalSection(&pReader->cs);
150
151 hr = NextToken(pReader, pToken);
152 if (E_NOMOREITEMS == hr)
153 {
154 ExitFunction();
155 }
156 ExitOnFailure(hr, "Failed to get next token.");
157
158 if (JSON_TOKEN_VALUE == *pToken)
159 {
160 hr = JsonReadValue(pReader, pValue);
161 }
162 else
163 {
164 ++pReader->pwz;
165 }
166
167LExit:
168 ::LeaveCriticalSection(&pReader->cs);
169 return hr;
170}
171
172
173DAPI_(HRESULT) JsonReadValue(
174 __in JSON_READER* /*pReader*/,
175 __in JSON_VALUE* /*pValue*/
176 )
177{
178 HRESULT hr = S_OK;
179
180//LExit:
181 return hr;
182}
183
184
185DAPI_(HRESULT) JsonInitializeWriter(
186 __in JSON_WRITER* pWriter
187 )
188{
189 memset(pWriter, 0, sizeof(JSON_WRITER));
190 ::InitializeCriticalSection(&pWriter->cs);
191
192 return S_OK;
193}
194
195
196DAPI_(void) JsonUninitializeWriter(
197 __in JSON_WRITER* pWriter
198 )
199{
200 ReleaseMem(pWriter->rgTokenStack);
201 ReleaseStr(pWriter->sczJson);
202
203 ::DeleteCriticalSection(&pWriter->cs);
204 memset(pWriter, 0, sizeof(JSON_WRITER));
205}
206
207
208DAPI_(HRESULT) JsonWriteBool(
209 __in JSON_WRITER* pWriter,
210 __in BOOL fValue
211 )
212{
213 HRESULT hr = S_OK;
214 LPWSTR sczValue = NULL;
215
216 hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0);
217 ExitOnFailure(hr, "Failed to convert boolean to string.");
218
219 hr = DoValue(pWriter, sczValue);
220 ExitOnFailure(hr, "Failed to add boolean to JSON.");
221
222LExit:
223 ReleaseStr(sczValue);
224 return hr;
225}
226
227
228DAPI_(HRESULT) JsonWriteNumber(
229 __in JSON_WRITER* pWriter,
230 __in DWORD dwValue
231 )
232{
233 HRESULT hr = S_OK;
234 LPWSTR sczValue = NULL;
235
236 hr = StrAllocFormatted(&sczValue, L"%u", dwValue);
237 ExitOnFailure(hr, "Failed to convert number to string.");
238
239 hr = DoValue(pWriter, sczValue);
240 ExitOnFailure(hr, "Failed to add number to JSON.");
241
242LExit:
243 ReleaseStr(sczValue);
244 return hr;
245}
246
247
248DAPI_(HRESULT) JsonWriteString(
249 __in JSON_WRITER* pWriter,
250 __in_z LPCWSTR wzValue
251 )
252{
253 HRESULT hr = S_OK;
254 LPWSTR sczJsonString = NULL;
255
256 hr = SerializeJsonString(&sczJsonString, wzValue);
257 ExitOnFailure(hr, "Failed to allocate string JSON.");
258
259 hr = DoValue(pWriter, sczJsonString);
260 ExitOnFailure(hr, "Failed to add string to JSON.");
261
262LExit:
263 ReleaseStr(sczJsonString);
264 return hr;
265}
266
267
268DAPI_(HRESULT) JsonWriteArrayStart(
269 __in JSON_WRITER* pWriter
270 )
271{
272 HRESULT hr = S_OK;
273
274 hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"[");
275 ExitOnFailure(hr, "Failed to start JSON array.");
276
277LExit:
278 return hr;
279}
280
281
282DAPI_(HRESULT) JsonWriteArrayEnd(
283 __in JSON_WRITER* pWriter
284 )
285{
286 HRESULT hr = S_OK;
287
288 hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]");
289 ExitOnFailure(hr, "Failed to end JSON array.");
290
291LExit:
292 return hr;
293}
294
295
296DAPI_(HRESULT) JsonWriteObjectStart(
297 __in JSON_WRITER* pWriter
298 )
299{
300 HRESULT hr = S_OK;
301
302 hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{");
303 ExitOnFailure(hr, "Failed to start JSON object.");
304
305LExit:
306 return hr;
307}
308
309
310DAPI_(HRESULT) JsonWriteObjectKey(
311 __in JSON_WRITER* pWriter,
312 __in_z LPCWSTR wzKey
313 )
314{
315 HRESULT hr = S_OK;
316 LPWSTR sczObjectKey = NULL;
317
318 hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey);
319 ExitOnFailure(hr, "Failed to allocate JSON object key.");
320
321 hr = DoKey(pWriter, sczObjectKey);
322 ExitOnFailure(hr, "Failed to add object key to JSON.");
323
324LExit:
325 ReleaseStr(sczObjectKey);
326 return hr;
327}
328
329
330DAPI_(HRESULT) JsonWriteObjectEnd(
331 __in JSON_WRITER* pWriter
332 )
333{
334 HRESULT hr = S_OK;
335
336 hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}");
337 ExitOnFailure(hr, "Failed to end JSON object.");
338
339LExit:
340 return hr;
341}
342
343
344static HRESULT DoStart(
345 __in JSON_WRITER* pWriter,
346 __in JSON_TOKEN tokenStart,
347 __in_z LPCWSTR wzStartString
348 )
349{
350 Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart);
351
352 HRESULT hr = S_OK;
353 JSON_TOKEN token = JSON_TOKEN_NONE;
354 BOOL fNeedComma = FALSE;
355 BOOL fPushToken = TRUE;
356
357 ::EnterCriticalSection(&pWriter->cs);
358
359 hr = EnsureTokenStack(pWriter);
360 ExitOnFailure(hr, "Failed to ensure token stack for start.");
361
362 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
363 switch (token)
364 {
365 case JSON_TOKEN_NONE:
366 token = tokenStart;
367 fPushToken = FALSE;
368 break;
369
370 case JSON_TOKEN_ARRAY_START: // array start changes to array value.
371 token = JSON_TOKEN_ARRAY_VALUE;
372 break;
373
374 case JSON_TOKEN_ARRAY_VALUE:
375 case JSON_TOKEN_ARRAY_END:
376 case JSON_TOKEN_OBJECT_END:
377 fNeedComma = TRUE;
378 break;
379
380 default: // everything else is not allowed.
381 hr = E_UNEXPECTED;
382 break;
383 }
384 ExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now.");
385
386 if (fNeedComma)
387 {
388 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
389 ExitOnFailure(hr, "Failed to add comma for start array or object to JSON.");
390 }
391
392 hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0);
393 ExitOnFailure(hr, "Failed to start JSON array or object.");
394
395 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
396 if (fPushToken)
397 {
398 pWriter->rgTokenStack[pWriter->cTokens] = tokenStart;
399 ++pWriter->cTokens;
400 }
401
402LExit:
403 ::LeaveCriticalSection(&pWriter->cs);
404 return hr;
405}
406
407
408static HRESULT DoEnd(
409 __in JSON_WRITER* pWriter,
410 __in JSON_TOKEN tokenEnd,
411 __in_z LPCWSTR wzEndString
412 )
413{
414 HRESULT hr = S_OK;
415
416 ::EnterCriticalSection(&pWriter->cs);
417
418 if (!pWriter->rgTokenStack || 0 == pWriter->cTokens)
419 {
420 hr = E_UNEXPECTED;
421 ExitOnRootFailure(hr, "Failure to pop token because the stack is empty.");
422 }
423 else
424 {
425 JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1];
426 if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) ||
427 (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token))
428 {
429 hr = E_UNEXPECTED;
430 ExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd);
431 }
432 }
433
434 hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0);
435 ExitOnFailure(hr, "Failed to end JSON array or object.");
436
437 --pWriter->cTokens;
438
439LExit:
440 ::LeaveCriticalSection(&pWriter->cs);
441 return hr;
442}
443
444
445static HRESULT DoKey(
446 __in JSON_WRITER* pWriter,
447 __in_z LPCWSTR wzKey
448 )
449{
450 HRESULT hr = S_OK;
451 JSON_TOKEN token = JSON_TOKEN_NONE;
452 BOOL fNeedComma = FALSE;
453
454 ::EnterCriticalSection(&pWriter->cs);
455
456 hr = EnsureTokenStack(pWriter);
457 ExitOnFailure(hr, "Failed to ensure token stack for key.");
458
459 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
460 switch (token)
461 {
462 case JSON_TOKEN_OBJECT_START:
463 token = JSON_TOKEN_OBJECT_KEY;
464 break;
465
466 case JSON_TOKEN_OBJECT_VALUE:
467 token = JSON_TOKEN_OBJECT_KEY;
468 fNeedComma = TRUE;
469 break;
470
471 default: // everything else is not allowed.
472 hr = E_UNEXPECTED;
473 break;
474 }
475 ExitOnRootFailure(hr, "Cannot add key to JSON serializer now.");
476
477 if (fNeedComma)
478 {
479 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
480 ExitOnFailure(hr, "Failed to add comma for key to JSON.");
481 }
482
483 hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0);
484 ExitOnFailure(hr, "Failed to add key to JSON.");
485
486 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
487
488LExit:
489 ::LeaveCriticalSection(&pWriter->cs);
490 return hr;
491}
492
493
494static HRESULT DoValue(
495 __in JSON_WRITER* pWriter,
496 __in_z_opt LPCWSTR wzValue
497 )
498{
499 HRESULT hr = S_OK;
500 JSON_TOKEN token = JSON_TOKEN_NONE;
501 BOOL fNeedComma = FALSE;
502
503 ::EnterCriticalSection(&pWriter->cs);
504
505 hr = EnsureTokenStack(pWriter);
506 ExitOnFailure(hr, "Failed to ensure token stack for value.");
507
508 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
509 switch (token)
510 {
511 case JSON_TOKEN_ARRAY_START:
512 token = JSON_TOKEN_ARRAY_VALUE;
513 break;
514
515 case JSON_TOKEN_ARRAY_VALUE:
516 fNeedComma = TRUE;
517 break;
518
519 case JSON_TOKEN_OBJECT_KEY:
520 token = JSON_TOKEN_OBJECT_VALUE;
521 break;
522
523 case JSON_TOKEN_NONE:
524 token = JSON_TOKEN_VALUE;
525 break;
526
527 default: // everything else is not allowed.
528 hr = E_UNEXPECTED;
529 break;
530 }
531 ExitOnRootFailure(hr, "Cannot add value to JSON serializer now.");
532
533 if (fNeedComma)
534 {
535 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
536 ExitOnFailure(hr, "Failed to add comma for value to JSON.");
537 }
538
539 if (wzValue)
540 {
541 hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0);
542 ExitOnFailure(hr, "Failed to add value to JSON.");
543 }
544 else
545 {
546 hr = StrAllocConcat(&pWriter->sczJson, L"null", 0);
547 ExitOnFailure(hr, "Failed to add null value to JSON.");
548 }
549
550 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
551
552LExit:
553 ::LeaveCriticalSection(&pWriter->cs);
554 return hr;
555}
556
557
558static HRESULT EnsureTokenStack(
559 __in JSON_WRITER* pWriter
560 )
561{
562 HRESULT hr = S_OK;
563 DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0;
564
565 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT);
566 ExitOnFailure(hr, "Failed to allocate JSON token stack.");
567
568 if (0 == pWriter->cTokens)
569 {
570 pWriter->rgTokenStack[0] = JSON_TOKEN_NONE;
571 ++pWriter->cTokens;
572 }
573
574LExit:
575 return hr;
576}
577
578
579static HRESULT SerializeJsonString(
580 __out_z LPWSTR* psczJsonString,
581 __in_z LPCWSTR wzString
582 )
583{
584 HRESULT hr = S_OK;
585 DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0)
586
587 for (LPCWSTR pch = wzString; *pch; ++pch)
588 {
589 // If it is a special JSON character, add space for the escape backslash.
590 if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch)
591 {
592 ++cchRequired;
593 }
594
595 ++cchRequired;
596 }
597
598 hr = StrAlloc(psczJsonString, cchRequired);
599 ExitOnFailure(hr, "Failed to allocate space for JSON string.");
600
601 LPWSTR pchTarget = *psczJsonString;
602
603 *pchTarget = L'\"';
604 ++pchTarget;
605
606 for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget)
607 {
608 // If it is a special JSON character, handle it or just add the character as is.
609 switch (*pch)
610 {
611 case L'"':
612 *pchTarget = L'\\';
613 ++pchTarget;
614 *pchTarget = L'"';
615 break;
616
617 case L'\\':
618 *pchTarget = L'\\';
619 ++pchTarget;
620 *pchTarget = L'\\';
621 break;
622
623 case L'/':
624 *pchTarget = L'\\';
625 ++pchTarget;
626 *pchTarget = L'/';
627 break;
628
629 case L'\b':
630 *pchTarget = L'\\';
631 ++pchTarget;
632 *pchTarget = L'b';
633 break;
634
635 case L'\f':
636 *pchTarget = L'\\';
637 ++pchTarget;
638 *pchTarget = L'f';
639 break;
640
641 case L'\n':
642 *pchTarget = L'\\';
643 ++pchTarget;
644 *pchTarget = L'n';
645 break;
646
647 case L'\r':
648 *pchTarget = L'\\';
649 ++pchTarget;
650 *pchTarget = L'r';
651 break;
652
653 case L'\t':
654 *pchTarget = L'\\';
655 ++pchTarget;
656 *pchTarget = L't';
657 break;
658
659 default:
660 *pchTarget = *pch;
661 break;
662 }
663
664 }
665
666 *pchTarget = L'\"';
667 ++pchTarget;
668 *pchTarget = L'\0';
669
670LExit:
671 return hr;
672}