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