【问题标题】:How can I parse JSON string in PL/SQL如何在 PL/SQL 中解析 JSON 字符串
【发布时间】:2018-10-23 23:37:58
【问题描述】:

我想从表 Tests_1 中解析 CLOB 列中的 JSON 字符串,并将其插入另一个表 (Test_2)。​​

如何在不使用任何 JSON 库的情况下在 PL/SQL 中执行此操作?

create table Tests_1
(
  value   CLOB
)
create table Test_2 (a date,b date,c number,d number, e number)

INSERT INTO Tests_1
  (value)
VALUES
  ('{ 
"a":"01/01/2015",
"b":"31/12/2015",
"c":"11111111111",
"d":"1111111111",
"e":"1234567890"
}');

【问题讨论】:

  • 您的 Oracle 版本是什么? 12.1.0.2 有native JSON support
  • 我的Oracle版本是11.0.4.1774
  • 11.0.4.1774 不是 Oracle RDBMS 版本号。看起来像您正在使用的 PL/SQL Developer 版本,此处不相关。
  • @AlexPoole 当我发布我的答案时,我什至懒得看 OP 贴得足够近的 Oracle 版本 ;-)。当然没有 11.0.4 版本的 RDBMS。

标签: json oracle plsql


【解决方案1】:

11.0.4版本(当然没有11.0.4版本)你至少有两种选择(除了自己写解析器)

根据您使用的 RDBMS 版本,这里有几个选项:

第一个:对于 Oracle 11.1.0.7 及更高版本,安装 Apex 5 并使用 apex_json 包:

-- here I have 12.1.0.1 version with version 5 of apex installed

column ora_version format a21;
column apex_version format a21;


select (select version from v$instance) as ora_version
     , (select version_no from apex_release) as apex_version
  from dual;

--drop table test_2;
/* our test table */  
create table test_2(
  c_a date,
  c_b date,
  c_c number,
  c_d number,
  c_e number
);

select * from test_2;

declare
  l_json_doc clob; 
begin
  dbms_output.put_line('Parsing json...');
  l_json_doc := '{"a":"01/01/2015","b":"31/12/2015",
                  "c":"11111111111","d":"1111111111",
                  "e":"1234567890"}';
  apex_json.parse(l_json_doc);
  insert into test_2(c_a, c_b, c_c, c_d, c_e)
    values(apex_json.get_date(p_path=>'a', p_format=>'dd/mm/yyyy'),
           apex_json.get_date(p_path=>'b', p_format=>'dd/mm/yyyy'),
           to_number(apex_json.get_varchar2(p_path=>'c')),
           to_number(apex_json.get_varchar2(p_path=>'d')),
           to_number(apex_json.get_varchar2(p_path=>'e')));
  commit;
  dbms_output.put_line('Done!');
end;
/

column c_c format 99999999999;
select to_char(c_a, 'dd/mm/yyyy') as c_a
     , to_char(c_b, 'dd/mm/yyyy') as c_b
     , c_c
     , c_d
     , c_e 
  from test_2;

结果:

ORA_VERSION           APEX_VERSION         
--------------------- ---------------------
12.1.0.1.0            5.0.2.00.07          

1 row selected.

Table created.

no rows selected.

Parsing json...
Done!
PL/SQL procedure successfully completed.


C_A        C_B                 C_C        C_D        C_E
---------- ---------- ------------ ---------- ----------
01/01/2015 31/12/2015  11111111111 1111111111 1234567890

1 row selected.

第二个:使用开源PL/JSON。以前没用过,所以借这个机会试试。和apex_json很像。

declare
  l_json      json;  --json object
  l_json_doc  clob;
begin
  dbms_output.put_line('Parsing json...');

  -- parsing is done upon object instantiation

  l_json_doc := '{"a":"01/01/2015","b":"31/12/2015",
                  "c":"11111111111","d":"1111111111",
                  "e":"1234567890"}';
  l_json := json(l_json_doc);


  insert into test_2(c_a, c_b, c_c, c_d, c_e)
    values(to_date(l_json.get('a').get_string, 'dd-mm-yyyy'),
           to_date(l_json.get('b').get_string, 'dd-mm-yyyy'),
           to_number(l_json.get('c').get_string),
           to_number(l_json.get('d').get_string),
           to_number(l_json.get('e').get_string));
  commit;
  dbms_output.put_line('Done!');
