1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-15 12:30:06 +00:00

fix: writing utf-8 to _webout on windows in a latin9 session causes problems with subsequent (regular) put statements. The workaround is to write to a different file and stream it back to _webout.

This commit is contained in:
munja
2022-06-26 22:09:54 +01:00
parent 6d0e34ba1d
commit 2372ff5f4f
6 changed files with 516 additions and 170 deletions

343
all.sas
View File

@@ -8575,7 +8575,12 @@ options
/** /**
@file mp_jsonout.sas @file mp_jsonout.sas
@brief Writes JSON in SASjs format to a fileref @brief Writes JSON in SASjs format to a fileref
@details PROC JSON is faster but will produce errs like the ones below if @details This macro can be used to OPEN a JSON stream and send one or more
tables as arrays of rows, where each row can be an object or a nested array.
There are two engines available - DATASTEP or PROCJSON.
PROC JSON is fast but will produce errs like the ones below if
special chars are encountered. special chars are encountered.
> (ERR)OR: Some code points did not transcode. > (ERR)OR: Some code points did not transcode.
@@ -8586,6 +8591,10 @@ options
If this happens, try running with ENGINE=DATASTEP. If this happens, try running with ENGINE=DATASTEP.
The DATASTEP engine is used to handle special SAS missing numerics, and
can also convert entire datasets to formatted values. Output JSON is always
in UTF-8.
Usage: Usage:
filename tmp temp; filename tmp temp;
@@ -8649,9 +8658,23 @@ options
run; run;
%end; %end;
%else %if (&action=ARR or &action=OBJ) %then %do; %else %if (&action=ARR or &action=OBJ) %then %do;
/* force variable names to always be uppercase in the JSON */
options validvarname=upcase; options validvarname=upcase;
data _null_; file &jref encoding='utf-8' mod; /* To avoid issues with _webout on EBI - such as encoding diffs and truncation
(https://support.sas.com/kb/49/325.html) we use temporary files */
filename _sjs1 temp lrecl=200 ;
data _null_; file _sjs1 encoding='utf-8';
put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":";
run;
/* now write to _webout 1 char at a time */
data _null_;
infile _sjs1 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjs1 clear;
/* grab col defs */ /* grab col defs */
proc contents noprint data=&ds proc contents noprint data=&ds
@@ -8708,10 +8731,20 @@ options
data &tempds;set &ds; data &tempds;set &ds;
%if &fmt=N %then format _numeric_ best32.;; %if &fmt=N %then format _numeric_ best32.;;
/* PRETTY is necessary to avoid line truncation in large files */ /* PRETTY is necessary to avoid line truncation in large files */
proc json out=&jref pretty filename _sjs2 temp lrecl=131068 encoding='utf-8';
proc json out=_sjs2 pretty
%if &action=ARR %then nokeys ; %if &action=ARR %then nokeys ;
;export &tempds / nosastags fmtnumeric; ;export &tempds / nosastags fmtnumeric;
run; run;
/* send back to webout */
data _null_;
infile _sjs2 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjs2 clear;
%end; %end;
%else %if &engine=DATASTEP %then %do; %else %if &engine=DATASTEP %then %do;
%datastep: %datastep:
@@ -8794,10 +8827,9 @@ options
%end; %end;
run; run;
/* write to temp loc to avoid _webout truncation filename _sjs3 temp lrecl=131068 ;
- https://support.sas.com/kb/49/325.html */ data _null_;
filename _sjs temp lrecl=131068 encoding='utf-8'; file _sjs3 encoding='utf-8';
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod ;
if _n_=1 then put "["; if _n_=1 then put "[";
set &tempds; set &tempds;
if _n_>1 then put "," @; put if _n_>1 then put "," @; put
@@ -8808,27 +8840,29 @@ options
"&&name&i"n /* name literal for reserved variable names */ "&&name&i"n /* name literal for reserved variable names */
%end; %end;
%if &action=ARR %then "]" ; %else "}" ; ; %if &action=ARR %then "]" ; %else "}" ; ;
/* now write the long strings to _webout 1 char at a time */
/* close out the table */
data _null_; data _null_;
infile _sjs lrecl=1 recfm=n; file _sjs3 mod encoding='utf-8';
put ']';
run;
data _null_;
infile _sjs3 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n; file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@; input sourcechar $char1. @@;
format sourcechar hex2.; format sourcechar hex2.;
put sourcechar char1. @@; put sourcechar char1. @@;
run; run;
/* close out the table */ filename _sjs3 clear;
data _null_;
file &jref mod;
put ']';
run;
filename _sjs clear;
%end; %end;
proc sql; proc sql;
drop table &colinfo, &tempds; drop table &colinfo, &tempds;
%if &showmeta=YES %then %do; %if &showmeta=YES %then %do;
data _null_; file &jref encoding='utf-8' mod; filename _sjs4 temp lrecl=131068 encoding='utf-8';
data _null_;
file _sjs4;
put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{";
do i=1 to &numcols; do i=1 to &numcols;
name=quote(trim(symget(cats('name',i)))); name=quote(trim(symget(cats('name',i))));
@@ -8842,6 +8876,15 @@ options
end; end;
put '}}'; put '}}';
run; run;
/* send back to webout */
data _null_;
infile _sjs4 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjs4 clear;
%end; %end;
%end; %end;
@@ -15026,9 +15069,23 @@ data _null_;
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' /* force variable names to always be uppercase in the JSON */ ';
put ' options validvarname=upcase; '; put ' options validvarname=upcase; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
put ' filename _sjs1 temp lrecl=200 ; ';
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjs1 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs1 clear; ';
put ' '; put ' ';
put ' /* grab col defs */ '; put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds '; put ' proc contents noprint data=&ds ';
@@ -15085,10 +15142,20 @@ data _null_;
put ' data &tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
put ' proc json out=_sjs2 pretty ';
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs2 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs2 clear; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
@@ -15171,10 +15238,9 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' data _null_; ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' file _sjs3 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; '; put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
@@ -15185,27 +15251,29 @@ data _null_;
put ' "&&name&i"n /* name literal for reserved variable names */ '; put ' "&&name&i"n /* name literal for reserved variable names */ ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' /* now write the long strings to _webout 1 char at a time */ '; put ' ';
put ' /* close out the table */ ';
put ' data _null_; '; put ' data _null_; ';
put ' infile _sjs lrecl=1 recfm=n; '; put ' file _sjs3 mod encoding=''utf-8''; ';
put ' put '']''; ';
put ' run; ';
put ' data _null_; ';
put ' infile _sjs3 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; '; put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; '; put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; '; put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; '; put ' put sourcechar char1. @@; ';
put ' run; '; put ' run; ';
put ' /* close out the table */ '; put ' filename _sjs3 clear; ';
put ' data _null_; ';
put ' file &jref mod; ';
put ' put '']''; ';
put ' run; ';
put ' filename _sjs clear; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop table &colinfo, &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; ';
put ' file _sjs4; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; '; put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); '; put ' name=quote(trim(symget(cats(''name'',i)))); ';
@@ -15219,6 +15287,15 @@ data _null_;
put ' end; '; put ' end; ';
put ' put ''}}''; '; put ' put ''}}''; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs4 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs4 clear; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';
@@ -15320,6 +15397,8 @@ data _null_;
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' /* To avoid issues with _webout on EBI we use a temporary file */ ';
put ' filename _sjsref temp lrecl=131068; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ '; put ' /* if debug mode, send back first 10 records of each work table also */ ';
put ' options obs=10; '; put ' options obs=10; ';
@@ -15333,11 +15412,11 @@ data _null_;
put ' i+1; '; put ' i+1; ';
put ' call symputx(cats(''wt'',i),name,''l''); '; put ' call symputx(cats(''wt'',i),name,''l''); ';
put ' call symputx(''wtcnt'',i,''l''); '; put ' call symputx(''wtcnt'',i,''l''); ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put ",""WORK"":{"; '; put ' put ",""WORK"":{"; ';
put ' %do i=1 %to &wtcnt; '; put ' %do i=1 %to &wtcnt; ';
put ' %let wt=&&wt&i; '; put ' %let wt=&&wt&i; ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' dsid=open("WORK.&wt",''is''); '; put ' dsid=open("WORK.&wt",''is''); ';
put ' nlobs=attrn(dsid,''NLOBS''); '; put ' nlobs=attrn(dsid,''NLOBS''); ';
put ' nvars=attrn(dsid,''NVARS''); '; put ' nvars=attrn(dsid,''NVARS''); ';
@@ -15346,16 +15425,16 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) '; put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=YES) ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; '; put ' put "}"; ';
put ' %end; '; put ' %end; ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; '; put ' put "}"; ';
put ' run; '; put ' run; ';
put ' %end; '; put ' %end; ';
put ' /* close off json */ '; put ' /* close off json */ ';
put ' data _null_;file &fref mod encoding=''utf-8''; '; put ' data _null_;file _sjsref mod encoding=''utf-8''; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); '; put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; '; put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; '; put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
@@ -15387,6 +15466,16 @@ data _null_;
put ' put ''>>weboutEND<<''; '; put ' put ''>>weboutEND<<''; ';
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjsref lrecl=1 recfm=n; ';
put ' file &fref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjsref clear; ';
put ' ';
put '%end; '; put '%end; ';
put ' '; put ' ';
put '%mend mm_webout; '; put '%mend mm_webout; ';
@@ -18757,23 +18846,23 @@ run;
@details This macro should be added to the start of each Stored Process, @details This macro should be added to the start of each Stored Process,
**immediately** followed by a call to: **immediately** followed by a call to:
%mm_webout(FETCH) %mm_webout(FETCH)
This will read all the input data and create same-named SAS datasets in the This will read all the input data and create same-named SAS datasets in the
WORK library. You can then insert your code, and send data back using the WORK library. You can then insert your code, and send data back using the
following syntax: following syntax:
data some datasets; * make some data ; data some datasets; * make some data ;
retain some columns; retain some columns;
run; run;
%mm_webout(OPEN) %mm_webout(OPEN)
%mm_webout(ARR,some) * Array format, fast, suitable for large tables ; %mm_webout(ARR,some) * Array format, fast, suitable for large tables ;
%mm_webout(OBJ,datasets) * Object format, easier to work with ; %mm_webout(OBJ,datasets) * Object format, easier to work with ;
Finally, wrap everything up send some helpful system variables too Finally, wrap everything up send some helpful system variables too
%mm_webout(CLOSE) %mm_webout(CLOSE)
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@@ -18788,6 +18877,10 @@ run;
object with the same name as the table but prefixed with a dollar sign - ie, object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@version 9.3 @version 9.3
@author Allan Bowe @author Allan Bowe
@@ -18864,6 +18957,8 @@ run;
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
/* To avoid issues with _webout on EBI we use a temporary file */
filename _sjsref temp lrecl=131068;
%if %str(&_debug) ge 131 %then %do; %if %str(&_debug) ge 131 %then %do;
/* if debug mode, send back first 10 records of each work table also */ /* if debug mode, send back first 10 records of each work table also */
options obs=10; options obs=10;
@@ -18877,11 +18972,11 @@ run;
i+1; i+1;
call symputx(cats('wt',i),name,'l'); call symputx(cats('wt',i),name,'l');
call symputx('wtcnt',i,'l'); call symputx('wtcnt',i,'l');
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
put ",""WORK"":{"; put ",""WORK"":{";
%do i=1 %to &wtcnt; %do i=1 %to &wtcnt;
%let wt=&&wt&i; %let wt=&&wt&i;
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
dsid=open("WORK.&wt",'is'); dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS'); nlobs=attrn(dsid,'NLOBS');
nvars=attrn(dsid,'NVARS'); nvars=attrn(dsid,'NVARS');
@@ -18890,16 +18985,16 @@ run;
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=YES)
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
put "}"; put "}";
%end; %end;
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
put "}"; put "}";
run; run;
%end; %end;
/* close off json */ /* close off json */
data _null_;file &fref mod encoding='utf-8'; data _null_;file _sjsref mod encoding='utf-8';
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ",""SYSUSERID"" : ""&sysuserid"" "; put ",""SYSUSERID"" : ""&sysuserid"" ";
put ",""MF_GETUSER"" : ""%mf_getuser()"" "; put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
@@ -18931,6 +19026,16 @@ run;
put '>>weboutEND<<'; put '>>weboutEND<<';
%end; %end;
run; run;
/* now write to _webout 1 char at a time */
data _null_;
infile _sjsref lrecl=1 recfm=n;
file &fref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjsref clear;
%end; %end;
%mend mm_webout; %mend mm_webout;
@@ -19759,9 +19864,23 @@ data _null_;
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' /* force variable names to always be uppercase in the JSON */ ';
put ' options validvarname=upcase; '; put ' options validvarname=upcase; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
put ' filename _sjs1 temp lrecl=200 ; ';
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjs1 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs1 clear; ';
put ' '; put ' ';
put ' /* grab col defs */ '; put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds '; put ' proc contents noprint data=&ds ';
@@ -19818,10 +19937,20 @@ data _null_;
put ' data &tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
put ' proc json out=_sjs2 pretty ';
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs2 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs2 clear; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
@@ -19904,10 +20033,9 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' data _null_; ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' file _sjs3 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; '; put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
@@ -19918,27 +20046,29 @@ data _null_;
put ' "&&name&i"n /* name literal for reserved variable names */ '; put ' "&&name&i"n /* name literal for reserved variable names */ ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' /* now write the long strings to _webout 1 char at a time */ '; put ' ';
put ' /* close out the table */ ';
put ' data _null_; '; put ' data _null_; ';
put ' infile _sjs lrecl=1 recfm=n; '; put ' file _sjs3 mod encoding=''utf-8''; ';
put ' put '']''; ';
put ' run; ';
put ' data _null_; ';
put ' infile _sjs3 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; '; put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; '; put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; '; put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; '; put ' put sourcechar char1. @@; ';
put ' run; '; put ' run; ';
put ' /* close out the table */ '; put ' filename _sjs3 clear; ';
put ' data _null_; ';
put ' file &jref mod; ';
put ' put '']''; ';
put ' run; ';
put ' filename _sjs clear; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop table &colinfo, &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; ';
put ' file _sjs4; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; '; put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); '; put ' name=quote(trim(symget(cats(''name'',i)))); ';
@@ -19952,6 +20082,15 @@ data _null_;
put ' end; '; put ' end; ';
put ' put ''}}''; '; put ' put ''}}''; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs4 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs4 clear; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';
@@ -22100,9 +22239,23 @@ data _null_;
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' /* force variable names to always be uppercase in the JSON */ ';
put ' options validvarname=upcase; '; put ' options validvarname=upcase; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
put ' filename _sjs1 temp lrecl=200 ; ';
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjs1 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs1 clear; ';
put ' '; put ' ';
put ' /* grab col defs */ '; put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds '; put ' proc contents noprint data=&ds ';
@@ -22159,10 +22312,20 @@ data _null_;
put ' data &tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
put ' proc json out=_sjs2 pretty ';
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs2 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs2 clear; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
@@ -22245,10 +22408,9 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' data _null_; ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' file _sjs3 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; '; put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
@@ -22259,27 +22421,29 @@ data _null_;
put ' "&&name&i"n /* name literal for reserved variable names */ '; put ' "&&name&i"n /* name literal for reserved variable names */ ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' /* now write the long strings to _webout 1 char at a time */ '; put ' ';
put ' /* close out the table */ ';
put ' data _null_; '; put ' data _null_; ';
put ' infile _sjs lrecl=1 recfm=n; '; put ' file _sjs3 mod encoding=''utf-8''; ';
put ' put '']''; ';
put ' run; ';
put ' data _null_; ';
put ' infile _sjs3 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; '; put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; '; put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; '; put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; '; put ' put sourcechar char1. @@; ';
put ' run; '; put ' run; ';
put ' /* close out the table */ '; put ' filename _sjs3 clear; ';
put ' data _null_; ';
put ' file &jref mod; ';
put ' put '']''; ';
put ' run; ';
put ' filename _sjs clear; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop table &colinfo, &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; ';
put ' file _sjs4; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; '; put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); '; put ' name=quote(trim(symget(cats(''name'',i)))); ';
@@ -22293,6 +22457,15 @@ data _null_;
put ' end; '; put ' end; ';
put ' put ''}}''; '; put ' put ''}}''; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs4 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs4 clear; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';

