Извлеките int, string, boolean и т. д. как соответствующий тип PostgreSQL из JSON

я чувствую, что мне просто не хватает чего-то простого здесь, но я просмотрел документацию PostgreSQL по JSON и операторам и функциям JSON и не вижу ничего, объясняющего это.

легко превратить вещи в JSON в PostgreSQL:

SELECT *, pg_typeof(j) FROM (VALUES
(to_json(5)),
(to_json(true)),
(to_json('foo'::TEXT))
) x (j);

вернет мне хороший набор результатов, полный jsons:

   j   | pg_typeof
-------+-----------
 5     | json
 true  | json
 "foo" | json

но как мне преобразовать эти json значения назад в их первоначальные типы? Я не ожидал конечно, все это можно сделать в одном результирующем наборе, поскольку типы несовместимы. Я имею в виду индивидуально.

много чего я пробовал

кастинг, конечно, не работает:

SELECT to_json(5)::NUMERIC;

дает

ERROR:  cannot cast type json to numeric

если я попытаюсь злоупотребить

3 ответов


самый простой способ для булевых и чисел, кажется, сначала бросить в TEXT а затем приведите к соответствующему типу:

SELECT j::TEXT::NUMERIC
FROM (VALUES ('5.4575e6'::json)) x (j)
-- Result is 5457500, with column type NUMERIC
SELECT j::TEXT::BOOLEAN
FROM (VALUES ('true'::json)) x (j)
-- Result is t, with column type BOOLEAN

это оставляет строки, где вы вместо этого возвращаете цитируемое значение, пытаясь это:

SELECT j::TEXT
FROM (VALUES (to_json('foo'::TEXT))) x (j)
-- Result is "foo"

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

SELECT array_to_json(array[j])->>0
FROM (VALUES (to_json('foo'::TEXT))) x (j)
-- Result is foo, with column type TEXT.

первый шаг: если ваши значения содержатся в структурах (что обычно имеет место), вам нужно использовать правильные операторы / функции для извлечения строкового представления ваших данных:->> (9.3+), #>> (9.3+), json_each_text() (9.3+), json_array_elements_text() (9.4+).

выбрать текстовое представление элементов массива JSON' в 9.3, вам нужно что-то вроде этого:

select json_array ->> indx
from   generate_series(0, json_array_length(json_array) - 1) indx

для скалярных значений, вы можете использовать эту маленькую хитрость:

select ('[' || json_scalar || ']')::json ->> 0 -- ...

на данный момент, строки и nulls обрабатываются (JSON nulls преобразуются в sql NULLs этими методами). Выберите цифры, вам нужно использовать слепки для numeric (это полностью1 совместим с номером json). Выберите логические используйте муляжи boolean (как true и false поддерживаются в качестве входных представлений). Но обратите внимание, что приведения могут привести к сбою запроса, если их входное представление не общепринятый. F. ex. если у вас есть объект json в некоторых из ваших столбцов, этот объект обычно имейте некоторый ключ, который обычно число (но не всегда), этот запрос может завершиться неудачно:

select (json_object ->> 'key')::numeric
from   your_table

если у вас есть такие данные, вам нужно отфильтровать свой выбор с помощью json_typeof() (9.4+):

select (json_object ->> 'key')::numeric
from   your_table
where  json_typeof(json_object -> 'key') = 'number'

1 я не проверил их полные синтаксисы, но numeric также принимает научную нотацию, поэтому теоретически все номера json должны обрабатываться правильно.

для 9.2+ эта функция может проверить тип значения json:

create or replace function json_typeof(json)
  returns text
  language sql
  immutable
  strict
as $func$
  select case left(trim(leading E'\x20\x09\x0A\x0D' from ::text), 1)
    when 'n' then 'null'
    when 't' then 'boolean'
    when 'f' then 'boolean'
    when '"' then 'string'
    when '[' then 'array'
    when '{' then 'object'
    else 'number'
  end
$func$;

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

возможно, но я не думаю, что это было реализовано.