end;

column c_c format 99999999999;
select to_char(c_a, 'dd/mm/yyyy') as c_a
     , to_char(c_b, 'dd/mm/yyyy') as c_b
     , c_c
     , c_d
     , c_e 
  from test_2;

结果:

C_A        C_B                 C_C        C_D        C_E
---------- ---------- ------------ ---------- ----------
01/01/2015 31/12/2015  11111111111 1111111111 1234567890
01/01/2015 31/12/2015  11111111111 1111111111 1234567890

2 rows selected.

在 12.1.0.2 版本中引入 json_table() 使得 JSON 解析更简单(只是为了演示):

insert into test_2
  select to_date(c_a, 'dd-mm-yyyy')
       , to_date(c_b, 'dd-mm-yyyy')
       , c_c
       , c_d
       , c_e
    from json_table('{"a":"01/01/2015",
                      "b":"31/12/2015",
                      "c":"11111111111",
                      "d":"1111111111",
                      "e":"1234567890"}'
                    , '$' 
                    columns ( 
                       c_a varchar2(21) path '$.a',
                       c_b varchar2(21) path '$.b',
                       c_c varchar2(21) path '$.c',
                       c_d varchar2(21) path '$.d',
                       c_e varchar2(21) path '$.e'
                    )) ;

结果:

 select *
   from test_2;


C_A         C_B                C_C        C_D        C_E
----------- ----------- ---------- ---------- ----------
1/1/2015    12/31/2015  1111111111 1111111111 1234567890

