在TCL中的字典中创建字典列表

shi*_*shu 2 json dictionary tcl

我被迫使用TCL做某些事情,我需要创建一个json字符串,如下所示:

{ "mainKey": "mainValue", "subKey": [{"key1":"value1"},{"key2":"value2"}]}
Run Code Online (Sandbox Code Playgroud)

所以我想这样做:

set subDict1 [dict create key1 value1]
set subDict2 [dict create key2 value2]
set subDictList [list $subDict1 $subDict2]
set finalDict [dict create mainKey mainValue subKey $subDictList]
Run Code Online (Sandbox Code Playgroud)

当我将此字典转换为json时,我得到:

{"mainKey":"mainValue", "subKey":{"key1 value1":{"key2":"value2"}}} 
Run Code Online (Sandbox Code Playgroud)

而不是必需的:

{ "mainKey": "mainValue", "subKey": [{"key1":"value1"},{"key2":"value2"}]}
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

sle*_*man 5

首先,您必须了解TCL是一种非常无类型的语言。TCL中的列表和字典到底是什么?

在Tcl中,列表是一个格式正确的字符串,其中列表的每个成员之间都用空格(空格,制表符或换行符)分隔,并且如果项目中包含的数据包含空格,则可以通过以下方式对它们进行转义:

  1. 使用反斜杠转义:

    "this is a list\ of\ four\ items"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用""分组:

    {this is a "list of four items"}
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用{}分组:

    {this is a {list of four items}}
    
    Run Code Online (Sandbox Code Playgroud)

请注意,在内部,将字符串解析为列表后,Tcl将使用其他内部数据结构来存储列表以提高速度。但是从语义上讲,它仍然是一个字符串。就像HTML是特殊格式的字符串或JSON是特殊格式的字符串一样,Tcl采取的态度是列表只不过是特殊格式的字符串。

那么,什么是字典?在Tcl字典中是具有偶数个元素的列表。而已。没什么特别的。因此,字典在语义上也是字符串(尽管如上所述,一旦tcl看到您将该字符串用作字典,它将把它编译为不同的数据结构以优化速度)。

再次注意tcl的核心理念:几乎所有数据结构(数组除外)都是字符串,碰巧以具有特殊含义的方式进行了格式化。

这就是为什么您不能将tcl数据结构自动转换为JSON的原因-如果您要求Tcl猜测数据结构是什么,那么最终编写该猜测函数的程序员希望得到它。在您的情况下,似乎默认情况下始终检测具有偶数个元素作为dict的列表。

那么如何正确生成JSON?

有几种方法可以做到这一点。您当然可以使用自定义的专用于循环或函数将数据结构(同样,这只是一种特殊格式的字符串)转换为JSON。

几年前,我编写了这个JSON编译器:

# data is plain old tcl values
# spec is defined as follows:
# {string} - data is simply a string, "quote" it if it's not a number
# {list} - data is a tcl list of strings, convert to JSON arrays
# {list list} - data is a tcl list of lists
# {list dict} - data is a tcl list of dicts
# {dict} - data is a tcl dict of strings
# {dict xx list} - data is a tcl dict where the value of key xx is a tcl list
# {dict * list} - data is a tcl dict of lists
# etc..
proc compile_json {spec data} {
    while [llength $spec] {
        set type [lindex $spec 0]
        set spec [lrange $spec 1 end]

        switch -- $type {
            dict {
                lappend spec * string

                set json {}
                foreach {key val} $data {
                    foreach {keymatch valtype} $spec {
                        if {[string match $keymatch $key]} {
                            lappend json [subst {"$key":[
                                compile_json $valtype $val]}]
                            break
                        }
                    }
                }
                return "{[join $json ,]}"
            }
            list {
                if {![llength $spec]} {
                    set spec string
                } else {
                    set spec [lindex $spec 0]
                }
                set json {}
                foreach {val} $data {
                    lappend json [compile_json $spec $val]
                }
                return "\[[join $json ,]\]"
            }
            string {
                if {[string is double -strict $data]} {
                    return $data
                } else {
                    return "\"$data\""
                }
            }
            default {error "Invalid type"}
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(有关JSON解析的原始实现和讨论,请参见http://wiki.tcl.tk/JSON

因为tcl永远无法正确猜出您的“字符串”是什么,所以我选择向函数提供格式字符串以正确解释tcl数据结构。例如,使用上面的函数来编译您的字典,您可以这样调用它:

compile_json {dict subKey list} finalDict
Run Code Online (Sandbox Code Playgroud)

我恳求tcllib维护者窃取我的代码,因为我仍然相信这是在tcl中处理JSON的正确方法,但到目前为止,它仍然不在tcllib中。

顺便说一句:我将上述代码许可为公共领域,您或任何人都可以要求其完整所有权。