- VPRJSOND ;SLC/KCM -- Decode JSON
- ;;1.0;VIRTUAL PATIENT RECORD;**2**;Sep 01, 2011;Build 317
- ;
- DECODE(VVJSON,VVROOT,VVERR) ; Set JSON object into closed array ref VVROOT
- ;
- DIRECT ; TAG for use by DECODE^VPRJSON
- ;
- ; Examples: D DECODE^VPRJSON("MYJSON","LOCALVAR","LOCALERR")
- ; D DECODE^VPRJSON("^MYJSON(1)","^GLO(99)","^TMP($J)")
- ;
- ; VVJSON: string/array containing serialized JSON object
- ; VVROOT: closed array reference for M representation of object
- ; VVERR: contains error messages, defaults to ^TMP("VPRJERR",$J)
- ;
- ; VVIDX: points to next character in JSON string to process
- ; VVSTACK: manages stack of subscripts
- ; VVPROP: true if next string is property name, otherwise treat as value
- ;
- N VVMAX S VVMAX=4000 ; limit document lines to 4000 characters
- S VVERR=$G(VVERR,"^TMP(""VPRJERR"",$J)")
- ; If a simple string is passed in, move it to an temp array (VVINPUT)
- ; so that the processing is consistently on an array.
- I $D(@VVJSON)=1 N VVINPUT S VVINPUT(1)=@VVJSON,VVJSON="VVINPUT"
- S VVROOT=$NA(@VVROOT@("Z")),VVROOT=$E(VVROOT,1,$L(VVROOT)-4) ; make open array ref
- N VVLINE,VVIDX,VVSTACK,VVPROP,VVTYPE,VVERRORS
- S VVLINE=$O(@VVJSON@("")),VVIDX=1,VVSTACK=0,VVPROP=0,VVERRORS=0
- F S VVTYPE=$$NXTKN() Q:VVTYPE="" D I VVERRORS Q
- . I VVTYPE="{" S VVSTACK=VVSTACK+1,VVSTACK(VVSTACK)="",VVPROP=1 D:VVSTACK>64 ERRX("STL{") Q
- . I VVTYPE="}" D:VVSTACK(VVSTACK) ERRX("OBM") S VVSTACK=VVSTACK-1 D:VVSTACK<0 ERRX("SUF}") Q
- . I VVTYPE="[" S VVSTACK=VVSTACK+1,VVSTACK(VVSTACK)=1 D:VVSTACK>64 ERRX("STL[") Q
- . I VVTYPE="]" D:'VVSTACK(VVSTACK) ERRX("ARM") S VVSTACK=VVSTACK-1 D:VVSTACK<0 ERRX("SUF]") Q
- . I VVTYPE="," D Q
- . . I VVSTACK(VVSTACK) S VVSTACK(VVSTACK)=VVSTACK(VVSTACK)+1 ; next in array
- . . E S VVPROP=1 ; or next property name
- . I VVTYPE=":" S VVPROP=0 D:'$L($G(VVSTACK(VVSTACK))) ERRX("MPN") Q
- . I VVTYPE="""" D Q
- . . I VVPROP S VVSTACK(VVSTACK)=$$NAMPARS() I 1
- . . E D ADDSTR
- . S VVTYPE=$TR(VVTYPE,"TFN","tfn")
- . I VVTYPE="t" D SETBOOL("t") Q
- . I VVTYPE="f" D SETBOOL("f") Q
- . I VVTYPE="n" D SETBOOL("n") Q
- . I "0123456789+-.eE"[VVTYPE S @$$CURNODE()=$$NUMPARS(VVTYPE) Q
- . D ERRX("TKN",VVTYPE)
- I VVSTACK'=0 D ERRX("SCT",VVSTACK)
- Q
- NXTKN() ; Move the pointers to the beginning of the next token
- N VVDONE,VVEOF,VVTOKEN
- S VVDONE=0,VVEOF=0 F D Q:VVDONE!VVEOF ; eat spaces & new lines until next visible char
- . I VVIDX>$L(@VVJSON@(VVLINE)) S VVLINE=$O(@VVJSON@(VVLINE)),VVIDX=1 I 'VVLINE S VVEOF=1 Q
- . I $A(@VVJSON@(VVLINE),VVIDX)>32 S VVDONE=1 Q
- . S VVIDX=VVIDX+1
- Q:VVEOF "" ; we're at the end of input
- S VVTOKEN=$E(@VVJSON@(VVLINE),VVIDX),VVIDX=VVIDX+1
- Q VVTOKEN
- ;
- ADDSTR ; Add string value to current node, escaping text along the way
- ; Expects VVLINE,VVIDX to reference that starting point of the index
- ; TODO: add a mechanism to specify names that should not be escaped
- ; just store as ":")= and ":",n)=
- ;
- ; Happy path -- we find the end quote in the same line
- N VVEND,VVX
- S VVEND=$F(@VVJSON@(VVLINE),"""",VVIDX)
- I VVEND,($E(@VVJSON@(VVLINE),VVEND-2)'="\") D SETSTR QUIT ;normal
- I VVEND,$$ISCLOSEQ(VVLINE) D SETSTR QUIT ;close quote preceded by escaped \
- ;
- ; Less happy path -- first quote wasn't close quote
- N VVDONE,VVTLINE
- S VVDONE=0,VVTLINE=VVLINE ; VVTLINE for temporary increment of VVLINE
- F D Q:VVDONE Q:VVERRORS
- . ;if no quote on current line advance line, scan again
- . I 'VVEND S VVTLINE=VVTLINE+1,VVEND=1 I '$D(@VVJSON@(VVTLINE)) D ERRX("EIQ") Q
- . S VVEND=$F(@VVJSON@(VVTLINE),"""",VVEND)
- . Q:'VVEND ; continue on to next line if no quote found on this one
- . I (VVEND>2),($E(@VVJSON@(VVTLINE),VVEND-2)'="\") S VVDONE=1 Q ; found quote position
- . S VVDONE=$$ISCLOSEQ(VVTLINE) ; see if this is an escaped quote or closing quote
- Q:VVERRORS
- ; unescape from VVIDX to VVEND, using \-extension nodes as necessary
- D UESEXT
- ; now we need to move VVLINE and VVIDX to next parsing point
- S VVLINE=VVTLINE,VVIDX=VVEND
- Q
- SETSTR ; Set simple string value from within same line
- ; expects VVJSON, VVLINE, VVINX, VVEND
- N VVX
- S VVX=$E(@VVJSON@(VVLINE),VVIDX,VVEND-2),VVIDX=VVEND
- S @$$CURNODE()=$$UES(VVX)
- I (+VVX=VVX)!($E(VVX,1,2)="0.")!($E(VVX,1,3)="-0.") S @$$CURNODE()@("\s")=""
- I VVIDX>$L(@VVJSON@(VVLINE)) S VVLINE=VVLINE+1,VVIDX=1
- Q
- UESEXT ; unescape from VVLINE,VVIDX to VVTLINE,VVEND & extend (\) if necessary
- ; expects VVLINE,VVIDX,VVTLINE,VVEND
- N VVI,VVY,VVSTART,VVSTOP,VVDONE,VVBUF,VVNODE,VVMORE,VVTO
- S VVNODE=$$CURNODE(),VVBUF="",VVMORE=0,VVSTOP=VVEND-2
- S VVI=VVIDX,VVY=VVLINE,VVDONE=0
- F D Q:VVDONE Q:VVERRORS
- . S VVSTART=VVI,VVI=$F(@VVJSON@(VVY),"\",VVI)
- . ; if we are on the last line, don't extract past VVSTOP
- . I (VVY=VVTLINE) S VVTO=$S('VVI:VVSTOP,VVI>VVSTOP:VVSTOP,1:VVI-2) I 1
- . E S VVTO=$S('VVI:99999,1:VVI-2)
- . D ADDBUF($E(@VVJSON@(VVY),VVSTART,VVTO))
- . I (VVY'<VVTLINE),(('VVI)!(VVI>VVSTOP)) S VVDONE=1 QUIT ; now past close quote
- . I 'VVI S VVY=VVY+1,VVI=1 QUIT ; nothing escaped, go to next line
- . I VVI>$L(@VVJSON@(VVY)) S VVY=VVY+1,VVI=1 I '$D(@VVJSON@(VVY)) D ERRX("EIU")
- . N VVTGT S VVTGT=$E(@VVJSON@(VVY),VVI)
- . I VVTGT="u" D I 1
- . . N VVTGTC S VVTGTC=$E(@VVJSON@(VVY),VVI+1,VVI+4),VVI=VVI+4
- . . I $L(VVTGTC)<4 S VVY=VVY+1,VVI=4-$L(VVTGTC),VVTGTC=VVTGTC_$E(@VVJSON@(VVY),1,VVI)
- . . D ADDBUF($C($$DEC^XLFUTL(VVTGTC,16)))
- . E D ADDBUF($$REALCHAR(VVTGT))
- . S VVI=VVI+1
- . I (VVY'<VVTLINE),(VVI>VVSTOP) S VVDONE=1 ; VVI incremented past stop
- Q:VVERRORS
- D SAVEBUF
- Q
- ADDBUF(VVX) ; add buffer of characters to destination
- ; expects VVBUF,VVMAX,VVNODE,VVMORE to be defined
- ; used directly by ADDSTR
- I $L(VVX)+$L(VVBUF)>VVMAX D SAVEBUF
- S VVBUF=VVBUF_VVX
- Q
- SAVEBUF ; write out buffer to destination
- ; expects VVBUF,VVMAX,VVNODE,VVMORE to be defined
- ; used directly by ADDSTR,ADDBUF
- I VVMORE S @VVNODE@("\",VVMORE)=VVBUF
- I 'VVMORE S @VVNODE=VVBUF I $L(VVBUF)<19,+$E(VVBUF,1,18) S @VVNODE@("\s")=""
- S VVMORE=VVMORE+1,VVBUF=""
- Q
- ISCLOSEQ(VVBLINE) ; return true if this is a closing, rather than escaped, quote
- ; expects
- ; VVJSON: lines of the JSON encoded string
- ; VVIDX: points to 1st character of the segment
- ; VVLINE: points to the line in which the segment starts
- ; VVEND: points to 1st character after the " (may be past the end of the line)
- ; used directly by ADDSTR
- N VVBS,VVBIDX,VVBDONE
- S VVBS=0,VVBIDX=VVEND-2,VVBDONE=0 ; VVBIDX starts at 1st character before quote
- ; count the backslashes preceding the quote (odd number means the quote was escaped)
- F D Q:VVBDONE!VVERRORS
- . I VVBIDX<1 D Q ; when VVBIDX<1 go back a line
- . . S VVBLINE=VVBLINE-1 I VVBLINE<VVLINE D ERRX("RSB") Q
- . . S VVBIDX=$L(@VVJSON@(VVBLINE))
- . I $E(@VVJSON@(VVBLINE),VVBIDX)'="\" S VVBDONE=1 Q
- . S VVBS=VVBS+1,VVBIDX=VVBIDX-1
- Q VVBS#2=0 ; VVBS is even if this is a close quote
- ;
- NAMPARS() ; Return parsed name, advancing index past the close quote
- ; -- This assumes no embedded quotes are in the name itself --
- N VVEND,VVDONE,VVNAME
- S VVDONE=0,VVNAME=""
- F D Q:VVDONE Q:VVERRORS
- . S VVEND=$F(@VVJSON@(VVLINE),"""",VVIDX)
- . I VVEND S VVNAME=VVNAME_$E(@VVJSON@(VVLINE),VVIDX,VVEND-2),VVIDX=VVEND,VVDONE=1
- . I 'VVEND S VVNAME=VVNAME_$E(@VVJSON@(VVLINE),VVIDX,$L(@VVJSON@(VVLINE)))
- . I 'VVEND!(VVEND>$L(@VVJSON@(VVLINE))) S VVLINE=VVLINE+1,VVIDX=1 I '$D(@VVJSON@(VVLINE)) D ERRX("ORN")
- Q VVNAME
- ;
- NUMPARS(VVDIGIT) ; Return parsed number, advancing index past the end of the number
- ; VVIDX intially references the second digit
- N VVDONE,VVNUM
- S VVDONE=0,VVNUM=VVDIGIT
- F D Q:VVDONE Q:VVERRORS
- . I '("0123456789+-.eE"[$E(@VVJSON@(VVLINE),VVIDX)) S VVDONE=1 Q
- . S VVNUM=VVNUM_$E(@VVJSON@(VVLINE),VVIDX)
- . S VVIDX=VVIDX+1 I VVIDX>$L(@VVJSON@(VVLINE)) S VVLINE=VVLINE+1,VVIDX=1 I '$D(@VVJSON@(VVLINE)) D ERRX("OR#")
- Q VVNUM
- ;
- SETBOOL(VVLTR) ; Return parsed value, advancing index past the end of the value
- N VVDONE,VVBOOL,VVX
- S VVDONE=0,VVBOOL=VVLTR
- F D Q:VVDONE Q:VVERRORS
- . S VVX=$TR($E(@VVJSON@(VVLINE),VVIDX),"TRUEFALSN","truefalsn")
- . I '("truefalsn"[VVX) S VVDONE=1 Q
- . S VVBOOL=VVBOOL_VVX
- . S VVIDX=VVIDX+1 I VVIDX>$L(@VVJSON@(VVLINE)) S VVLINE=VVLINE+1,VVIDX=1 I '$D(@VVJSON@(VVLINE)) D ERRX("ORB")
- I VVLTR="t",(VVBOOL'="true") D ERRX("EXT",VVTYPE)
- I VVLTR="f",(VVBOOL'="false") D ERRX("EXF",VVTYPE)
- I VVLTR="n",(VVBOOL'="null") D ERRX("EXN",VVTYPE)
- S @$$CURNODE()=VVBOOL
- Q
- ;
- OSETBOOL(VVX) ; set a value and increment VVIDX
- S @$$CURNODE()=VVX
- S VVIDX=VVIDX+$L(VVX)-1
- N VVDIFF S VVDIFF=VVIDX-$L(@VVJSON@(VVLINE)) ; in case VVIDX moves to next line
- I VVDIFF>0 S VVLINE=VVLINE+1,VVIDX=VVDIFF I '$D(@VVJSON@(VVLINE)) D ERRX("ORB")
- Q
- CURNODE() ; Return a global/local variable name based on VVSTACK
- ; Expects VVSTACK to be defined already
- N VVI,VVSUBS
- S VVSUBS=""
- F VVI=1:1:VVSTACK S:VVI>1 VVSUBS=VVSUBS_"," D
- . I VVSTACK(VVI) S VVSUBS=VVSUBS_VVSTACK(VVI)
- . E S VVSUBS=VVSUBS_""""_VVSTACK(VVI)_""""
- Q VVROOT_VVSUBS_")"
- ;
- UES(X) ; Unescape JSON string
- ; copy segments from START to POS-2 (right before \)
- ; translate target character (which is at $F position)
- N POS,Y,START
- S POS=0,Y=""
- F S START=POS+1 D Q:START>$L(X)
- . S POS=$F(X,"\",START) ; find next position
- . I 'POS S Y=Y_$E(X,START,$L(X)),POS=$L(X) Q
- . ; otherwise handle escaped char
- . N TGT
- . S TGT=$E(X,POS),Y=Y_$E(X,START,POS-2)
- . I TGT="u" S Y=Y_$C($$DEC^XLFUTL($E(X,POS+1,POS+4),16)),POS=POS+4 Q
- . S Y=Y_$$REALCHAR(TGT)
- Q Y
- ;
- REALCHAR(C) ; Return actual character from escaped
- I C="""" Q """"
- I C="/" Q "/"
- I C="\" Q "\"
- I C="b" Q $C(8)
- I C="f" Q $C(12)
- I C="n" Q $C(10)
- I C="r" Q $C(13)
- I C="t" Q $C(9)
- I C="u" ;case covered above in $$DEC^XLFUTL calls
- ;otherwise
- I $L($G(VVERR)) D ERRX("ESC",C)
- Q C
- ;
- ERRX(ID,VAL) ; Set the appropriate error message
- D ERRX^VPRJSON(ID,$G(VAL))
- Q
- --- Routine Detail --- with STRUCTURED ROUTINE LISTING ---[H[J[2J[HVPRJSOND 9970 printed Mar 13, 2025@21:50:26 Page 2
- VPRJSOND ;SLC/KCM -- Decode JSON
- +1 ;;1.0;VIRTUAL PATIENT RECORD;**2**;Sep 01, 2011;Build 317
- +2 ;
- DECODE(VVJSON,VVROOT,VVERR) ; Set JSON object into closed array ref VVROOT
- +1 ;
- DIRECT ; TAG for use by DECODE^VPRJSON
- +1 ;
- +2 ; Examples: D DECODE^VPRJSON("MYJSON","LOCALVAR","LOCALERR")
- +3 ; D DECODE^VPRJSON("^MYJSON(1)","^GLO(99)","^TMP($J)")
- +4 ;
- +5 ; VVJSON: string/array containing serialized JSON object
- +6 ; VVROOT: closed array reference for M representation of object
- +7 ; VVERR: contains error messages, defaults to ^TMP("VPRJERR",$J)
- +8 ;
- +9 ; VVIDX: points to next character in JSON string to process
- +10 ; VVSTACK: manages stack of subscripts
- +11 ; VVPROP: true if next string is property name, otherwise treat as value
- +12 ;
- +13 ; limit document lines to 4000 characters
- NEW VVMAX
- SET VVMAX=4000
- +14 SET VVERR=$GET(VVERR,"^TMP(""VPRJERR"",$J)")
- +15 ; If a simple string is passed in, move it to an temp array (VVINPUT)
- +16 ; so that the processing is consistently on an array.
- +17 IF $DATA(@VVJSON)=1
- NEW VVINPUT
- SET VVINPUT(1)=@VVJSON
- SET VVJSON="VVINPUT"
- +18 ; make open array ref
- SET VVROOT=$NAME(@VVROOT@("Z"))
- SET VVROOT=$EXTRACT(VVROOT,1,$LENGTH(VVROOT)-4)
- +19 NEW VVLINE,VVIDX,VVSTACK,VVPROP,VVTYPE,VVERRORS
- +20 SET VVLINE=$ORDER(@VVJSON@(""))
- SET VVIDX=1
- SET VVSTACK=0
- SET VVPROP=0
- SET VVERRORS=0
- +21 FOR
- SET VVTYPE=$$NXTKN()
- if VVTYPE=""
- QUIT
- Begin DoDot:1
- +22 IF VVTYPE="{"
- SET VVSTACK=VVSTACK+1
- SET VVSTACK(VVSTACK)=""
- SET VVPROP=1
- if VVSTACK>64
- DO ERRX("STL{")
- QUIT
- +23 IF VVTYPE="}"
- if VVSTACK(VVSTACK)
- DO ERRX("OBM")
- SET VVSTACK=VVSTACK-1
- if VVSTACK<0
- DO ERRX("SUF}")
- QUIT
- +24 IF VVTYPE="["
- SET VVSTACK=VVSTACK+1
- SET VVSTACK(VVSTACK)=1
- if VVSTACK>64
- DO ERRX("STL[")
- QUIT
- +25 IF VVTYPE="]"
- if 'VVSTACK(VVSTACK)
- DO ERRX("ARM")
- SET VVSTACK=VVSTACK-1
- if VVSTACK<0
- DO ERRX("SUF]")
- QUIT
- +26 IF VVTYPE=","
- Begin DoDot:2
- +27 ; next in array
- IF VVSTACK(VVSTACK)
- SET VVSTACK(VVSTACK)=VVSTACK(VVSTACK)+1
- +28 ; or next property name
- IF '$TEST
- SET VVPROP=1
- End DoDot:2
- QUIT
- +29 IF VVTYPE=":"
- SET VVPROP=0
- if '$LENGTH($GET(VVSTACK(VVSTACK)))
- DO ERRX("MPN")
- QUIT
- +30 IF VVTYPE=""""
- Begin DoDot:2
- +31 IF VVPROP
- SET VVSTACK(VVSTACK)=$$NAMPARS()
- IF 1
- +32 IF '$TEST
- DO ADDSTR
- End DoDot:2
- QUIT
- +33 SET VVTYPE=$TRANSLATE(VVTYPE,"TFN","tfn")
- +34 IF VVTYPE="t"
- DO SETBOOL("t")
- QUIT
- +35 IF VVTYPE="f"
- DO SETBOOL("f")
- QUIT
- +36 IF VVTYPE="n"
- DO SETBOOL("n")
- QUIT
- +37 IF "0123456789+-.eE"[VVTYPE
- SET @$$CURNODE()=$$NUMPARS(VVTYPE)
- QUIT
- +38 DO ERRX("TKN",VVTYPE)
- End DoDot:1
- IF VVERRORS
- QUIT
- +39 IF VVSTACK'=0
- DO ERRX("SCT",VVSTACK)
- +40 QUIT
- NXTKN() ; Move the pointers to the beginning of the next token
- +1 NEW VVDONE,VVEOF,VVTOKEN
- +2 ; eat spaces & new lines until next visible char
- SET VVDONE=0
- SET VVEOF=0
- FOR
- Begin DoDot:1
- +3 IF VVIDX>$LENGTH(@VVJSON@(VVLINE))
- SET VVLINE=$ORDER(@VVJSON@(VVLINE))
- SET VVIDX=1
- IF 'VVLINE
- SET VVEOF=1
- QUIT
- +4 IF $ASCII(@VVJSON@(VVLINE),VVIDX)>32
- SET VVDONE=1
- QUIT
- +5 SET VVIDX=VVIDX+1
- End DoDot:1
- if VVDONE!VVEOF
- QUIT
- +6 ; we're at the end of input
- if VVEOF
- QUIT ""
- +7 SET VVTOKEN=$EXTRACT(@VVJSON@(VVLINE),VVIDX)
- SET VVIDX=VVIDX+1
- +8 QUIT VVTOKEN
- +9 ;
- ADDSTR ; Add string value to current node, escaping text along the way
- +1 ; Expects VVLINE,VVIDX to reference that starting point of the index
- +2 ; TODO: add a mechanism to specify names that should not be escaped
- +3 ; just store as ":")= and ":",n)=
- +4 ;
- +5 ; Happy path -- we find the end quote in the same line
- +6 NEW VVEND,VVX
- +7 SET VVEND=$FIND(@VVJSON@(VVLINE),"""",VVIDX)
- +8 ;normal
- IF VVEND
- IF ($EXTRACT(@VVJSON@(VVLINE),VVEND-2)'="\")
- DO SETSTR
- QUIT
- +9 ;close quote preceded by escaped \
- IF VVEND
- IF $$ISCLOSEQ(VVLINE)
- DO SETSTR
- QUIT
- +10 ;
- +11 ; Less happy path -- first quote wasn't close quote
- +12 NEW VVDONE,VVTLINE
- +13 ; VVTLINE for temporary increment of VVLINE
- SET VVDONE=0
- SET VVTLINE=VVLINE
- +14 FOR
- Begin DoDot:1
- +15 ;if no quote on current line advance line, scan again
- +16 IF 'VVEND
- SET VVTLINE=VVTLINE+1
- SET VVEND=1
- IF '$DATA(@VVJSON@(VVTLINE))
- DO ERRX("EIQ")
- QUIT
- +17 SET VVEND=$FIND(@VVJSON@(VVTLINE),"""",VVEND)
- +18 ; continue on to next line if no quote found on this one
- if 'VVEND
- QUIT
- +19 ; found quote position
- IF (VVEND>2)
- IF ($EXTRACT(@VVJSON@(VVTLINE),VVEND-2)'="\")
- SET VVDONE=1
- QUIT
- +20 ; see if this is an escaped quote or closing quote
- SET VVDONE=$$ISCLOSEQ(VVTLINE)
- End DoDot:1
- if VVDONE
- QUIT
- if VVERRORS
- QUIT
- +21 if VVERRORS
- QUIT
- +22 ; unescape from VVIDX to VVEND, using \-extension nodes as necessary
- +23 DO UESEXT
- +24 ; now we need to move VVLINE and VVIDX to next parsing point
- +25 SET VVLINE=VVTLINE
- SET VVIDX=VVEND
- +26 QUIT
- SETSTR ; Set simple string value from within same line
- +1 ; expects VVJSON, VVLINE, VVINX, VVEND
- +2 NEW VVX
- +3 SET VVX=$EXTRACT(@VVJSON@(VVLINE),VVIDX,VVEND-2)
- SET VVIDX=VVEND
- +4 SET @$$CURNODE()=$$UES(VVX)
- +5 IF (+VVX=VVX)!($EXTRACT(VVX,1,2)="0.")!($EXTRACT(VVX,1,3)="-0.")
- SET @$$CURNODE()@("\s")=""
- +6 IF VVIDX>$LENGTH(@VVJSON@(VVLINE))
- SET VVLINE=VVLINE+1
- SET VVIDX=1
- +7 QUIT
- UESEXT ; unescape from VVLINE,VVIDX to VVTLINE,VVEND & extend (\) if necessary
- +1 ; expects VVLINE,VVIDX,VVTLINE,VVEND
- +2 NEW VVI,VVY,VVSTART,VVSTOP,VVDONE,VVBUF,VVNODE,VVMORE,VVTO
- +3 SET VVNODE=$$CURNODE()
- SET VVBUF=""
- SET VVMORE=0
- SET VVSTOP=VVEND-2
- +4 SET VVI=VVIDX
- SET VVY=VVLINE
- SET VVDONE=0
- +5 FOR
- Begin DoDot:1
- +6 SET VVSTART=VVI
- SET VVI=$FIND(@VVJSON@(VVY),"\",VVI)
- +7 ; if we are on the last line, don't extract past VVSTOP
- +8 IF (VVY=VVTLINE)
- SET VVTO=$SELECT('VVI:VVSTOP,VVI>VVSTOP:VVSTOP,1:VVI-2)
- IF 1
- +9 IF '$TEST
- SET VVTO=$SELECT('VVI:99999,1:VVI-2)
- +10 DO ADDBUF($EXTRACT(@VVJSON@(VVY),VVSTART,VVTO))
- +11 ; now past close quote
- IF (VVY'<VVTLINE)
- IF (('VVI)!(VVI>VVSTOP))
- SET VVDONE=1
- QUIT
- +12 ; nothing escaped, go to next line
- IF 'VVI
- SET VVY=VVY+1
- SET VVI=1
- QUIT
- +13 IF VVI>$LENGTH(@VVJSON@(VVY))
- SET VVY=VVY+1
- SET VVI=1
- IF '$DATA(@VVJSON@(VVY))
- DO ERRX("EIU")
- +14 NEW VVTGT
- SET VVTGT=$EXTRACT(@VVJSON@(VVY),VVI)
- +15 IF VVTGT="u"
- Begin DoDot:2
- +16 NEW VVTGTC
- SET VVTGTC=$EXTRACT(@VVJSON@(VVY),VVI+1,VVI+4)
- SET VVI=VVI+4
- +17 IF $LENGTH(VVTGTC)<4
- SET VVY=VVY+1
- SET VVI=4-$LENGTH(VVTGTC)
- SET VVTGTC=VVTGTC_$EXTRACT(@VVJSON@(VVY),1,VVI)
- +18 DO ADDBUF($CHAR($$DEC^XLFUTL(VVTGTC,16)))
- End DoDot:2
- IF 1
- +19 IF '$TEST
- DO ADDBUF($$REALCHAR(VVTGT))
- +20 SET VVI=VVI+1
- +21 ; VVI incremented past stop
- IF (VVY'<VVTLINE)
- IF (VVI>VVSTOP)
- SET VVDONE=1
- End DoDot:1
- if VVDONE
- QUIT
- if VVERRORS
- QUIT
- +22 if VVERRORS
- QUIT
- +23 DO SAVEBUF
- +24 QUIT
- ADDBUF(VVX) ; add buffer of characters to destination
- +1 ; expects VVBUF,VVMAX,VVNODE,VVMORE to be defined
- +2 ; used directly by ADDSTR
- +3 IF $LENGTH(VVX)+$LENGTH(VVBUF)>VVMAX
- DO SAVEBUF
- +4 SET VVBUF=VVBUF_VVX
- +5 QUIT
- SAVEBUF ; write out buffer to destination
- +1 ; expects VVBUF,VVMAX,VVNODE,VVMORE to be defined
- +2 ; used directly by ADDSTR,ADDBUF
- +3 IF VVMORE
- SET @VVNODE@("\",VVMORE)=VVBUF
- +4 IF 'VVMORE
- SET @VVNODE=VVBUF
- IF $LENGTH(VVBUF)<19
- IF +$EXTRACT(VVBUF,1,18)
- SET @VVNODE@("\s")=""
- +5 SET VVMORE=VVMORE+1
- SET VVBUF=""
- +6 QUIT
- ISCLOSEQ(VVBLINE) ; return true if this is a closing, rather than escaped, quote
- +1 ; expects
- +2 ; VVJSON: lines of the JSON encoded string
- +3 ; VVIDX: points to 1st character of the segment
- +4 ; VVLINE: points to the line in which the segment starts
- +5 ; VVEND: points to 1st character after the " (may be past the end of the line)
- +6 ; used directly by ADDSTR
- +7 NEW VVBS,VVBIDX,VVBDONE
- +8 ; VVBIDX starts at 1st character before quote
- SET VVBS=0
- SET VVBIDX=VVEND-2
- SET VVBDONE=0
- +9 ; count the backslashes preceding the quote (odd number means the quote was escaped)
- +10 FOR
- Begin DoDot:1
- +11 ; when VVBIDX<1 go back a line
- IF VVBIDX<1
- Begin DoDot:2
- +12 SET VVBLINE=VVBLINE-1
- IF VVBLINE<VVLINE
- DO ERRX("RSB")
- QUIT
- +13 SET VVBIDX=$LENGTH(@VVJSON@(VVBLINE))
- End DoDot:2
- QUIT
- +14 IF $EXTRACT(@VVJSON@(VVBLINE),VVBIDX)'="\"
- SET VVBDONE=1
- QUIT
- +15 SET VVBS=VVBS+1
- SET VVBIDX=VVBIDX-1
- End DoDot:1
- if VVBDONE!VVERRORS
- QUIT
- +16 ; VVBS is even if this is a close quote
- QUIT VVBS#2=0
- +17 ;
- NAMPARS() ; Return parsed name, advancing index past the close quote
- +1 ; -- This assumes no embedded quotes are in the name itself --
- +2 NEW VVEND,VVDONE,VVNAME
- +3 SET VVDONE=0
- SET VVNAME=""
- +4 FOR
- Begin DoDot:1
- +5 SET VVEND=$FIND(@VVJSON@(VVLINE),"""",VVIDX)
- +6 IF VVEND
- SET VVNAME=VVNAME_$EXTRACT(@VVJSON@(VVLINE),VVIDX,VVEND-2)
- SET VVIDX=VVEND
- SET VVDONE=1
- +7 IF 'VVEND
- SET VVNAME=VVNAME_$EXTRACT(@VVJSON@(VVLINE),VVIDX,$LENGTH(@VVJSON@(VVLINE)))
- +8 IF 'VVEND!(VVEND>$LENGTH(@VVJSON@(VVLINE)))
- SET VVLINE=VVLINE+1
- SET VVIDX=1
- IF '$DATA(@VVJSON@(VVLINE))
- DO ERRX("ORN")
- End DoDot:1
- if VVDONE
- QUIT
- if VVERRORS
- QUIT
- +9 QUIT VVNAME
- +10 ;
- NUMPARS(VVDIGIT) ; Return parsed number, advancing index past the end of the number
- +1 ; VVIDX intially references the second digit
- +2 NEW VVDONE,VVNUM
- +3 SET VVDONE=0
- SET VVNUM=VVDIGIT
- +4 FOR
- Begin DoDot:1
- +5 IF '("0123456789+-.eE"[$EXTRACT(@VVJSON@(VVLINE),VVIDX))
- SET VVDONE=1
- QUIT
- +6 SET VVNUM=VVNUM_$EXTRACT(@VVJSON@(VVLINE),VVIDX)
- +7 SET VVIDX=VVIDX+1
- IF VVIDX>$LENGTH(@VVJSON@(VVLINE))
- SET VVLINE=VVLINE+1
- SET VVIDX=1
- IF '$DATA(@VVJSON@(VVLINE))
- DO ERRX("OR#")
- End DoDot:1
- if VVDONE
- QUIT
- if VVERRORS
- QUIT
- +8 QUIT VVNUM
- +9 ;
- SETBOOL(VVLTR) ; Return parsed value, advancing index past the end of the value
- +1 NEW VVDONE,VVBOOL,VVX
- +2 SET VVDONE=0
- SET VVBOOL=VVLTR
- +3 FOR
- Begin DoDot:1
- +4 SET VVX=$TRANSLATE($EXTRACT(@VVJSON@(VVLINE),VVIDX),"TRUEFALSN","truefalsn")
- +5 IF '("truefalsn"[VVX)
- SET VVDONE=1
- QUIT
- +6 SET VVBOOL=VVBOOL_VVX
- +7 SET VVIDX=VVIDX+1
- IF VVIDX>$LENGTH(@VVJSON@(VVLINE))
- SET VVLINE=VVLINE+1
- SET VVIDX=1
- IF '$DATA(@VVJSON@(VVLINE))
- DO ERRX("ORB")
- End DoDot:1
- if VVDONE
- QUIT
- if VVERRORS
- QUIT
- +8 IF VVLTR="t"
- IF (VVBOOL'="true")
- DO ERRX("EXT",VVTYPE)
- +9 IF VVLTR="f"
- IF (VVBOOL'="false")
- DO ERRX("EXF",VVTYPE)
- +10 IF VVLTR="n"
- IF (VVBOOL'="null")
- DO ERRX("EXN",VVTYPE)
- +11 SET @$$CURNODE()=VVBOOL
- +12 QUIT
- +13 ;
- OSETBOOL(VVX) ; set a value and increment VVIDX
- +1 SET @$$CURNODE()=VVX
- +2 SET VVIDX=VVIDX+$LENGTH(VVX)-1
- +3 ; in case VVIDX moves to next line
- NEW VVDIFF
- SET VVDIFF=VVIDX-$LENGTH(@VVJSON@(VVLINE))
- +4 IF VVDIFF>0
- SET VVLINE=VVLINE+1
- SET VVIDX=VVDIFF
- IF '$DATA(@VVJSON@(VVLINE))
- DO ERRX("ORB")
- +5 QUIT
- CURNODE() ; Return a global/local variable name based on VVSTACK
- +1 ; Expects VVSTACK to be defined already
- +2 NEW VVI,VVSUBS
- +3 SET VVSUBS=""
- +4 FOR VVI=1:1:VVSTACK
- if VVI>1
- SET VVSUBS=VVSUBS_","
- Begin DoDot:1
- +5 IF VVSTACK(VVI)
- SET VVSUBS=VVSUBS_VVSTACK(VVI)
- +6 IF '$TEST
- SET VVSUBS=VVSUBS_""""_VVSTACK(VVI)_""""
- End DoDot:1
- +7 QUIT VVROOT_VVSUBS_")"
- +8 ;
- UES(X) ; Unescape JSON string
- +1 ; copy segments from START to POS-2 (right before \)
- +2 ; translate target character (which is at $F position)
- +3 NEW POS,Y,START
- +4 SET POS=0
- SET Y=""
- +5 FOR
- SET START=POS+1
- Begin DoDot:1
- +6 ; find next position
- SET POS=$FIND(X,"\",START)
- +7 IF 'POS
- SET Y=Y_$EXTRACT(X,START,$LENGTH(X))
- SET POS=$LENGTH(X)
- QUIT
- +8 ; otherwise handle escaped char
- +9 NEW TGT
- +10 SET TGT=$EXTRACT(X,POS)
- SET Y=Y_$EXTRACT(X,START,POS-2)
- +11 IF TGT="u"
- SET Y=Y_$CHAR($$DEC^XLFUTL($EXTRACT(X,POS+1,POS+4),16))
- SET POS=POS+4
- QUIT
- +12 SET Y=Y_$$REALCHAR(TGT)
- End DoDot:1
- if START>$LENGTH(X)
- QUIT
- +13 QUIT Y
- +14 ;
- REALCHAR(C) ; Return actual character from escaped
- +1 IF C=""""
- QUIT """"
- +2 IF C="/"
- QUIT "/"
- +3 IF C="\"
- QUIT "\"
- +4 IF C="b"
- QUIT $CHAR(8)
- +5 IF C="f"
- QUIT $CHAR(12)
- +6 IF C="n"
- QUIT $CHAR(10)
- +7 IF C="r"
- QUIT $CHAR(13)
- +8 IF C="t"
- QUIT $CHAR(9)
- +9 ;case covered above in $$DEC^XLFUTL calls
- IF C="u"
- +10 ;otherwise
- +11 IF $LENGTH($GET(VVERR))
- DO ERRX("ESC",C)
- +12 QUIT C
- +13 ;
- ERRX(ID,VAL) ; Set the appropriate error message
- +1 DO ERRX^VPRJSON(ID,$GET(VAL))
- +2 QUIT