View File

@@ -1,7 +1,12 @@
/** /**
@file mp_jsonout.sas @file mp_jsonout.sas
@brief Writes JSON in SASjs format to a fileref @brief Writes JSON in SASjs format to a fileref
@details PROC JSON is faster but will produce errs like the ones below if @details This macro can be used to OPEN a JSON stream and send one or more
tables as arrays of rows, where each row can be an object or a nested array.
There are two engines available - DATASTEP or PROCJSON.
PROC JSON is fast but will produce errs like the ones below if
special chars are encountered. special chars are encountered.
> (ERR)OR: Some code points did not transcode. > (ERR)OR: Some code points did not transcode.
@@ -12,6 +17,10 @@
If this happens, try running with ENGINE=DATASTEP. If this happens, try running with ENGINE=DATASTEP.
The DATASTEP engine is used to handle special SAS missing numerics, and
can also convert entire datasets to formatted values. Output JSON is always
in UTF-8.
Usage: Usage:
filename tmp temp; filename tmp temp;
@@ -75,9 +84,23 @@
run; run;
%end; %end;
%else %if (&action=ARR or &action=OBJ) %then %do; %else %if (&action=ARR or &action=OBJ) %then %do;
/* force variable names to always be uppercase in the JSON */
options validvarname=upcase; options validvarname=upcase;
data _null_; file &jref encoding='utf-8' mod; /* To avoid issues with _webout on EBI - such as encoding diffs and truncation
(https://support.sas.com/kb/49/325.html) we use temporary files */
filename _sjs1 temp lrecl=200 ;
data _null_; file _sjs1 encoding='utf-8';
put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":";
run;
/* now write to _webout 1 char at a time */
data _null_;
infile _sjs1 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjs1 clear;
/* grab col defs */ /* grab col defs */
proc contents noprint data=&ds proc contents noprint data=&ds
@@ -134,10 +157,20 @@
data &tempds;set &ds; data &tempds;set &ds;
%if &fmt=N %then format _numeric_ best32.;; %if &fmt=N %then format _numeric_ best32.;;
/* PRETTY is necessary to avoid line truncation in large files */ /* PRETTY is necessary to avoid line truncation in large files */
proc json out=&jref pretty filename _sjs2 temp lrecl=131068 encoding='utf-8';
proc json out=_sjs2 pretty
%if &action=ARR %then nokeys ; %if &action=ARR %then nokeys ;
;export &tempds / nosastags fmtnumeric; ;export &tempds / nosastags fmtnumeric;
run; run;
/* send back to webout */
data _null_;
infile _sjs2 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjs2 clear;
%end; %end;
%else %if &engine=DATASTEP %then %do; %else %if &engine=DATASTEP %then %do;
%datastep: %datastep:
@@ -220,10 +253,9 @@
%end; %end;
run; run;
/* write to temp loc to avoid _webout truncation filename _sjs3 temp lrecl=131068 ;
- https://support.sas.com/kb/49/325.html */ data _null_;
filename _sjs temp lrecl=131068 encoding='utf-8'; file _sjs3 encoding='utf-8';
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod ;
if _n_=1 then put "["; if _n_=1 then put "[";
set &tempds; set &tempds;
if _n_>1 then put "," @; put if _n_>1 then put "," @; put
@@ -234,27 +266,29 @@
"&&name&i"n /* name literal for reserved variable names */ "&&name&i"n /* name literal for reserved variable names */
%end; %end;
%if &action=ARR %then "]" ; %else "}" ; ; %if &action=ARR %then "]" ; %else "}" ; ;
/* now write the long strings to _webout 1 char at a time */
/* close out the table */
data _null_; data _null_;
infile _sjs lrecl=1 recfm=n; file _sjs3 mod encoding='utf-8';
put ']';
run;
data _null_;
infile _sjs3 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n; file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@; input sourcechar $char1. @@;
format sourcechar hex2.; format sourcechar hex2.;
put sourcechar char1. @@; put sourcechar char1. @@;
run; run;
/* close out the table */ filename _sjs3 clear;
data _null_;
file &jref mod;
put ']';
run;
filename _sjs clear;
%end; %end;
proc sql; proc sql;
drop table &colinfo, &tempds; drop table &colinfo, &tempds;
%if &showmeta=YES %then %do; %if &showmeta=YES %then %do;
data _null_; file &jref encoding='utf-8' mod; filename _sjs4 temp lrecl=131068 encoding='utf-8';
data _null_;
file _sjs4;
put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{";
do i=1 to &numcols; do i=1 to &numcols;
name=quote(trim(symget(cats('name',i)))); name=quote(trim(symget(cats('name',i))));
@@ -268,6 +302,15 @@
end; end;
put '}}'; put '}}';
run; run;
/* send back to webout */
data _null_;
infile _sjs4 lrecl=1 recfm=n;
file &jref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjs4 clear;
%end; %end;
%end; %end;

View File

@@ -109,9 +109,23 @@ data _null_;
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' /* force variable names to always be uppercase in the JSON */ ';
put ' options validvarname=upcase; '; put ' options validvarname=upcase; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
put ' filename _sjs1 temp lrecl=200 ; ';
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjs1 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs1 clear; ';
put ' '; put ' ';
put ' /* grab col defs */ '; put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds '; put ' proc contents noprint data=&ds ';
@@ -168,10 +182,20 @@ data _null_;
put ' data &tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
put ' proc json out=_sjs2 pretty ';
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs2 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs2 clear; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
@@ -254,10 +278,9 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' data _null_; ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' file _sjs3 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; '; put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
@@ -268,27 +291,29 @@ data _null_;
put ' "&&name&i"n /* name literal for reserved variable names */ '; put ' "&&name&i"n /* name literal for reserved variable names */ ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' /* now write the long strings to _webout 1 char at a time */ '; put ' ';
put ' /* close out the table */ ';
put ' data _null_; '; put ' data _null_; ';
put ' infile _sjs lrecl=1 recfm=n; '; put ' file _sjs3 mod encoding=''utf-8''; ';
put ' put '']''; ';
put ' run; ';
put ' data _null_; ';
put ' infile _sjs3 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; '; put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; '; put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; '; put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; '; put ' put sourcechar char1. @@; ';
put ' run; '; put ' run; ';
put ' /* close out the table */ '; put ' filename _sjs3 clear; ';
put ' data _null_; ';
put ' file &jref mod; ';
put ' put '']''; ';
put ' run; ';
put ' filename _sjs clear; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop table &colinfo, &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; ';
put ' file _sjs4; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; '; put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); '; put ' name=quote(trim(symget(cats(''name'',i)))); ';
@@ -302,6 +327,15 @@ data _null_;
put ' end; '; put ' end; ';
put ' put ''}}''; '; put ' put ''}}''; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs4 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs4 clear; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';
@@ -403,6 +437,8 @@ data _null_;
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' /* To avoid issues with _webout on EBI we use a temporary file */ ';
put ' filename _sjsref temp lrecl=131068; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ '; put ' /* if debug mode, send back first 10 records of each work table also */ ';
put ' options obs=10; '; put ' options obs=10; ';
@@ -416,11 +452,11 @@ data _null_;
put ' i+1; '; put ' i+1; ';
put ' call symputx(cats(''wt'',i),name,''l''); '; put ' call symputx(cats(''wt'',i),name,''l''); ';
put ' call symputx(''wtcnt'',i,''l''); '; put ' call symputx(''wtcnt'',i,''l''); ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put ",""WORK"":{"; '; put ' put ",""WORK"":{"; ';
put ' %do i=1 %to &wtcnt; '; put ' %do i=1 %to &wtcnt; ';
put ' %let wt=&&wt&i; '; put ' %let wt=&&wt&i; ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' dsid=open("WORK.&wt",''is''); '; put ' dsid=open("WORK.&wt",''is''); ';
put ' nlobs=attrn(dsid,''NLOBS''); '; put ' nlobs=attrn(dsid,''NLOBS''); ';
put ' nvars=attrn(dsid,''NVARS''); '; put ' nvars=attrn(dsid,''NVARS''); ';
@@ -429,16 +465,16 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) '; put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=YES) ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; '; put ' put "}"; ';
put ' %end; '; put ' %end; ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; '; put ' put "}"; ';
put ' run; '; put ' run; ';
put ' %end; '; put ' %end; ';
put ' /* close off json */ '; put ' /* close off json */ ';
put ' data _null_;file &fref mod encoding=''utf-8''; '; put ' data _null_;file _sjsref mod encoding=''utf-8''; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); '; put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; '; put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; '; put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
@@ -470,6 +506,16 @@ data _null_;
put ' put ''>>weboutEND<<''; '; put ' put ''>>weboutEND<<''; ';
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjsref lrecl=1 recfm=n; ';
put ' file &fref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjsref clear; ';
put ' ';
put '%end; '; put '%end; ';
put ' '; put ' ';
put '%mend mm_webout; '; put '%mend mm_webout; ';

