Skip to content

Commit 056d377

Browse files
committed
- Added api get_default_options, get_options and set_options to json_utilspackage to configure (Pretty, AsciiOutput and EscapeSolitus) the output.
- Added support for formatted (pretty) json output with the option `Pretty`. - Fixed another error when converting `\f` and `\r`.
1 parent 2489cc9 commit 056d377

File tree

9 files changed

+296
-62
lines changed

9 files changed

+296
-62
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,28 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1313
### Fixed
1414

1515

16+
## [0.6.0] - 2020-03-09
17+
18+
### Changed
19+
- Added api `get_default_options`, `get_options` and `set_options` to `json_utils`package to configure (`Pretty`, `AsciiOutput` and `EscapeSolitus`) the output.
20+
- Added support for formatted (pretty) json output with the option `Pretty`.
21+
22+
23+
### Fixed
24+
- Fixed another error when converting `\f` and `\r`.
25+
26+
1627
## [0.5.0] - 2020-03-08
1728

1829
### Changed
1930
- No longer install debug module by default.
20-
- Removed package json_cost and json_clob.
31+
- Removed package `json_cost` and `json_clob`.
2132
- Removed the obsolete performance tests.
2233

2334

2435
### Fixed
2536
- Required naming changes to run on current Oracle versions that now also have json support.
26-
- Fixed an error when converting \f and \r.
37+
- Fixed an error when converting `\f` and `\r`.
2738

2839

2940
## [0.4.1] - 2016-12-22

json_utils.pkb

Lines changed: 160 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,56 @@ CREATE OR REPLACE
22
PACKAGE BODY json_utils
33
IS
44

5+
outputOptions OutputOptionsType;
6+
7+
NEW_LINE CONSTANT VARCHAR2(2) := '
8+
';
9+
INDENTATION_CHARACTER CONSTANT VARCHAR2(1) := ' ';
10+
511
PROCEDURE copySubNodes(theTarget IN OUT NOCOPY jsonNodes, theFirstID IN OUT NOCOPY BINARY_INTEGER, theParentID IN BINARY_INTEGER, theSource IN jsonNodes, theFirstSourceID IN BINARY_INTEGER);
612
FUNCTION boolean_to_json(theBoolean IN NUMBER) RETURN VARCHAR2;
713
FUNCTION number_to_json(theNumber IN NUMBER) RETURN VARCHAR2;
14+
FUNCTION get_gap(theIndentation IN INTEGER) RETURN VARCHAR2;
815
PROCEDURE escapeLOB(theInputLob IN CLOB, theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theAsciiOutput IN BOOLEAN DEFAULT TRUE, theEscapeSolitus IN BOOLEAN DEFAULT FALSE);
916

17+
----------------------------------------------------------
18+
-- get_default_options
19+
--
20+
FUNCTION get_default_options RETURN outputOptionsType
21+
IS
22+
aOptions outputOptionsType;
23+
BEGIN
24+
RETURN aOptions;
25+
END get_default_options;
26+
27+
----------------------------------------------------------
28+
-- get_options
29+
--
30+
FUNCTION get_options RETURN outputOptionsType
31+
IS
32+
BEGIN
33+
RETURN outputOptions;
34+
END get_options;
35+
36+
----------------------------------------------------------
37+
-- set_options
38+
--
39+
PROCEDURE set_options(theOptions IN outputOptionsType)
40+
IS
41+
BEGIN
42+
outputOptions := theOptions;
43+
END set_options;
44+
45+
----------------------------------------------------------
46+
-- get_default_output_options
47+
--
48+
FUNCTION get_default_output_options RETURN OutputOptionsType
49+
IS
50+
aOptions OutputOptionsType;
51+
BEGIN
52+
RETURN aOptions;
53+
END get_default_output_options;
54+
1055
----------------------------------------------------------
1156
-- add_string
1257
--
@@ -483,17 +528,22 @@ END removeNode;
483528
----------------------------------------------------------
484529
-- value_to_clob
485530
--
486-
PROCEDURE value_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER)
531+
PROCEDURE value_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theIndentation IN OUT INTEGER)
487532
IS
488-
aNode jsonNode := theNodes(theNodeID);
533+
PRAGMA INLINE (get_gap, 'YES');
534+
GAP CONSTANT VARCHAR2(32767) := get_gap(theIndentation);
489535

