Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Improved error handling and error messages.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | asTypedList
Files: files | file ages | folders
SHA3-256: cf19b79d68e957ac279cca0adfacf34ee891641a5d21f25c1031a5c0ab0a8460
User & Date: rolf 2024-09-27 00:15:47
Context
2024-09-27
12:17
Merged from trunk. check-in: 78a7f45535 user: rolf tags: asTypedList
00:15
Improved error handling and error messages. check-in: cf19b79d68 user: rolf tags: asTypedList
2024-09-23
20:41
Renamed createDocumentFromTypedList createFromTypedList - it is still a mouthful. check-in: 6bc8b7435a user: rolf tags: asTypedList
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/domjson.c.

519
520
521
522
523
524
525
526
527
528
529
530
531
532
533





534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569

570
571
572
573
574

575
576
577
578
579

580
581
582




583






584






585
586







587







588
589
590
591
592
593
594
595
    Tcl_Interp *interp,
    Tcl_Obj *list,
    Tcl_Obj **typeValue
    )
{
    Tcl_Obj *symbol;
    char *s;
    domLength slen;
    
    if (Tcl_ListObjIndex (interp, list, 0, &symbol) != TCL_OK) {
        return -1;
    }
    if (!symbol) {
        /* Empty lists are not allowed. */
        SetResult ("Invalid list format: Empty list.");





        return -1;
    }
    Tcl_ListObjIndex (interp, list, 1, typeValue);
    s = Tcl_GetStringFromObj (symbol, &slen);
    if (strcmp (s, "STRING") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Invalid list format: Missing value for STRING.");
            return -1;
        }
        return JSON_STRING;
    } else if (strcmp (s, "OBJECT") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Invalid list format: Missing value for OBJECT.");
            return -1;
        }
        return JSON_OBJECT;
    } else if (strcmp (s, "NUMBER") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Invalid list format: Missing value for NUMBER.");
            return -1;
        }
        s = Tcl_GetStringFromObj (*typeValue, &slen);
        if (!isJSONNumber (s, slen)) {
            SetResult ("Invalid list format: Not a valid NUMBER value.");
            return -1;
        }
        return JSON_NUMBER;
    } else if (strcmp (s, "ARRAY") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Invalid list format: Missing value for ARRAY.");
            return -1;
        }
        return JSON_ARRAY;
    } else if (strcmp (s, "TRUE") == 0) {
        if (*typeValue != NULL) {
            SetResult ("Invalid list format: No value expected for TRUE.");

        }
        return JSON_TRUE;
    } else if (strcmp (s, "FALSE") == 0) {
        if (*typeValue != NULL) {
            SetResult ("Invalid list format: No value expected for FALSE.");

        }
        return JSON_FALSE;
    } else if (strcmp (s, "NULL") == 0) {
        if (*typeValue != NULL) {
            SetResult ("Invalid list format: No value expected for NULL.");

        }
        return JSON_NULL;
    } else {




        if (*typeValue != NULL) {






            SetResult3 ("Invalid list format: Invalid symbol \"", s, "\".");






        }
        return -1;







    }            







}

static int
TypedList2DOMWorker (
    Tcl_Interp *interp,
    domNode *parent,
    Tcl_Obj *value
    )







|






|
>
>
>
>
>






|





|





|




|





|





|
>




|
>




|
>



>
>
>
>
|
>
>
>
>
>
>
|
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>