View File

@@ -4,23 +4,23 @@
@details This macro should be added to the start of each Stored Process, @details This macro should be added to the start of each Stored Process,
**immediately** followed by a call to: **immediately** followed by a call to:
%mm_webout(FETCH) %mm_webout(FETCH)
This will read all the input data and create same-named SAS datasets in the This will read all the input data and create same-named SAS datasets in the
WORK library. You can then insert your code, and send data back using the WORK library. You can then insert your code, and send data back using the
following syntax: following syntax:
data some datasets; * make some data ; data some datasets; * make some data ;
retain some columns; retain some columns;
run; run;
%mm_webout(OPEN) %mm_webout(OPEN)
%mm_webout(ARR,some) * Array format, fast, suitable for large tables ; %mm_webout(ARR,some) * Array format, fast, suitable for large tables ;
%mm_webout(OBJ,datasets) * Object format, easier to work with ; %mm_webout(OBJ,datasets) * Object format, easier to work with ;
Finally, wrap everything up send some helpful system variables too Finally, wrap everything up send some helpful system variables too
%mm_webout(CLOSE) %mm_webout(CLOSE)
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@@ -35,6 +35,10 @@
object with the same name as the table but prefixed with a dollar sign - ie, object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@version 9.3 @version 9.3
@author Allan Bowe @author Allan Bowe
@@ -111,6 +115,8 @@
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
/* To avoid issues with _webout on EBI we use a temporary file */
filename _sjsref temp lrecl=131068;
%if %str(&_debug) ge 131 %then %do; %if %str(&_debug) ge 131 %then %do;
/* if debug mode, send back first 10 records of each work table also */ /* if debug mode, send back first 10 records of each work table also */
options obs=10; options obs=10;
@@ -124,11 +130,11 @@
i+1; i+1;
call symputx(cats('wt',i),name,'l'); call symputx(cats('wt',i),name,'l');
call symputx('wtcnt',i,'l'); call symputx('wtcnt',i,'l');
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
put ",""WORK"":{"; put ",""WORK"":{";
%do i=1 %to &wtcnt; %do i=1 %to &wtcnt;
%let wt=&&wt&i; %let wt=&&wt&i;
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
dsid=open("WORK.&wt",'is'); dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS'); nlobs=attrn(dsid,'NLOBS');
nvars=attrn(dsid,'NVARS'); nvars=attrn(dsid,'NVARS');
@@ -137,16 +143,16 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=YES)
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
put "}"; put "}";
%end; %end;
data _null_; file &fref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
put "}"; put "}";
run; run;
%end; %end;
/* close off json */ /* close off json */
data _null_;file &fref mod encoding='utf-8'; data _null_;file _sjsref mod encoding='utf-8';
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ",""SYSUSERID"" : ""&sysuserid"" "; put ",""SYSUSERID"" : ""&sysuserid"" ";
put ",""MF_GETUSER"" : ""%mf_getuser()"" "; put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
@@ -178,6 +184,16 @@
put '>>weboutEND<<'; put '>>weboutEND<<';
%end; %end;
run; run;
/* now write to _webout 1 char at a time */
data _null_;
infile _sjsref lrecl=1 recfm=n;
file &fref mod lrecl=1 recfm=n;
input sourcechar $char1. @@;
format sourcechar hex2.;
put sourcechar char1. @@;
run;
filename _sjsref clear;
%end; %end;
%mend mm_webout; %mend mm_webout;