【讨论】:

    【解决方案2】:

    Oracle 12c 支持 JSON

    如果你有一个现有的表,只需这样做

    ALTER TABLE table1 ADD CONSTRAINT constraint_name CHECK (your_column IS json);
    SELECT t.your_column.id FROM table1 t;
    

    请注意,由于某种原因,t 昵称是必需的

    或完整示例:

    CREATE TABLE json_documents (
      id    RAW(16) NOT NULL,
      data  CLOB,
      CONSTRAINT json_documents_pk PRIMARY KEY (id),
      CONSTRAINT json_documents_json_chk CHECK (data IS JSON)
    );
    
    INSERT INTO json_documents (id, data)
    VALUES (SYS_GUID(),
            '{
              "FirstName"      : "John",
              "LastName"       : "Doe",
              "Job"            : "Clerk",
              "Address"        : {
                                  "Street"   : "99 My Street",
                                  "City"     : "My City",
                                  "Country"  : "UK",
                                  "Postcode" : "A12 34B"
                                 },
              "ContactDetails" : {
                                  "Email"    : "john.doe@example.com",
                                  "Phone"    : "44 123 123456",
                                  "Twitter"  : "@johndoe"
                                 },
              "DateOfBirth"    : "01-JAN-1980",
              "Active"         : true
             }');
    
    
    
    
    SELECT a.data.FirstName,
           a.data.LastName,
           a.data.Address.Postcode AS Postcode,
           a.data.ContactDetails.Email AS Email
    FROM   json_documents a;
    
    
    FIRSTNAME       LASTNAME        POSTCODE   EMAIL
    --------------- --------------- ---------- -------------------------
    Jayne           Doe             A12 34B    jayne.doe@example.com
    John            Doe             A12 34B    john.doe@example.com
    
    2 rows selected.
    

    更多信息

    【讨论】:

    • 在 Oracle 18c 中引入TREAT AS JSON 会更加简单:)
    • SELECT JSON_VALUE(tab.col_json_data, '$.fieldName') AS fieldName FROM json_data_table tab;
    【解决方案3】:

    由于您指定不想使用任何 JSON 库,如果格式是固定的,您可以将其强制转换为可以解析为 XML 的内容,首先剥离花括号,用等号替换冒号,然后删除每个名称/值对的第一部分的双引号:

    select regexp_replace(regexp_replace(value, '(^{|}$)'),
      '^"(.*)":(".*")($|,)', '\1=\2', 1, 0, 'm')
    from tests_1;
    
    REGEXP_REPLACE(REGEXP_REPLACE(VALUE,'(^{|}$)'),'^"(.*)":(".*")($|,)','\1=\2',1,0
    --------------------------------------------------------------------------------
    
    a="01/01/2015"
    b="31/12/2015"
    c="11111111111"
    d="1111111111"
    e="1234567890"
    

    您可以将其用作虚拟 XML 节点的属性;将其转换为 XMLType,您可以使用 XMLTable 来提取属性:

    select x.a, x.b, x.c, x.d, x.e
    from tests_1 t
    cross join xmltable('/tmp'
      passing xmltype('<tmp ' ||regexp_replace(regexp_replace(value, '(^{|}$)'),
        '^"(.*)":(".*")($|,)', '\1=\2', 1, 0, 'm') || ' />')
      columns a varchar2(10) path '@a',
        b varchar2(10) path '@b',
        c number path '@c',
        d number path '@d',
        e number path '@e'
    ) x;
    
    A          B                      C             D             E
    ---------- ---------- ------------- ------------- -------------
    01/01/2015 31/12/2015   11111111111    1111111111    1234567890
    

    然后您可以在插入期间将字符串转换为日期:

    insert into test_2 (a, b, c, d, e)
    select to_date(x.a, 'DD/MM/YYYY'), to_date(x.b, 'DD/MM/YYYY'), x.c, x.d, x.e
    from tests_1 t
    cross join xmltable('/tmp'
      passing xmltype('<tmp ' || regexp_replace(regexp_replace(value, '(^{|}$)'),
        '^"(.*)":(".*")($|,)', '\1=\2', 1, 0, 'm') || ' />')
      columns a varchar2(10) path '@a',
        b varchar2(10) path '@b',
        c number path '@c',
        d number path '@d',
        e number path '@e'
    ) x;
    
    select * from test_2;
    
    A          B                      C             D             E
    ---------- ---------- ------------- ------------- -------------
    2015-01-01 2015-12-31   11111111111    1111111111    1234567890
    

    这将处理一些不存在的名称/值对,如果发生这种情况,您将得到空值。

    如果所有对将始终存在,您可以标记字符串并提取相关部分:

    select to_date(regexp_substr(value, '[^"]+', 1, 4), 'DD/MM/YYYY') as a,
      to_date(regexp_substr(value, '[^"]+', 1, 8), 'DD/MM/YYYY') as b,
      to_number(regexp_substr(value, '[^"]+', 1, 12)) as c,
      to_number(regexp_substr(value, '[^"]+', 1, 16)) as d,
      to_number(regexp_substr(value, '[^"]+', 1, 20)) as e
    from tests_1;
    
    A          B                      C             D             E
    ---------- ---------- ------------- ------------- -------------
    2015-01-01 2015-12-31   11111111111    1111111111    1234567890
    

    【讨论】:

      【解决方案4】:

      来自Oracle 18c,您可以使用TREAT AS JSON operator

      JSON 的 SQL 增强功能

      • 您可以使用 TREAT (... AS JSON) 指定给定的 SQL 表达式返回 JSON 数据。

      • TREAT (... AS JSON) 允许您指定将来自给定 SQL 表达式的返回值视为 JSON 数据。此类表达式可以包括 PL/SQL 函数调用和 SQL WITH 子句指定的列。新的数据指南视图可以轻松访问 JSON 字段的路径和类型信息,这些信息是为索引支持的数据指南记录的。在 LOB 实例中返回生成和查询的 JSON 数据拓宽了关系数据的使用范围。


      该运算符提供了一种方法来通知数据库 VARCHAR2、BLOB、CLOB 的内容应被视为包含 JSON。 这启用了许多有用的功能,包括对没有“IS JSON”约束的数据库对象使用“简化语法”的能力。

      在你的例子中:

      create table Test_1(val CLOB);
      create table Test_2(a date,b date,c number,d number, e number);
      
      INSERT INTO Test_1(val)
      VALUES('{ 
      "a":"01/01/2015",
      "b":"31/12/2015",
      "c":"11111111111",
      "d":"1111111111",
      "e":"1234567890"
      }');
      
      INSERT INTO Test_2(a,b,c,d,e)
      SELECT sub.val_as_json.a,
             sub.val_as_json.b,
             sub.val_as_json.c,
             sub.val_as_json.d,
             sub.val_as_json.e
      FROM (SELECT TREAT(val as JSON) val_as_json
            FROM Test_1) sub;
      COMMIT;
      

      db<>fiddle demo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-25
        • 1970-01-01
        • 2017-01-13
        • 2011-05-20
        • 2016-12-05
        • 2014-09-10
        • 1970-01-01
        相关资源
        最近更新 更多