490-
aName VARCHAR2(32767);
491-
aCLOB CLOB;
536+
aNode jsonNode := theNodes(theNodeID);
537+
aName VARCHAR2(32767);
538+
aCLOB CLOB;
492539
BEGIN
493540
-- Add the property name
494541
IF (aNode.nam IS NOT NULL) THEN
495542
PRAGMA INLINE (escape, 'YES');
496-
aName := '"' || escape(aNode.nam) || '":';
543+
aName := GAP || '"' || escape(theString=>aNode.nam, theAsciiOutput=>outputOptions.AsciiOutput, theEscapeSolitus=>outputOptions.EscapeSolitus) || '":';
544+
IF (outputOptions.Pretty) THEN
545+
aName := aName || ' ';
546+
END IF;
497547

498548
PRAGMA INLINE (add_string, 'YES');
499549
json_utils.add_string(theLobBuf, theStrBuf, aName);
@@ -504,36 +554,36 @@ BEGIN
504554

505555
WHEN json_utils.NODE_TYPE_NULL THEN
506556
PRAGMA INLINE (add_string, 'YES');
507-
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>'null');
557+
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>GAP || 'null');
508558

509559
WHEN json_utils.NODE_TYPE_STRING THEN
510560
PRAGMA INLINE (escape, 'YES');
511-
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>'"' || escape(aNode.str) || '"');
561+
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>GAP || '"' || escape(theString=>aNode.str, theAsciiOutput=>outputOptions.AsciiOutput, theEscapeSolitus=>outputOptions.EscapeSolitus) || '"');
512562

513563
WHEN json_utils.NODE_TYPE_LOB THEN
514564
PRAGMA INLINE (add_string, 'YES');
515-
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>'"');
565+
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>GAP || '"');
516566
PRAGMA INLINE (escapeLOB, 'YES');
517-
escapeLOB(theInputLob=>aNode.lob, theLobBuf=>theLobBuf, theStrBuf=>theStrBuf);
567+
escapeLOB(theInputLob=>aNode.lob, theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theAsciiOutput=>outputOptions.AsciiOutput, theEscapeSolitus=>outputOptions.EscapeSolitus);
518568
PRAGMA INLINE (add_string, 'YES');
519569
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>'"');
520570

521571
WHEN json_utils.NODE_TYPE_NUMBER THEN
522572
PRAGMA INLINE (number_to_json, 'YES');
523-
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>number_to_json(aNode.num));
573+
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>GAP || number_to_json(aNode.num));
524574

525575
WHEN json_utils.NODE_TYPE_DATE THEN
526-
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>'"' || TO_CHAR(aNode.dat, 'FXYYYY-MM-DD"T"HH24:MI:SS') || '"');
576+
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>GAP || '"' || TO_CHAR(aNode.dat, 'FXYYYY-MM-DD"T"HH24:MI:SS') || '"');
527577

528578
WHEN json_utils.NODE_TYPE_BOOLEAN THEN
529579
PRAGMA INLINE (boolean_to_json, 'YES');
530-
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>boolean_to_json(aNode.num));
580+
json_utils.add_string(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theValue=>GAP || boolean_to_json(aNode.num));
531581

532582
WHEN json_utils.NODE_TYPE_OBJECT THEN
533-
json_utils.object_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>theNodes(theNodeID).sub, theFlushToLOB=>FALSE);
583+
json_utils.object_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>theNodes(theNodeID).sub, theIndentation=>theIndentation, theFlushToLOB=>FALSE);
534584

