SQLite 확장으로 Tcl과 JSON 연결하기
SQLite 확장으로 Tcl과 JSON 연결하기 (Bridging Tcl and JSON with the SQLite Extension)
Tcl SQLite 확장은 Tcl과 JSON 사이의 임피던스 부정합(impedance mismatch)을 완화하는 브리지 역할을 할 수 있어요. 파싱과 이스케이프 처리의 대부분을 Tcl에서 SQLite로 거의 노력 없이 넘길 수 있어요. 이 문서는 SQLite를 통해 JSON을 소비(consume)하고 생성(emit)하는 기법을 보여줘요.
출처: 문서
본문
1. SQLite로 Tcl과 JSON 연결하기
Tcl SQLite 확장은 Tcl과 JSON 사이의 임피던스 부정합을 완화하는 브리지 역할을 할 수 있어요. 파싱과 이스케이프 처리를 Tcl에서 SQLite로 거의 노력 없이 모두 옮겨요. 아래 절들은 SQLite를 통해 JSON을 소비하고 생성하는 기법을 보여줘요.
1.1. JSON 소비하기
JSON 페이로드를 소비하고 분해하는 가장 간단한 방법 중 하나는 그것을 SQLite에 넣고 Tcl의 lassign 함수로 결과를 추출하는 거예요:
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 생성: DB 테이블을 JSON 객체로
여기에 설명된 접근 방식은 인메모리 db를 사용해 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, boolean처럼 보이는 값을 문자열이 아니라 그 자체로 생성할 수 있도록 타입 처리를 충실히 처리해요.
#
# 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"
}
}
더 알아보기 (Learn more)
- JSON Functions in SQLite — JSON1 확장 함수 문서
- The Tcl interface to the SQLite library — Tcl SQLite 확장 전반
- Processing JSON in Tcl — Tcl에서 JSON 처리 팁