Пользователь уровня сериализации / маршалинга пакетов определяет типы записей в формате JSON

#json #oracle #plsql #oracle19c

#json #Oracle #plsql #oracle19c

Вопрос:

Я пытаюсь преобразовать тип записи, определенный в пакете PL / SQL, в JSON.

Я обнаружил, что в SQL я могу использовать select json_object(*) from SomeTable для возврата объектов, обладающих свойством для каждого столбца в таблице, но, похоже, я не могу сделать это с типами записей в коде PL / SQL.

Пример пакета с типами и функцией для возврата (сериализации) json на основе типа:

 create or replace package Customer as
  type RT_Address is record (
    Line1 varchar2(100),
    Line2 varchar2(100),
    City varchar2(100)
  );

  type RT_Customer is record (
    FirstName varchar2(100),
    LastName varchar2(100),
    Address RT_Address
  );

  function AsJson(P_Customer RT_Customer)
  return varchar2;

end;

create or replace package body devvanessa.Customer as

  function AsJson(P_Customer RT_Customer)
  return varchar2 is
    V_DOM jdom_t;
    V_JSON json_object_t;
    V_JSONBody varchar2(4000);
  begin

    V_JSON := json_object(P_Customer); -- PLS-00103: Encountered the symbol when expecting one of the following: . ( * @ % amp; -   / at mod remainder rem <een exponent (**)> || multiset value

    if V_DOM.append(P_CUSTOMER) then -- PLS-00306: wrong number or types of arguments
      null;
    end if;
    V_JSONBody := V_Json.STRINGIFY;

    return V_JSONBody;
  end;

end;
  

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

Как мне преобразовать тип записи в Json в PL / SQL, не указывая все отдельные поля отдельно. Мне также любопытно, как это будет работать наоборот.

Я искал различные источники, такие как документация по функциям JSON, документация JSON Oracle 19 и просто подсказки по завершению кода, которые я получил по типам json_object_t и jdom_t , но пока я не могу найти никаких доказательств того, что это вообще возможно.

Это сработало бы:

     V_JSon.Put('FirstName', P_Customer.FirstName);
    -- repeated for each property
  

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

Ответ №1:

У JSON_OBJECT_T нет конструктора, который принимает тип записи, поэтому вам нужно будет явно определить каждый из ключей / значений для определения JSON. JDOM_T не требуется для того, что вы пытаетесь сделать. Ниже приведен пример того, как преобразовать ваши типы записей в JSON и обратно.

 DECLARE
    TYPE RT_Address IS RECORD
    (
        Line1    VARCHAR2 (100),
        Line2    VARCHAR2 (100),
        City     VARCHAR2 (100)
    );

    TYPE RT_Customer IS RECORD
    (
        FirstName    VARCHAR2 (100),
        LastName     VARCHAR2 (100),
        Address      RT_Address
    );

    l_customer1   rt_customer
        := rt_customer ('John', 'Doe', rt_address ('123 Main Street', 'Apartment# 2A', 'London'));
    l_customer2   rt_customer
        := rt_customer ('Jane', 'Smith', rt_address ('456 Broken Dreams Blvd', NULL, 'Greenville'));
    l_json        json_object_t;
    l_record      rt_customer;

    FUNCTION customer_record_to_json (P_Customer RT_Customer)
        RETURN json_object_t
    IS
        l_address    json_object_t := json_object_t ();
        l_customer   json_object_t := json_object_t ();
    BEGIN
        l_address.put ('Line1', p_customer.address.line1);
        l_address.put ('Line2', p_customer.address.line2);
        l_address.put ('City', p_customer.address.city);

        l_customer.put ('FirstName', p_customer.firstname);
        l_customer.put ('LastName', p_customer.lastname);
        l_customer.put ('Address', l_address);

        RETURN l_customer;
    END;

    FUNCTION customer_json_to_record (p_customer_json json_object_t)
        RETURN rt_customer
    IS
        l_address_json   json_object_t := json_object_t ();

        l_address        rt_address;
        l_customer       rt_customer;
    BEGIN
        l_address_json := p_customer_json.get_object ('Address');
        l_address.line1 := l_address_json.get_string ('Line1');
        l_address.line2 := l_address_json.get_string ('Line2');
        l_address.city := l_address_json.get_string ('City');

        l_customer.firstname := p_customer_json.get_string ('FirstName');
        l_customer.lastname := p_customer_json.get_string ('LastName');
        l_customer.address := l_address;
        RETURN l_customer;
    END;
BEGIN
    l_json := customer_record_to_json (l_customer1);
    DBMS_OUTPUT.put_line ('Customer 1 (JSON): ' || l_json.stringify);
    l_record := customer_json_to_record (l_json);
    DBMS_OUTPUT.put_line ('Customer 1 (Record) (FirstName): ' || l_record.firstname);
    DBMS_OUTPUT.put_line ('Customer 1 (Record) (LastName): ' || l_record.lastname);
    DBMS_OUTPUT.put_line ('Customer 1 (Record) (Line1): ' || l_record.address.line1);
    DBMS_OUTPUT.put_line ('Customer 1 (Record) (Line2): ' || l_record.address.line2);
    DBMS_OUTPUT.put_line ('Customer 1 (Record) (City): ' || l_record.address.city);

    l_json := customer_record_to_json (l_customer2);
    DBMS_OUTPUT.put_line ('Customer 2 (JSON): ' || l_json.stringify);
    l_record := customer_json_to_record (l_json);
    DBMS_OUTPUT.put_line ('Customer 2 (Record) (FirstName): ' || l_record.firstname);
    DBMS_OUTPUT.put_line ('Customer 2 (Record) (LastName): ' || l_record.lastname);
    DBMS_OUTPUT.put_line ('Customer 2 (Record) (Line1): ' || l_record.address.line1);
    DBMS_OUTPUT.put_line ('Customer 2 (Record) (Line2): ' || l_record.address.line2);
    DBMS_OUTPUT.put_line ('Customer 2 (Record) (City): ' || l_record.address.city);
END;
  

Обновить

Можно просто JSON_OBJECT(*) создать объект JSON, но расширение * не будет работать с типами записей, так как вы получите ORA-40579: star expansion is not allowed .

Если вы хотите использовать JSON_OBJECT для создания JSON вместо JSON_OBJECT_T, используемые вами типы должны быть предопределены (не в блоке pl / sql), и вам все равно нужно будет определить каждое поле в структуре JSON. Вам также нужно будет определить тип таблицы типа RT_CUSTOMER, чтобы вы могли выполнять запросы из нее.

 CREATE TYPE RT_Address AS OBJECT (Line1 VARCHAR2 (100), Line2 VARCHAR2 (100), City VARCHAR2 (100));

CREATE TYPE RT_Customer AS OBJECT
(
    FirstName VARCHAR2 (100),
    LastName VARCHAR2 (100),
    Address RT_Address
);

CREATE TYPE rt_customer_t AS TABLE OF rt_customer;

SELECT json_object (
           'firstname' VALUE firstname,
           'lastname' VALUE lastname,
           'address' VALUE
               json_object ('line1' VALUE TREAT (address AS rt_address).line1,
                            'line2' VALUE TREAT (address AS rt_address).line2,
                            'city' VALUE TREAT (address AS rt_address).city)) as customer
  FROM TABLE (
           rt_customer_t (
               RT_Customer ('John',
                            'Doe',
                            rt_address ('123 Main Street', 'Apartment# 2A', 'London')),
               RT_Customer ('Jane',
                            'Smith',
                            rt_address ('456 Broken Dreams Blvd', NULL, 'Greenville'))));
  

Комментарии:

1. Спасибо! Не то, что я хотел услышать, но, боюсь, это все, что есть .. : o)