519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
    Tcl_Interp *interp,
    Tcl_Obj *list,
    Tcl_Obj **typeValue
    )
{
    Tcl_Obj *symbol;
    char *s;
    domLength slen, llen;
    
    if (Tcl_ListObjIndex (interp, list, 0, &symbol) != TCL_OK) {
        return -1;
    }
    if (!symbol) {
        /* Empty lists are not allowed. */
        SetResult ("Empty list.");
        return -1;
    }
    Tcl_ListObjLength (interp, list, &llen);
    if (llen > 2) {
        SetResult ("Too much list elements.");
        return -1;
    }
    Tcl_ListObjIndex (interp, list, 1, typeValue);
    s = Tcl_GetStringFromObj (symbol, &slen);
    if (strcmp (s, "STRING") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Missing value for STRING.");
            return -1;
        }
        return JSON_STRING;
    } else if (strcmp (s, "OBJECT") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Missing value for OBJECT.");
            return -1;
        }
        return JSON_OBJECT;
    } else if (strcmp (s, "NUMBER") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Missing value for NUMBER.");
            return -1;
        }
        s = Tcl_GetStringFromObj (*typeValue, &slen);
        if (!isJSONNumber (s, slen)) {
            SetResult ("Not a valid NUMBER value.");
            return -1;
        }
        return JSON_NUMBER;
    } else if (strcmp (s, "ARRAY") == 0) {
        if (*typeValue == NULL) {
            SetResult ("Missing value for ARRAY.");
            return -1;
        }
        return JSON_ARRAY;
    } else if (strcmp (s, "TRUE") == 0) {
        if (*typeValue != NULL) {
            SetResult ("No value expected for TRUE.");
            return -1;
        }
        return JSON_TRUE;
    } else if (strcmp (s, "FALSE") == 0) {
        if (*typeValue != NULL) {
            SetResult ("No value expected for FALSE.");
            return -1;
        }
        return JSON_FALSE;
    } else if (strcmp (s, "NULL") == 0) {
        if (*typeValue != NULL) {
            SetResult ("No value expected for NULL.");
            return -1;
        }
        return JSON_NULL;
    } else {
        SetResult3 ("Unkown symbol \"", s, "\".");
        return -1;
    }            
}

static void objectErrMsg (
    Tcl_Interp *interp,
    domNode *node
    ) 
{
    Tcl_Obj *msg;

    msg = Tcl_GetObjResult (interp);
    Tcl_IncrRefCount (msg);
    Tcl_ResetResult (interp);
    Tcl_AppendResult (interp, "object property \"", node->nodeName,
                      "\": ", Tcl_GetString (msg), (char *)NULL);
    Tcl_DecrRefCount (msg);
}

static void arrayErrMsg (
    Tcl_Interp *interp,
    domLength i
    ) 
{
    Tcl_Obj *msg;
    char buf[20];

    msg = Tcl_GetObjResult (interp);
    Tcl_IncrRefCount (msg);
    Tcl_ResetResult (interp);
    sprintf (buf, domLengthConversion, i + 1);
    Tcl_AppendResult (interp, "array element ", buf, ": ",
                      Tcl_GetString (msg), (char *) NULL);
    Tcl_DecrRefCount (msg);
}

static int
TypedList2DOMWorker (
    Tcl_Interp *interp,
    domNode *parent,
    Tcl_Obj *value
    )
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625

626
627
628
629
630
631