535585
WHEN json_utils.NODE_TYPE_ARRAY THEN
536-
json_utils.array_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>theNodes(theNodeID).sub, theFlushToLOB=>FALSE);
586+
json_utils.array_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>theNodes(theNodeID).sub, theIndentation=>theIndentation, theFlushToLOB=>FALSE);
537587

538588
ELSE
539589
raise_application_error(-20100, 'Invalid node type: '||aNode.typ, TRUE);
@@ -543,29 +593,58 @@ END value_to_clob;
543593
----------------------------------------------------------
544594
-- object_to_clob
545595
--
546-
PROCEDURE object_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theFlushToLOB IN BOOLEAN DEFAULT TRUE)
596+
PROCEDURE object_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theIndentation IN OUT INTEGER, theFlushToLOB IN BOOLEAN DEFAULT TRUE)
547597
IS
548-
i BINARY_INTEGER := theNodeID;
598+
PRAGMA INLINE (get_gap, 'YES');
599+
GAP CONSTANT VARCHAR2(32767) := get_gap(theIndentation);
600+
601+
aBracket VARCHAR2(32767);
602+
aDelimiter VARCHAR2(32767);
603+
aNodeID BINARY_INTEGER := theNodeID;
549604
BEGIN
550-
-- Serialize the object
605+
-- open bracket {
606+
IF (outputOptions.Pretty) THEN
607+
aBracket := '{' || NEW_LINE;
608+
ELSE
609+
aBracket := '{';
610+
END IF;
551611
PRAGMA INLINE (add_string, 'YES');
552-
json_utils.add_string(theLobBuf, theStrBuf, '{');
553-
WHILE (i IS NOT NULL) LOOP
612+
json_utils.add_string(theLobBuf, theStrBuf, aBracket);
613+
614+
-- compute the delimiter
615+
IF (outputOptions.Pretty) THEN
616+
aDelimiter := ',' || NEW_LINE;
617+
ELSE
618+
aDelimiter := ',';
619+
END IF;
620+
621+
-- process all properties in the object
622+
WHILE (aNodeID IS NOT NULL) LOOP
554623
-- Add separator from last property if we are not the first one
555-
IF (i != theNodeID) THEN
624+
IF (aNodeID != theNodeID) THEN
556625
PRAGMA INLINE (add_string, 'YES');
557-
json_utils.add_string(theLobBuf, theStrBuf, ',');
626+
json_utils.add_string(theLobBuf, theStrBuf, aDelimiter);
558627
END IF;
559628

560629
-- Add the property pair
630+
theIndentation := theIndentation + 1;
561631
--PRAGMA INLINE (value_to_clob, 'YES');
562-
json_utils.value_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>i);
632+
json_utils.value_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>aNodeID, theIndentation=>theIndentation);
633+
theIndentation := theIndentation -+ 1;
563634

564-
i := theNodes(i).nex;
635+
aNodeID := theNodes(aNodeID).nex;
565636
END LOOP;
637+
638+
-- close bracket }
639+
IF (outputOptions.Pretty) THEN
640+
aBracket := NEW_LINE || GAP || '}';
641+
ELSE
642+
aBracket := GAP || '}';
643+
END IF;
566644
PRAGMA INLINE (add_string, 'YES');
567-
json_utils.add_string(theLobBuf, theStrBuf, '}');
645+
json_utils.add_string(theLobBuf, theStrBuf, aBracket);
568646

