1.前提条件,安装json的包
2.创建JSON_UTIL_PKG
CREATE OR REPLACE PACKAGE JSON_UTIL_PKG AUTHID CURRENT_USER AS
/*
Purpose: JSON utilities for PL/SQL
see http://ora-00001.blogspot.com/
Remarks:
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
JKR 01.05.2010 Edited to fit in PL/JSON
JKR 19.01.2011 Newest stylesheet + bugfix handling
*/
-- generate JSON from REF Cursor
FUNCTION REF_CURSOR_TO_JSON(P_REF_CURSOR IN SYS_REFCURSOR,
P_MAX_ROWS IN NUMBER := NULL,
P_SKIP_ROWS IN NUMBER := NULL) RETURN JSON_LIST;
-- generate JSON from SQL statement
FUNCTION SQL_TO_JSON(P_SQL IN VARCHAR2, P_MAX_ROWS IN NUMBER := NULL, P_SKIP_ROWS IN NUMBER := NULL) RETURN JSON_LIST;
END JSON_UTIL_PKG;
create or replace package body json_util_pkg
as
scanner_exception exception;
pragma exception_init(scanner_exception, -20100);
parser_exception exception;
pragma exception_init(parser_exception, -20101);
/*
Purpose: JSON utilities for PL/SQL
Remarks:
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
*/
g_json_null_object constant varchar2(20) := '{ }';
function get_xml_to_json_stylesheet return varchar2
as
begin
/*
Purpose: return XSLT stylesheet for XML to JSON transformation
Remarks: see http://code.google.com/p/xml2json-xslt/
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
MBR 30.01.2010 Added fix for nulls
*/
return q'^<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="no" omit-xml-declaration="yes" method="text" encoding="UTF-8" media-type="text/x-json"/>
<xsl:strip-space elements="*"/>
<!--contant-->
<xsl:variable name="d">0123456789</xsl:variable>
<!-- ignore document text -->
<xsl:template match="text()[preceding-sibling::node() or following-sibling::node()]"/>
<!-- string -->
<xsl:template match="text()">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="."/>
</xsl:call-template>
</xsl:template>
<!-- Main template for escaping strings; used by above template and for object-properties
Responsibilities: placed quotes around string, and chain up to next filter, escape-bs-string -->
<xsl:template name="escape-string">
<xsl:param name="s"/>
<xsl:text>"</xsl:text>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:template>
<!-- Escape the backslash (\) before everything else. -->
<xsl:template name="escape-bs-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'\')">
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="concat(substring-before($s,'\'),'\\')"/>
</xsl:call-template>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="substring-after($s,'\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Escape the double quote ("). -->
<xsl:template name="escape-quot-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'"')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'"'),'\"')"/>
</xsl:call-template>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="substring-after($s,'"')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="encode-string">
<xsl:param name="s"/>
<xsl:choose>
<!-- tab -->
<xsl:when test="contains($s,' ')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,' '),'\t',substring-after($s,' '))"/>
</xsl:call-template>
</xsl:when>
<!-- line feed -->
<xsl:when test="contains($s,'
')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'
'),'\n',substring-after($s,'
'))"/>
</xsl:call-template>
</xsl:when>
<!-- carriage return -->
<xsl:when test="contains($s,'
')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'
'),'\r',substring-after($s,'
'))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$s"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- number (no support for javascript mantissa) -->
<xsl:template match="text()[not(string(number())='NaN' or
(starts-with(.,'0' ) and . != '0' and
not(starts-with(.,'0.' ))) or
(starts-with(.,'-0' ) and . != '-0' and
not(starts-with(.,'-0.' )))
)]">
<xsl:value-of select="."/>
</xsl:template>
<!-- boolean, case-insensitive -->
<xsl:template match="text()[translate(.,'TRUE','true')='true']">true</xsl:template>
<xsl:template match="text()[translate(.,'FALSE','false')='false']">false</xsl:template>
<!-- object -->
<xsl:template match="*" name="base">
<xsl:if test="not(preceding-sibling::*)">{</xsl:if>
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<!-- check type of node -->
<xsl:choose>
<!-- null nodes -->
<xsl:when test="count(child::node())=0">null</xsl:when>
<!-- other nodes -->
<xsl:otherwise>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<!-- end of type check -->
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">}</xsl:if>
</xsl:template>
<!-- array -->
<xsl:template match="*[count(../*[name(../*)=name(.)])=count(../*) and count(../*)>1]">
<xsl:if test="not(preceding-sibling::*)">[</xsl:if>
<xsl:choose>
<xsl:when test="not(child::node())">
<xsl:text>null</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">]</xsl:if>
</xsl:template>
<!-- convert root element to an anonymous container -->
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>^';
end get_xml_to_json_stylesheet;
function ref_cursor_to_json (p_ref_cursor in sys_refcursor,
p_max_rows in number := null,
p_skip_rows in number := null) return json_list
as
l_ctx dbms_xmlgen.ctxhandle;
l_num_rows pls_integer;
l_xml xmltype;
l_json xmltype;
l_returnvalue clob;
begin
/*
Purpose: generate JSON from REF Cursor
Remarks:
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
JKR 01.05.2010 Edited to fit in PL/JSON
*/
l_ctx := dbms_xmlgen.newcontext (p_ref_cursor);
dbms_xmlgen.setnullhandling (l_ctx, dbms_xmlgen.empty_tag);
-- for pagination
if p_max_rows is not null then
dbms_xmlgen.setmaxrows (l_ctx, p_max_rows);
end if;
if p_skip_rows is not null then
dbms_xmlgen.setskiprows (l_ctx, p_skip_rows);
end if;
-- get the XML content
l_xml := dbms_xmlgen.getxmltype (l_ctx, dbms_xmlgen.none);
l_num_rows := dbms_xmlgen.getnumrowsprocessed (l_ctx);
dbms_xmlgen.closecontext (l_ctx);
close p_ref_cursor;
if l_num_rows > 0 then
-- perform the XSL transformation
l_json := l_xml.transform (xmltype(get_xml_to_json_stylesheet));
l_returnvalue := l_json.getclobval();
else
l_returnvalue := g_json_null_object;
end if;
l_returnvalue := dbms_xmlgen.convert (l_returnvalue, dbms_xmlgen.entity_decode);
if(l_num_rows = 0) then
return json_list();
else
if(l_num_rows = 1) then
declare ret json_list := json_list();
begin
ret.append(
json(
json(l_returnvalue).get('ROWSET')
).get('ROW')
);
return ret;
end;
else
return json_list(json(l_returnvalue).get('ROWSET'));
end if;
end if;
exception
when scanner_exception then
dbms_output.put('Scanner problem with the following input: ');
dbms_output.put_line(l_returnvalue);
raise;
when parser_exception then
dbms_output.put('Parser problem with the following input: ');
dbms_output.put_line(l_returnvalue);
raise;
when others then raise;
end ref_cursor_to_json;
function sql_to_json (p_sql in varchar2,
p_max_rows in number := null,
p_skip_rows in number := null) return json_list
as
v_cur sys_refcursor;
begin
open v_cur for p_sql;
return ref_cursor_to_json(v_cur, p_max_rows, p_skip_rows);
end sql_to_json;
end json_util_pkg;
3. 测试
DECLARE
V_REFER SYS_REFCURSOR;
V_JSON_LIST JSON_LIST;
BEGIN
OPEN V_REFER FOR
SELECT * FROM EMP;
V_JSON_LIST := JSON_UTIL_PKG.REF_CURSOR_TO_JSON(V_REFER);
V_JSON_LIST.PRINT;
END;
[{
"EMPNO" : 7369,
"ENAME" : "SMITH",
"JOB" : "CLERK",
"MGR" : 7902,
"HIREDATE" : "17-DEC-80",
"SAL" : 800,
"COMM" : null,
"DEPTNO" : 20
}, {
"EMPNO" : 7499,
"ENAME" : "ALLEN",
"JOB" : "SALESMAN",
"MGR" : 7698,
"HIREDATE" : "20-FEB-81",
"SAL" : 1600,
"COMM" : 300,
"DEPTNO" : 30
}, {
"EMPNO" : 7521,
"ENAME" : "WARD",
"JOB" : "SALESMAN",
"MGR" : 7698,
"HIREDATE" : "22-FEB-81",
"SAL" : 1250,
"COMM" : 500,
"DEPTNO" : 30
}, {
"EMPNO" : 7566,
"ENAME" : "JONES",
"JOB" : "MANAGER",
"MGR" : 7839,
"HIREDATE" : "02-APR-81",
"SAL" : 2975,
"COMM" : null,
"DEPTNO" : 20
}, {
"EMPNO" : 7654,
"ENAME" : "MARTIN",
"JOB" : "SALESMAN",
"MGR" : 7698,
"HIREDATE" : "28-SEP-81",
"SAL" : 1250,
"COMM" : 1400,
"DEPTNO" : 30
}, {
"EMPNO" : 7698,
"ENAME" : "BLAKE",
"JOB" : "MANAGER",
"MGR" : 7839,
"HIREDATE" : "01-MAY-81",
"SAL" : 2850,
"COMM" : null,
"DEPTNO" : 30
}, {
"EMPNO" : 7782,
"ENAME" : "CLARK",
"JOB" : "MANAGER",
"MGR" : 7839,
"HIREDATE" : "09-JUN-81",
"SAL" : 2450,
"COMM" : null,
"DEPTNO" : 10
}, {
"EMPNO" : 7788,
"ENAME" : "SCOTT",
"JOB" : "ANALYST",
"MGR" : 7566,
"HIREDATE" : "19-APR-87",
"SAL" : 3000,
"COMM" : null,
"DEPTNO" : 20
}, {
"EMPNO" : 7839,
"ENAME" : "KING",
"JOB" : "PRESIDENT",
"MGR" : null,
"HIREDATE" : "17-NOV-81",
"SAL" : 5000,
"COMM" : null,
"DEPTNO" : 10
}, {
"EMPNO" : 7844,
"ENAME" : "TURNER",
"JOB" : "SALESMAN",
"MGR" : 7698,
"HIREDATE" : "08-SEP-81",
"SAL" : 1500,
"COMM" : 0,
"DEPTNO" : 30
}, {
"EMPNO" : 7876,
"ENAME" : "ADAMS",
"JOB" : "CLERK",
"MGR" : 7788,
"HIREDATE" : "23-MAY-87",
"SAL" : 1100,
"COMM" : null,
"DEPTNO" : 20
}, {
"EMPNO" : 7900,
"ENAME" : "JAMES",
"JOB" : "CLERK",
"MGR" : 7698,
"HIREDATE" : "03-DEC-81",
"SAL" : 950,
"COMM" : null,
"DEPTNO" : 30
}, {
"EMPNO" : 7902,
"ENAME" : "FORD",
"JOB" : "ANALYST",
"MGR" : 7566,
"HIREDATE" : "03-DEC-81",
"SAL" : 3000,
"COMM" : null,
"DEPTNO" : 20
}, {
"EMPNO" : 7934,
"ENAME" : "MILLER",
"JOB" : "CLERK",
"MGR" : 7782,
"HIREDATE" : "23-JAN-82",
"SAL" : 1300,
"COMM" : null,
"DEPTNO" : 10
}]