632
633
634
635
636
637
638
    
    switch (parent->info) {
    case JSON_OBJECT:
        if (Tcl_ListObjLength (interp, value, &llen) != TCL_OK) {
            return TCL_ERROR;
        }
        if (llen % 2 != 0) {
            Tcl_ResetResult (interp);
            Tcl_AppendResult (interp, "An OBJECT value must be a Tcl "
                              "list with an even number of elements.",
                              (char *) NULL);
            return TCL_ERROR;
        }
        for (i = 0; i < llen; i += 2) {
            /* Since we loop over all elements every element is
             * present and there is no need to check property or
             * pvalue for NULL. */
            Tcl_ListObjIndex (interp, value, i, &property);
            Tcl_ListObjIndex (interp, value, i+1, &pvalue);
            pnode = domAppendNewElementNode (parent, Tcl_GetString (property),
                                             NULL);
            jsonType = getJSONTypeFromList (interp, pvalue, &pdetail);
            if (jsonType < 0) {

                return TCL_ERROR;
            }
            if (jsonType < 3) {
                /* JSON_OBJECT or JSON_ARRAY */
                pnode->info = jsonType;
                if (TypedList2DOMWorker (interp, pnode, pdetail) != TCL_OK) {

                    return TCL_ERROR;
                }
            } else {
                /* The other json types are represented by a text node.*/
                switch (jsonType) {
                case JSON_NUMBER:
                case JSON_STRING:







<
|
|
<












>






>







641
642
643
644
645
646
647

648
649

650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
    
    switch (parent->info) {
    case JSON_OBJECT:
        if (Tcl_ListObjLength (interp, value, &llen) != TCL_OK) {
            return TCL_ERROR;
        }
        if (llen % 2 != 0) {

            SetResult ("An OBJECT value must be a Tcl list with an even "
                       "number of elements.");

            return TCL_ERROR;
        }
        for (i = 0; i < llen; i += 2) {
            /* Since we loop over all elements every element is
             * present and there is no need to check property or
             * pvalue for NULL. */
            Tcl_ListObjIndex (interp, value, i, &property);
            Tcl_ListObjIndex (interp, value, i+1, &pvalue);
            pnode = domAppendNewElementNode (parent, Tcl_GetString (property),
                                             NULL);
            jsonType = getJSONTypeFromList (interp, pvalue, &pdetail);
            if (jsonType < 0) {
                objectErrMsg (interp, pnode);
                return TCL_ERROR;
            }
            if (jsonType < 3) {
                /* JSON_OBJECT or JSON_ARRAY */
                pnode->info = jsonType;
                if (TypedList2DOMWorker (interp, pnode, pdetail) != TCL_OK) {
                    objectErrMsg (interp, pnode);
                    return TCL_ERROR;
                }
            } else {
                /* The other json types are represented by a text node.*/
                switch (jsonType) {
                case JSON_NUMBER:
                case JSON_STRING:
654
655
656
657
658
659
660

661
662
663
664
665
666
667
668
669



670
671
672
673
674
675
676



677
678
679
680
681
682
683
        if (Tcl_ListObjLength (interp, value, &llen) != TCL_OK) {
            return TCL_ERROR;
        }
        for (i = 0; i < llen; i++) {
            Tcl_ListObjIndex (interp, value, i, &aelm);
            jsonType = getJSONTypeFromList (interp, aelm, &adetail);
            if (jsonType < 0) {

                return TCL_ERROR;
            }
            switch (jsonType) {
            case JSON_OBJECT:
                container = domAppendNewElementNode (parent,
                                                     JSON_OBJECT_CONTAINER,
                                                     NULL);
                container->info = JSON_OBJECT;                
                TypedList2DOMWorker (interp, container, adetail);



                break;
            case JSON_ARRAY:
                container = domAppendNewElementNode (parent,
                                                     JSON_ARRAY_CONTAINER,
                                                     NULL);
                container->info = JSON_ARRAY;                
                TypedList2DOMWorker (interp, container, adetail);



                break;
            default:
                /* The other json types are represented by a text node.*/
                switch (jsonType) {
                case JSON_NUMBER:
                case JSON_STRING:
                    str = Tcl_GetStringFromObj (adetail, &strl);







>








|
>
>
>






|
>
>
>







692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
        if (Tcl_ListObjLength (interp, value, &llen) != TCL_OK) {
            return TCL_ERROR;
        }
        for (i = 0; i < llen; i++) {
            Tcl_ListObjIndex (interp, value, i, &aelm);
            jsonType = getJSONTypeFromList (interp, aelm, &adetail);
            if (jsonType < 0) {
                arrayErrMsg (interp, i);
                return TCL_ERROR;
            }
            switch (jsonType) {
            case JSON_OBJECT:
                container = domAppendNewElementNode (parent,
                                                     JSON_OBJECT_CONTAINER,
                                                     NULL);
                container->info = JSON_OBJECT;                
                if (TypedList2DOMWorker (interp, container, adetail) != TCL_OK) {
                    arrayErrMsg (interp, i);
                    return TCL_ERROR;
                }
                break;
            case JSON_ARRAY:
                container = domAppendNewElementNode (parent,
                                                     JSON_ARRAY_CONTAINER,
                                                     NULL);
                container->info = JSON_ARRAY;                
                if (TypedList2DOMWorker (interp, container, adetail) != TCL_OK) {
                    arrayErrMsg (interp, i);
                    return TCL_ERROR;
                }
                break;
            default:
                /* The other json types are represented by a text node.*/
                switch (jsonType) {
                case JSON_NUMBER:
                case JSON_STRING:
                    str = Tcl_GetStringFromObj (adetail, &strl);
710
711
712
713
714
715
716
717
718
719
720
721
722
723






724
725
726
727
728
729
730
731



732




733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
    Tcl_Interp *interp,
    Tcl_Obj *typedList
    )
{
    domDocument *doc;
    domNode *rootNode;
    domTextNode *textNode;
    Tcl_Obj *value;
    char *str;
    domLength strl;
    int jsonType;

    jsonType = getJSONTypeFromList (interp, typedList, &value);
    if (jsonType < 0) {






        return NULL;
    }
    doc  = domCreateDoc (NULL, 0);
    rootNode = doc->rootNode;
    if (jsonType < 3) {
        /* JSON_OBJECT or JSON_ARRAY */
        rootNode->info = jsonType;
        if (TypedList2DOMWorker (interp, rootNode, value) != TCL_OK) {



            goto error;




        }
    } else {
        /* The other json types are represented by a text node.*/
        if (jsonType > 5) {
            /* JSON_STRING or JSON_NUMBER */
            str = Tcl_GetStringFromObj (value, &strl);
        } else {
            str = "";
            strl = 0;
        }
        textNode = domNewTextNode (doc, str, strl, TEXT_NODE);
        textNode->info = jsonType;
        domAppendChild (rootNode, (domNode *) textNode);
    }
    return doc;
error:
    tcldom_deleteDoc (interp, doc);
    return NULL;
}

domDocument *
JSON_Parse (
    char *json,    /* Complete text of the json string being parsed */
    char *documentElement, /* name of the root element, may be NULL */
    int   maxnesting,







|






>
>
>
>
>
>








>
>
>
|
>
>
>
>















<
<
<







755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805



806
807
808
809
810
811
812
    Tcl_Interp *interp,
    Tcl_Obj *typedList
    )
{
    domDocument *doc;
    domNode *rootNode;
    domTextNode *textNode;
    Tcl_Obj *value, *msg;
    char *str;
    domLength strl;
    int jsonType;

    jsonType = getJSONTypeFromList (interp, typedList, &value);
    if (jsonType < 0) {
        msg = Tcl_GetObjResult (interp);
        Tcl_IncrRefCount (msg);
        Tcl_ResetResult (interp);
        Tcl_AppendResult (interp, "Invalid typed list format: ",
                          Tcl_GetString (msg), (char *) NULL);
        Tcl_DecrRefCount (msg);
        return NULL;
    }
    doc  = domCreateDoc (NULL, 0);
    rootNode = doc->rootNode;
    if (jsonType < 3) {
        /* JSON_OBJECT or JSON_ARRAY */
        rootNode->info = jsonType;
        if (TypedList2DOMWorker (interp, rootNode, value) != TCL_OK) {
            msg = Tcl_GetObjResult (interp);
            Tcl_IncrRefCount (msg);
            domFreeDocument(doc, NULL, interp);
            Tcl_ResetResult (interp);
            Tcl_AppendResult (interp, "Invalid typed list format: ",
                              Tcl_GetString (msg), (char *) NULL);
            Tcl_DecrRefCount (msg);
            return NULL;
        }
    } else {
        /* The other json types are represented by a text node.*/
        if (jsonType > 5) {
            /* JSON_STRING or JSON_NUMBER */
            str = Tcl_GetStringFromObj (value, &strl);
        } else {
            str = "";
            strl = 0;
        }
        textNode = domNewTextNode (doc, str, strl, TEXT_NODE);
        textNode->info = jsonType;
        domAppendChild (rootNode, (domNode *) textNode);
    }
    return doc;



}

domDocument *
JSON_Parse (
    char *json,    /* Complete text of the json string being parsed */
    char *documentElement, /* name of the root element, may be NULL */
    int   maxnesting,

Changes to tests/domjson.test.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

testConstraint needExpand 1
if {$tcl_version < 8.5} {
    testConstraint needExpand 0
}
# See below the comment in proc dom for explanation
if {[info commands _dom] eq ""} {
    testConstraint failtest 1
} else {
    testConstraint failtest 0
}
namespace eval nodeCmds {
    dom createNodeCmd elementNode e1
    dom createNodeCmd -jsonType ARRAY elementNode jae1
    dom createNodeCmd elementNode e2
    dom createNodeCmd commentNode c
    dom createNodeCmd textNode    t







|

|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

testConstraint needExpand 1
if {$tcl_version < 8.5} {
    testConstraint needExpand 0
}
# See below the comment in proc dom for explanation
if {[info commands _dom] eq ""} {
    testConstraint nonetest 1
} else {
    testConstraint nonetest 0
}
namespace eval nodeCmds {
    dom createNodeCmd elementNode e1
    dom createNodeCmd -jsonType ARRAY elementNode jae1
    dom createNodeCmd elementNode e2
    dom createNodeCmd commentNode c
    dom createNodeCmd textNode    t
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
test json-1.8 {Parse JSON - true, false, null} {
    set doc [dom parse -json -jsonroot "JSONObject" {{"a":true,"b":false,"c":null,"d":"true","e":""}}]
    set result [$doc asXML -indent none]
    $doc delete
    set result
} {<JSONObject><a>true</a><b>false</b><c>null</c><d>true</d><e></e></JSONObject>}

test json-1.9 {JSON syntax error} {failtest} {
    set result [catch {dom parse -json {{"a" "a value"}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 5
"{"a" " <--Error-- a value"}"}}

test json-1.10 {JSON syntax error} {failtest} {
    set result [catch {dom parse -json {{"a":00.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 6
"{"a":00 <--Error-- .23}"}}

test json-1.11 {JSON syntax error} {failtest} {
    set result [catch {dom parse -json {{"a":-00.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 7
"{"a":-00 <--Error-- .23}"}}

test json-1.12 {JSON syntax error} {failtest} {
    set result [catch {dom parse -json {{"a":.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 5
"{"a":. <--Error-- 23}"}}

test json-1.13 {JSON syntax error} {failtest} {
    set result [catch {dom parse -json {{"a":-.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 6
"{"a":-. <--Error-- 23}"}}

test json-1.14 {JSON syntax error} {failtest} {
    set result [catch {dom parse -json {{"a":-}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 5
"{"a":- <--Error-- }"}}

test json-1.15 {Parse JSON - nested object} {
    set doc [dom parse -json {["a",["aa","bb"],"b"]}]
    set result [$doc asXML -indent none]
    $doc delete
    set result
} "a<arraycontainer>aabb</arraycontainer>b"

set notJsons {
    {{null}}
    {{1.23}}
    {{"string"}}
    {{"e":}}
}
test json-1.16 {Invalid input} {failtest} {
    set result ""
    set ind 0
    foreach notJson $notJsons {
        if {![catch {dom parse -json $notJson docNode} errMsg]} {
            lappend result $errMsg
        }
    }
    set result
} ""

test json-1.17 {Literal binary 0 (NUL, '\0') is not allowed in input} {failtest} {
    catch {dom parse -json "\"a\u0000\""}
} 1

test json-1.18 {Escaped binary 0 (NUL, '\0') is OK} {
    dom parse -json "\"a\\u0000\"" doc
    set result [$doc asJSON]
    $doc delete
    set result
} "\"a\\u0000\""

test json-1.19 {Invalid input - incomplete \u escape} {failtest} {
    set result 1
    foreach jsonstr {
        "ab\u00"
        "ab\ua"
        "ab\u12"
        "ab\u123"
        "ab\u123g"







|





|





|





|





|





|


















|










|










|







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
test json-1.8 {Parse JSON - true, false, null} {
    set doc [dom parse -json -jsonroot "JSONObject" {{"a":true,"b":false,"c":null,"d":"true","e":""}}]
    set result [$doc asXML -indent none]
    $doc delete
    set result
} {<JSONObject><a>true</a><b>false</b><c>null</c><d>true</d><e></e></JSONObject>}

test json-1.9 {JSON syntax error} {nonetest} {
    set result [catch {dom parse -json {{"a" "a value"}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 5
"{"a" " <--Error-- a value"}"}}

test json-1.10 {JSON syntax error} {nonetest} {
    set result [catch {dom parse -json {{"a":00.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 6
"{"a":00 <--Error-- .23}"}}

test json-1.11 {JSON syntax error} {nonetest} {
    set result [catch {dom parse -json {{"a":-00.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 7
"{"a":-00 <--Error-- .23}"}}

test json-1.12 {JSON syntax error} {nonetest} {
    set result [catch {dom parse -json {{"a":.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 5
"{"a":. <--Error-- 23}"}}

test json-1.13 {JSON syntax error} {nonetest} {
    set result [catch {dom parse -json {{"a":-.23}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 6
"{"a":-. <--Error-- 23}"}}

test json-1.14 {JSON syntax error} {nonetest} {
    set result [catch {dom parse -json {{"a":-}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 5
"{"a":- <--Error-- }"}}

test json-1.15 {Parse JSON - nested object} {
    set doc [dom parse -json {["a",["aa","bb"],"b"]}]
    set result [$doc asXML -indent none]
    $doc delete
    set result
} "a<arraycontainer>aabb</arraycontainer>b"

set notJsons {
    {{null}}
    {{1.23}}
    {{"string"}}
    {{"e":}}
}
test json-1.16 {Invalid input} {nonetest} {
    set result ""
    set ind 0
    foreach notJson $notJsons {
        if {![catch {dom parse -json $notJson docNode} errMsg]} {
            lappend result $errMsg
        }
    }
    set result
} ""

test json-1.17 {Literal binary 0 (NUL, '\0') is not allowed in input} {nonetest} {
    catch {dom parse -json "\"a\u0000\""}
} 1

test json-1.18 {Escaped binary 0 (NUL, '\0') is OK} {
    dom parse -json "\"a\\u0000\"" doc
    set result [$doc asJSON]
    $doc delete
    set result
} "\"a\\u0000\""

test json-1.19 {Invalid input - incomplete \u escape} {nonetest} {
    set result 1
    foreach jsonstr {
        "ab\u00"
        "ab\ua"
        "ab\u12"
        "ab\u123"
        "ab\u123g"
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
test json-3.4.1 {unescaping} {
    set doc [dom parse -jsonroot json -json {["\\a","\u0071"]}]
    set result [$doc asXML -indent none]
    $doc delete
    set result
} {<json>\aq</json>}

test json-3.5 {unescaping} {failtest} {
    set result [catch {dom parse -json {{"this":"a\lb"}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 11
"{"this":"a\l <--Error-- b"}"}}

test json-3.6 {unescaping} {
    set doc [dom parse -json {{"this":"a\nbc"}}]







|







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
test json-3.4.1 {unescaping} {
    set doc [dom parse -jsonroot json -json {["\\a","\u0071"]}]
    set result [$doc asXML -indent none]
    $doc delete
    set result
} {<json>\aq</json>}

test json-3.5 {unescaping} {nonetest} {
    set result [catch {dom parse -json {{"this":"a\lb"}}} errMsg]
    list $result $errMsg
} {1 {error "JSON syntax error" at position 11
"{"this":"a\l <--Error-- b"}"}}

test json-3.6 {unescaping} {
    set doc [dom parse -json {{"this":"a\nbc"}}]
2643
2644
2645
2646
2647
2648
2649


2650
2651
2652
2653
2654
2655
2656
			4,
			"abc"
		]
	},
	"b": "bvalue"
}}



test json-7.1 {jsonType} {
    set doc [dom parse {<j>foo</j>}]
    set root [$doc documentElement]
    set result [list]
    lappend result [$root asJSON]
    lappend result [$root jsonType]
    $root jsonType ARRAY







>
>







2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
			4,
			"abc"
		]
	},
	"b": "bvalue"
}}

if {[info commands _dom] eq ""} {

test json-7.1 {jsonType} {
    set doc [dom parse {<j>foo</j>}]
    set root [$doc documentElement]
    set result [list]
    lappend result [$root asJSON]
    lappend result [$root jsonType]
    $root jsonType ARRAY
2743
2744
2745
2746
2747
2748
2749

2750
2751
2752
2753
2754
2755
2756
        nodeCmds::true
        nodeCmds::false
    }
    set result [$doc asJSON]
    $doc delete
    set result
} {[null,true,false]}


test json-9.1 {cloneNode -deep} {
    dom parse -json {[["a",1,"b",{"foo":"bar","baz":"boo"},null],"",null]} doc
    dom createDocument some other
    $other documentElement root
    $root appendChild [[$doc firstChild] cloneNode -deep]
    set result [[$root firstChild] asJSON]







>







2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
        nodeCmds::true
        nodeCmds::false
    }
    set result [$doc asJSON]
    $doc delete
    set result
} {[null,true,false]}
}

test json-9.1 {cloneNode -deep} {
    dom parse -json {[["a",1,"b",{"foo":"bar","baz":"boo"},null],"",null]} doc
    dom createDocument some other
    $other documentElement root
    $root appendChild [[$doc firstChild] cloneNode -deep]
    set result [[$root firstChild] asJSON]
3032
3033
3034
3035
3036
3037
3038


3039
3040
3041
3042
3043
3044
3045
    } {
        set doc [dom parse -json $json]
        lappend result [$doc asTypedList]
        $doc delete
    }
    set result
} {{STRING {string foo}} NULL TRUE FALSE {NUMBER -1.23}}



test json-12.1 {createFromTypedList} {
    set doc [dom createFromTypedList {OBJECT {Titel {STRING Wirtschaftsinformatik} Keywords {ARRAY {{STRING Introduction} {STRING Basics}}} Year {NUMBER 2022}}}]
    set result [$doc asJSON]
    $doc delete
    set result
} {{"Titel":"Wirtschaftsinformatik","Keywords":["Introduction","Basics"],"Year":2022}}







>
>







3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
    } {
        set doc [dom parse -json $json]
        lappend result [$doc asTypedList]
        $doc delete
    }
    set result
} {{STRING {string foo}} NULL TRUE FALSE {NUMBER -1.23}}

if {[info commands _dom] eq ""} {

test json-12.1 {createFromTypedList} {
    set doc [dom createFromTypedList {OBJECT {Titel {STRING Wirtschaftsinformatik} Keywords {ARRAY {{STRING Introduction} {STRING Basics}}} Year {NUMBER 2022}}}]
    set result [$doc asJSON]
    $doc delete
    set result
} {{"Titel":"Wirtschaftsinformatik","Keywords":["Introduction","Basics"],"Year":2022}}
3079
3080
3081
3082
3083
3084
3085


























3086
3087
3088
3089
3090
3091
3092
        } else {
            lappend result 1
        }
        $doc delete
    }
    set result
} {1 1 1 1 1}



























if {[info commands _dom] eq ""} {
    rename dom _dom
    proc dom {args} {
        if {[lindex $args 0] != "parse" || [lsearch -exact $args "-json"] < 0} {
            return [uplevel 1 [linsert $args 0 _dom]]
        }







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
        } else {
            lappend result 1
        }
        $doc delete
    }
    set result
} {1 1 1 1 1}

test json-12.5 {createFromTypedList} {
    set result ""
    foreach wrong {
        NUL
        ""
        {one two}
        {STRING two three}
    } {
        catch {dom createFromTypedList $wrong} errMsg
        lappend result $errMsg
    }
    set result
} {{Invalid typed list format: Unkown symbol "NUL".} {Invalid typed list format: Empty list.} {Invalid typed list format: Unkown symbol "one".} {Invalid typed list format: Too much list elements.}}

test json-12.6 {createFromTypedList} {
    catch {dom createFromTypedList {OBJECT {one {STRING two} two {}}}} errMsg
    set errMsg
} {Invalid typed list format: object property "two": Empty list.}

test json-12.7 {createFromTypedList} {
    catch {dom createFromTypedList {OBJECT {one {STRING two} two {ARRAY {{NUMBER 1} {NULL foo}}}}}} errMsg
    set errMsg
} {Invalid typed list format: object property "two": array element 2: No value expected for NULL.}

}

if {[info commands _dom] eq ""} {
    rename dom _dom
    proc dom {args} {
        if {[lindex $args 0] != "parse" || [lsearch -exact $args "-json"] < 0} {
            return [uplevel 1 [linsert $args 0 _dom]]
        }
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
            }
        }
        if {[catch {
            _dom parse -json $data doc
        } errMsg]} {
            # Some tests check if invalid json data is detected. Since
            # we need valid json data for what is tested here that
            # tests should be marked with constraint failtest. To
            # raise error here does not help much because if the test
            # data is expected to be invalid the command will be
            # [catch]ed. Therefore the puts to catch attention.
            puts  "Unexpeced invalid json data '$data'"
        }
        set njson [$doc asJSON]
        set typedList [$doc asTypedList]







|







3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
            }
        }
        if {[catch {
            _dom parse -json $data doc
        } errMsg]} {
            # Some tests check if invalid json data is detected. Since
            # we need valid json data for what is tested here that
            # tests should be marked with constraint nonetest. To
            # raise error here does not help much because if the test
            # data is expected to be invalid the command will be
            # [catch]ed. Therefore the puts to catch attention.
            puts  "Unexpeced invalid json data '$data'"
        }
        set njson [$doc asJSON]
        set typedList [$doc asTypedList]