View File

@@ -110,9 +110,23 @@ data _null_;
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' /* force variable names to always be uppercase in the JSON */ ';
put ' options validvarname=upcase; '; put ' options validvarname=upcase; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
put ' filename _sjs1 temp lrecl=200 ; ';
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjs1 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs1 clear; ';
put ' '; put ' ';
put ' /* grab col defs */ '; put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds '; put ' proc contents noprint data=&ds ';
@@ -169,10 +183,20 @@ data _null_;
put ' data &tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
put ' proc json out=_sjs2 pretty ';
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs2 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs2 clear; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
@@ -255,10 +279,9 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' data _null_; ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' file _sjs3 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; '; put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
@@ -269,27 +292,29 @@ data _null_;
put ' "&&name&i"n /* name literal for reserved variable names */ '; put ' "&&name&i"n /* name literal for reserved variable names */ ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' /* now write the long strings to _webout 1 char at a time */ '; put ' ';
put ' /* close out the table */ ';
put ' data _null_; '; put ' data _null_; ';
put ' infile _sjs lrecl=1 recfm=n; '; put ' file _sjs3 mod encoding=''utf-8''; ';
put ' put '']''; ';
put ' run; ';
put ' data _null_; ';
put ' infile _sjs3 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; '; put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; '; put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; '; put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; '; put ' put sourcechar char1. @@; ';
put ' run; '; put ' run; ';
put ' /* close out the table */ '; put ' filename _sjs3 clear; ';
put ' data _null_; ';
put ' file &jref mod; ';
put ' put '']''; ';
put ' run; ';
put ' filename _sjs clear; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop table &colinfo, &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; ';
put ' file _sjs4; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; '; put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); '; put ' name=quote(trim(symget(cats(''name'',i)))); ';
@@ -303,6 +328,15 @@ data _null_;
put ' end; '; put ' end; ';
put ' put ''}}''; '; put ' put ''}}''; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs4 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs4 clear; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';