647+
-- flush
569648
IF (theFlushToLOB) THEN
570649
json_utils.flush_clob(theLobBuf, theStrBuf);
571650
END IF;
@@ -574,29 +653,58 @@ END object_to_clob;
574653
----------------------------------------------------------
575654
-- array_to_clob
576655
--
577-
PROCEDURE array_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theFlushToLOB IN BOOLEAN DEFAULT TRUE)
656+
PROCEDURE array_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theIndentation IN OUT INTEGER, theFlushToLOB IN BOOLEAN DEFAULT TRUE)
578657
IS
579-
i BINARY_INTEGER := theNodeID;
658+
PRAGMA INLINE (get_gap, 'YES');
659+
GAP CONSTANT VARCHAR2(32767) := get_gap(theIndentation);
660+
661+
aBracket VARCHAR2(32767);
662+
aDelimiter VARCHAR2(32767);
663+
aNodeID BINARY_INTEGER := theNodeID;
580664
BEGIN
581-
-- Serialize the object
665+
-- open bracket {
666+
IF (outputOptions.Pretty) THEN
667+
aBracket := '[' || NEW_LINE;
668+
ELSE
669+
aBracket := '[';
670+
END IF;
582671
PRAGMA INLINE (add_string, 'YES');
583-
json_utils.add_string(theLobBuf, theStrBuf, '[');
584-
WHILE (i IS NOT NULL) LOOP
672+
json_utils.add_string(theLobBuf, theStrBuf, aBracket);
673+
674+
-- compute the delimiter
675+
IF (outputOptions.Pretty) THEN
676+
aDelimiter := ',' || NEW_LINE;
677+
ELSE
678+
aDelimiter := ',';
679+
END IF;
680+
681+
-- process all properties in the object
682+
WHILE (aNodeID IS NOT NULL) LOOP
585683
-- Add separator from last array entry if we are not the first one
586-
IF (i != theNodeID) THEN
684+
IF (aNodeID != theNodeID) THEN
587685
PRAGMA INLINE (add_string, 'YES');
588-
json_utils.add_string(theLobBuf, theStrBuf, ',');
686+
json_utils.add_string(theLobBuf, theStrBuf, aDelimiter);
589687
END IF;
590688

591689
-- Add the property pair
690+
theIndentation := theIndentation + 1;
592691
PRAGMA INLINE (value_to_clob, 'YES');
593-
json_utils.value_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>i);
692+
json_utils.value_to_clob(theLobBuf=>theLobBuf, theStrBuf=>theStrBuf, theNodes=>theNodes, theNodeID=>aNodeID, theIndentation=>theIndentation);
693+
theIndentation := theIndentation - 1;
594694

595-
i := theNodes(i).nex;
695+
aNodeID := theNodes(aNodeID).nex;
596696
END LOOP;
697+
698+
-- close bracket }
699+
IF (outputOptions.Pretty) THEN
700+
aBracket := NEW_LINE || GAP || ']';
701+
ELSE
702+
aBracket := GAP || ']';
703+
END IF;
597704
PRAGMA INLINE (add_string, 'YES');
598-
json_utils.add_string(theLobBuf, theStrBuf, ']');
705+
json_utils.add_string(theLobBuf, theStrBuf, aBracket);
599706

707+
-- flush
600708
IF (theFlushToLOB) THEN
601709
json_utils.flush_clob(theLobBuf, theStrBuf);
602710
END IF;
@@ -662,6 +770,19 @@ BEGIN
662770
END IF;
663771
END htp_output_close;
664772

773+
----------------------------------------------------------
774+
-- get_gap (private)
775+
--
776+
FUNCTION get_gap(theIndentation IN INTEGER) RETURN VARCHAR2
777+
IS
778+
BEGIN
779+
IF (outputOptions.Pretty) THEN
780+
RETURN RPAD(INDENTATION_CHARACTER, theIndentation, INDENTATION_CHARACTER);
781+
ELSE
782+
RETURN '';
783+
END IF;
784+
END get_gap;
785+
665786
----------------------------------------------------------
666787
-- copySubNodes (private)
667788
--
@@ -817,11 +938,11 @@ BEGIN
817938
END IF;
818939

