Spec-Zone.ru › SQLite

Связь Tcl и JSON с помощью расширения SQLite

Оглавление
1. Использование SQLite для связи Tcl и JSON
1.1. Обработка JSON
1.2. Формирование JSON: таблица БД как объект JSON
1.3. Формирование JSON: поэтапное построение

1. Использование SQLite для связи Tcl и JSON

Расширение SQLite для Tcl может служить связующим звеном, устраняя несоответствие между Tcl и JSON и перенося всю обработку и экранирование из Tcl в SQLite с минимальными усилиями. В разделах ниже показаны способы чтения и формирования JSON с помощью SQLite.

1.1. Обработка JSON

Один из простейших способов обработать и разобрать данные JSON — передать их в SQLite и извлечь результаты с помощью функции lassign в Tcl:

set json theJSON...
lassign [db eval {
  with payload(j) as (select jsonb($json)) select
  -- A simple value:
  j->>'foo',
  -- A sub-object or array:
  CASE WHEN json_valid(j->>'bar') THEN j->>'bar' ELSE NULL END
  -- Any other fields...
  from payload
}] foo bar

Массивы можно загрузить с помощью json_each():

set json {[1, "two", 3.0, {"nested": true}, null]}
set myList [db eval {
  select value from json_each($json)
}]

# Emits: 1 two 3.0 {{"nested":true}} {}

Массивы объектов можно преобразовать в список объектов dict:

set json {[{"id":1, "v":"A"}, {"id":2, "v":"B"}]}
set myDictList [db eval {
  select json_object('id', value->>'id', 'v', value->>'v')
  from json_each($json)
}]

# Emits: {{"id":1,"v":"A"}} {{"id":2,"v":"B"}}

Элементы объектов в массивах объектов можно извлечь способом, подобным описанному в первом примере выше.

1.2. Формирование JSON: таблица БД как объект JSON

Описанный здесь подход использует базу данных в памяти для поэтапного построения объекта JSON и автоматически обрабатывает различия между типами данных, например числами и строками.

#
# Initialize, if needed, the sqlite3 connection for the JR temp db and
# return its command name.
#
# JR = JSON Response (to an HTTP request)
#
proc jr-db {} {
  if {"" eq [info command ::__jrdb]} {
    sqlite3 ::__jrdb ":memory:"
    ::__jrdb eval {
      -- Holds a list of key/value pairs for a JSON response object
      create table jr(k TEXT UNIQUE ON CONFLICT REPLACE,v ANY);
    }
  }
  return ::__jrdb
}

#
# Remove all entries from the JR buffer.
#
proc jr-reset {args} {
  [jr-db] eval {delete from jr}
}

#
# Set one or more key/value pairs in the JR object.
#
proc jr-set {args} {
  set db [jr-db]
  foreach {k v} $args {
    $db eval {insert into jr(k,v) values($k,$v)}
  }
}

#
# Returns a JSON-format object representing the current JR state.
#
proc jr-get {} {
  [jr-db] onecolumn {
    select json_group_object(k,CASE WHEN json_valid(v,1)
                             THEN json(v) ELSE v END) from jr
  }
}

Пример использования:

jr-set message "This is a message."
jr-set aBool true isNull null anInt 37 aFloat 42.42
puts [jr-get]

Результат:

{"message":"This is a message.","aBool":true,"isNull":null,"anInt":37,"aFloat":42.42}

1.3. Формирование JSON: поэтапное построение

В описанном здесь подходе используются две процедуры для поэтапного построения вложенного объекта или массива с корректной обработкой типов, благодаря чему целые числа, null и значения, похожие на логические, выводятся как есть, а не как строки.

#
# Returns the command name of the shared SQLite3 instance used by the
# X-to-JSON APIs, initializing it on demand.
#
proc json-db {} {
  if {"" eq [info command ::__jsonDb]} {
    sqlite3 ::__jsonDb ":memory:"
  }
  return ::__jsonDb
}

#
# Given a dict-style object, this creates a JSON object representation.
# It does not handle nested objects but each value may itself be the result
# of its own call to this method, which has a similar effect.
#
proc json-obj {kvps} {
  set q "select json_object("
  set n 0
  set nn 1
  foreach {k v} $kvps {
    set $n $k
    set $nn $v
    if {$n} {append q ","}
    append q "\$$n, CASE WHEN json_valid(\$$nn) THEN json(\$$nn) ELSE \$$nn END"
    # This may look like an SQL injection opportunity, but it's
    # injecting prepared statement placeholders, not values.
    incr n 2
    incr nn 2
  }
  append q ")"
  [json-db] onecolumn $q
}

#
# Returns a JSON-format array of the given arguments. See json-object
# for notes about nesting.
#
proc json-array {args} {
  set n 0
  set q "select json_array("
  foreach a $args {
    set $n $a
    if {$n} {append q ","}
    append q "CASE WHEN json_valid(\$$n) THEN json(\$$n) ELSE \$$n END"
    incr n
  }
  append q ")"
  [json-db] onecolumn $q
}

Пример использования:

proc json-pretty {arg {space "  "}} {
  [json-db] onecolumn {select json_pretty($arg,$space)}
}
set o {
  hi world
  number 17.3
  truth true
  lies false
  nil null
  nada "null"
  notNull "Null"
}
lappend o list [json-array hello there [json-obj {nested true}]]
lappend o obj [json-obj {name "Nested object"}]
puts [json-pretty [json-obj $o]]

Результат:

{
  "hi": "world",
  "number": 17.3,
  "truth": true,
  "lies": false,
  "nil": null,
  "nada": null,
  "notNull": "Null",
  "list": [
    "hello",
    "there",
    {
      "nested": true
    }
  ],
  "obj": {
    "name": "Nested object"
  }
}

Последнее обновление этой страницы: 2026-02-24 16:01:53Z

SQLite is in the Public Domain.
https://sqlite.org/tcljson.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API