View File

@@ -252,9 +252,23 @@ data _null_;
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' /* force variable names to always be uppercase in the JSON */ ';
put ' options validvarname=upcase; '; put ' options validvarname=upcase; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
put ' filename _sjs1 temp lrecl=200 ; ';
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' run; ';
put ' /* now write to _webout 1 char at a time */ ';
put ' data _null_; ';
put ' infile _sjs1 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs1 clear; ';
put ' '; put ' ';
put ' /* grab col defs */ '; put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds '; put ' proc contents noprint data=&ds ';
@@ -311,10 +325,20 @@ data _null_;
put ' data &tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
put ' proc json out=_sjs2 pretty ';
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs2 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs2 clear; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
@@ -397,10 +421,9 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' data _null_; ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' file _sjs3 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; '; put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
@@ -411,27 +434,29 @@ data _null_;
put ' "&&name&i"n /* name literal for reserved variable names */ '; put ' "&&name&i"n /* name literal for reserved variable names */ ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' /* now write the long strings to _webout 1 char at a time */ '; put ' ';
put ' /* close out the table */ ';
put ' data _null_; '; put ' data _null_; ';
put ' infile _sjs lrecl=1 recfm=n; '; put ' file _sjs3 mod encoding=''utf-8''; ';
put ' put '']''; ';
put ' run; ';
put ' data _null_; ';
put ' infile _sjs3 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; '; put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; '; put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; '; put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; '; put ' put sourcechar char1. @@; ';
put ' run; '; put ' run; ';
put ' /* close out the table */ '; put ' filename _sjs3 clear; ';
put ' data _null_; ';
put ' file &jref mod; ';
put ' put '']''; ';
put ' run; ';
put ' filename _sjs clear; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop table &colinfo, &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; ';
put ' file _sjs4; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; '; put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); '; put ' name=quote(trim(symget(cats(''name'',i)))); ';
@@ -445,6 +470,15 @@ data _null_;
put ' end; '; put ' end; ';
put ' put ''}}''; '; put ' put ''}}''; ';
put ' run; '; put ' run; ';
put ' /* send back to webout */ ';
put ' data _null_; ';
put ' infile _sjs4 lrecl=1 recfm=n; ';
put ' file &jref mod lrecl=1 recfm=n; ';
put ' input sourcechar $char1. @@; ';
put ' format sourcechar hex2.; ';
put ' put sourcechar char1. @@; ';
put ' run; ';
put ' filename _sjs4 clear; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';