819940
CASE buf
820-
WHEN CHR( 8) THEN buf := '\b'; -- backspace b = U+0008
821-
WHEN CHR( 9) THEN buf := '\t'; -- tabulator t = U+0009
822-
WHEN CHR(10) THEN buf := '\n'; -- newline n = U+000A
823-
WHEN CHR(13) THEN buf := '\f'; -- formfeed f = U+000C
824-
WHEN CHR(14) THEN buf := '\r'; -- carret r = U+000D
941+
WHEN CHR(8) THEN buf := '\b'; -- backspace b = U+0008 = chr(8)
942+
WHEN CHR(9) THEN buf := '\t'; -- tabulator t = U+0009 = chr(9)
943+
WHEN CHR(10) THEN buf := '\n'; -- newline n = U+000A = chr(10)
944+
WHEN CHR(12) THEN buf := '\f'; -- formfeed f = U+000C = chr(12)
945+
WHEN CHR(13) THEN buf := '\r'; -- carret r = U+000D = chr(13)
825946
WHEN CHR(34) THEN buf := '\"';
826947
WHEN CHR(47) THEN -- slash
827948
IF (theEscapeSolitus) THEN

json_utils.pks

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ CREATE OR REPLACE
22
PACKAGE json_utils
33
IS
44

5+
-- output options
6+
TYPE outputOptionsType IS RECORD
7+
(
8+
Pretty BOOLEAN DEFAULT FALSE,
9+
AsciiOutput BOOLEAN DEFAULT TRUE,
10+
EscapeSolitus BOOLEAN DEFAULT FALSE
11+
);
12+
13+
-- node types
514
NODE_TYPE_NULL CONSTANT VARCHAR2(1) := '0';
615
NODE_TYPE_STRING CONSTANT VARCHAR2(1) := 'S';
716
NODE_TYPE_LOB CONSTANT VARCHAR2(1) := 'L';
@@ -11,6 +20,21 @@ NODE_TYPE_BOOLEAN CONSTANT VARCHAR2(1) := 'B';
1120
NODE_TYPE_OBJECT CONSTANT VARCHAR2(1) := 'O';
1221
NODE_TYPE_ARRAY CONSTANT VARCHAR2(1) := 'A';
1322

23+
----------------------------------------------------------
24+
-- get_default_options
25+
--
26+
FUNCTION get_default_options RETURN outputOptionsType;
27+
28+
----------------------------------------------------------
29+
-- get_options
30+
--
31+
FUNCTION get_options RETURN outputOptionsType;
32+
33+
----------------------------------------------------------
34+
-- set_options
35+
--
36+
PROCEDURE set_options(theOptions IN outputOptionsType);
37+
1438
----------------------------------------------------------
1539
-- add_string
1640
--
@@ -81,17 +105,17 @@ FUNCTION removeNode(theNodes IN OUT NOCOPY jsonNodes, theNodeID IN BINARY_INTEGE
81105
----------------------------------------------------------
82106
-- convert a node value to a JSON string
83107
--
84-
PROCEDURE value_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER);
108+
PROCEDURE value_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theIndentation IN OUT INTEGER);
85109

86110
----------------------------------------------------------
87111
-- convert an object to a JSON string
88112
--
89-
PROCEDURE object_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theFlushToLOB IN BOOLEAN DEFAULT TRUE);
113+
PROCEDURE object_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theIndentation IN OUT INTEGER, theFlushToLOB IN BOOLEAN DEFAULT TRUE);
90114

91115
----------------------------------------------------------
92116
-- convert an array to a JSON string
93117
--
94-
PROCEDURE array_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theFlushToLOB IN BOOLEAN DEFAULT TRUE);
118+
PROCEDURE array_to_clob(theLobBuf IN OUT NOCOPY CLOB, theStrBuf IN OUT NOCOPY VARCHAR2, theNodes IN jsonNodes, theNodeID IN NUMBER, theIndentation IN OUT INTEGER, theFlushToLOB IN BOOLEAN DEFAULT TRUE);
95119

96120
----------------------------------------------------------
97121
-- copy output to the browser using htp.prn

0 commit comments

Comments
 (0)