HMPJSONE ;SLC/KCM,ASMR/RRB - Encode JSON;9/25/2015 10:17
 ;;2.0;ENTERPRISE HEALTH MANAGEMENT PLATFORM;**;Sep 01, 2011;Build 63
 ;Per VA Directive 6402, this routine should not be modified.
 ;
 Q
 ;
ENCODE(VVROOT,VVJSON,VVERR) ; VVROOT (M structure) --> VVJSON (array of strings)
 ;
DIRECT ; TAG for use by ENCODE^HMPJSON
 ;
 ; Examples:  D ENCODE^HMPJSON("^GLO(99,2)","^TMP($J)")
 ;            D ENCODE^HMPJSON("LOCALVAR","MYJSON","LOCALERR")
 ;
 ; VVROOT: closed array reference for M representation of object
 ; VVJSON: destination variable for the string array formatted as JSON
 ;  VVERR: contains error messages, defaults to ^TMP("HMPJERR",$J)
 ;
 S VVERR=$G(VVERR,"^TMP(""HMPJERR"",$J)")
 I '$L($G(VVROOT)) ; set error info
 I '$L($G(VVJSON)) ; set error info
 N VVLINE,VVMAX,VVERRORS
 S VVLINE=1,VVMAX=4000,VVERRORS=0  ; 96 more bytes of wiggle room
 S @VVJSON@(VVLINE)=""
 D SEROBJ(VVROOT)
 Q
 ;
SEROBJ(VVROOT) ; Serialize into a JSON object
 N VVFIRST,VVSUB,VVNXT
 S @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"{"
 S VVFIRST=1
 S VVSUB="" F  S VVSUB=$O(@VVROOT@(VVSUB)) Q:VVSUB=""  D
 . S:'VVFIRST @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"," S VVFIRST=0
 . ; get the name part
 . D SERNAME(VVSUB)
 . ; if this is a value, serialize it
 . I $$ISVALUE(VVROOT,VVSUB) D SERVAL(VVROOT,VVSUB) Q
 . ; otherwise navigate to the next child object or array
 . I $D(@VVROOT@(VVSUB))=10 S VVNXT=$O(@VVROOT@(VVSUB,"")) D  Q
 . . ; Need to check if numeric representation matches string representation to decide if it is an array
 . . I +VVNXT=VVNXT D SERARY($NA(@VVROOT@(VVSUB))) I 1
 . . E  D SEROBJ($NA(@VVROOT@(VVSUB)))
 . D ERRX("SOB",VVSUB)  ; should quit loop before here
 S @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"}"
 Q
SERARY(VVROOT) ; Serialize into a JSON array
 N VVFIRST,VVI,VVNXT
 S @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"["
 S VVFIRST=1
 S VVI=0 F  S VVI=$O(@VVROOT@(VVI)) Q:'VVI  D
 . S:'VVFIRST @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"," S VVFIRST=0
 . I $$ISVALUE(VVROOT,VVI) D SERVAL(VVROOT,VVI) Q  ; write value
 . I $D(@VVROOT@(VVI))=10 S VVNXT=$O(@VVROOT@(VVI,"")) D  Q
 . . ; Need to check if numeric representation matches string representation to decide if it is an array
 . . I +VVNXT=VVNXT D SERARY($NA(@VVROOT@(VVI))) I 1
 . . E  D SEROBJ($NA(@VVROOT@(VVI)))
 . D ERRX("SAR",VVI)  ; should quit loop before here
 S @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"]"
 Q
SERNAME(VVSUB) ; Serialize the object name into JSON string
 I $E(VVSUB)="""" S VVSUB=$E(VVSUB,2,$L(VVSUB)) ; quote indicates numeric label
 I ($L(VVSUB)+$L(@VVJSON@(VVLINE)))>VVMAX S VVLINE=VVLINE+1,@VVJSON@(VVLINE)=""
 S @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_""""_VVSUB_""""_":"
 Q
SERVAL(VVROOT,VVSUB) ; Serialize X into appropriate JSON representation
 N VVX,VVI,VVDONE
 ; if the node is already in JSON format, just add it
 I $D(@VVROOT@(VVSUB,":")) D  QUIT  ; <-- jump out here if preformatted
 . S VVX=$G(@VVROOT@(VVSUB,":")) D:$L(VVX) CONCAT
 . S VVI=0 F  S VVI=$O(@VVROOT@(VVSUB,":",VVI)) Q:'VVI  S VVX=@VVROOT@(VVSUB,":",VVI) D CONCAT
 ;
 S VVX=$G(@VVROOT@(VVSUB)),VVDONE=0
 ; handle the numeric, boolean, and null types
 I $D(@VVROOT@(VVSUB,"\n")) S:$L(@VVROOT@(VVSUB,"\n")) VVX=@VVROOT@(VVSUB,"\n") D CONCAT QUIT  ; when +X'=X
 I '$D(@VVROOT@(VVSUB,"\s")),$L(VVX) D  QUIT:VVDONE
 . I VVX']]$C(1) S VVX=$$JNUM(VVX) D CONCAT S VVDONE=1 QUIT
 . I VVX="true"!(VVX="false")!(VVX="null") D CONCAT S VVDONE=1 QUIT
 ; otherwise treat it as a string type
 S VVX=""""_$$ESC(VVX) ; open quote
 D CONCAT
 I $D(@VVROOT@(VVSUB,"\")) D  ; handle continuation nodes
 . S VVI=0 F  S VVI=$O(@VVROOT@(VVSUB,"\",VVI)) Q:'VVI   D
 . . S VVX=$$ESC(@VVROOT@(VVSUB,"\",VVI))
 . . D CONCAT
 S VVX="""" D CONCAT    ; close quote
 Q
CONCAT ; come here to concatenate to JSON string
 I ($L(VVX)+$L(@VVJSON@(VVLINE)))>VVMAX S VVLINE=VVLINE+1,@VVJSON@(VVLINE)=""
 S @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_VVX
 Q
ISVALUE(VVROOT,VVSUB) ; Return true if this is a value node
 I $D(@VVROOT@(VVSUB))#2 Q 1
 N VVX S VVX=$O(@VVROOT@(VVSUB,""))
 Q:VVX="\" 1  ; word processing continuation node
 Q:VVX=":" 1  ; pre-formatted JSON node
 Q 0
 ;
NUMERIC(X) ; Return true if the numeric
 I $L(X)>18 Q 0        ; string (too long for numeric)
 I X=0 Q 1             ; numeric (value is zero)
 I +X=0 Q 0            ; string
 I $E(X,1)="." Q 0     ; not a JSON number (although numeric in M)
 I $E(X,1,2)="-." Q 0  ; not a JSON number
 I +X=X Q 1            ; numeric
 I X?1"0."1.n Q 1      ; positive fraction
 I X?1"-0."1.N Q 1     ; negative fraction
 S X=$TR(X,"e","E")
 I X?.1"-"1.N.1".".N1"E".1"+"1.N Q 1  ; {-}99{.99}E{+}99
 I X?.1"-"1.N.1".".N1"E-"1.N Q 1      ; {-}99{.99}E-99
 Q 0
 ;
ESC(X) ; Escape string for JSON
 N Y,I,PAIR,FROM,TO
 S Y=X
 F PAIR="\\","""""","//",$C(8,98),$C(12,102),$C(10,110),$C(13,114),$C(9,116) D
 . S FROM=$E(PAIR),TO=$E(PAIR,2)
 . S X=Y,Y=$P(X,FROM) F I=2:1:$L(X,FROM) S Y=Y_"\"_TO_$P(X,FROM,I)
 I Y?.E1.C.E S X=Y,Y="" F I=1:1:$L(X) S FROM=$A(X,I) D
 . ; skip NUL character, otherwise encode ctrl-char
 . I FROM<32 Q:FROM=0  S Y=Y_$$UCODE(FROM) Q
 . I FROM>126,(FROM<160) S Y=Y_$$UCODE(FROM) Q
 . S Y=Y_$E(X,I)
 Q Y
 ;
JNUM(N) ; Return JSON representation of a number
 I N'<1 Q N
 I N'>-1 Q N
 I N>0 Q "0"_N
 I N<0 Q "-0"_$P(N,"-",2,9)
 Q N
 ;
UCODE(C) ; Return \u00nn representation of decimal character value
 N H S H="0000"_$$CNV^XLFUTL(C,16)
 Q "\u"_$E(H,$L(H)-3,$L(H))
 ;
ERRX(ID,VAL) ; Set the appropriate error message
 D ERRX^HMPJSON(ID,$G(VAL))
 Q
 
--- Routine Detail   --- with STRUCTURED ROUTINE LISTING ---[H[J[2J[HHMPJSONE   5540     printed  Sep 23, 2025@19:30:07                                                                                                                                                                                                    Page 2
HMPJSONE  ;SLC/KCM,ASMR/RRB - Encode JSON;9/25/2015 10:17
 +1       ;;2.0;ENTERPRISE HEALTH MANAGEMENT PLATFORM;**;Sep 01, 2011;Build 63
 +2       ;Per VA Directive 6402, this routine should not be modified.
 +3       ;
 +4        QUIT 
 +5       ;
ENCODE(VVROOT,VVJSON,VVERR) ; VVROOT (M structure) --> VVJSON (array of strings)
 +1       ;
DIRECT    ; TAG for use by ENCODE^HMPJSON
 +1       ;
 +2       ; Examples:  D ENCODE^HMPJSON("^GLO(99,2)","^TMP($J)")
 +3       ;            D ENCODE^HMPJSON("LOCALVAR","MYJSON","LOCALERR")
 +4       ;
 +5       ; VVROOT: closed array reference for M representation of object
 +6       ; VVJSON: destination variable for the string array formatted as JSON
 +7       ;  VVERR: contains error messages, defaults to ^TMP("HMPJERR",$J)
 +8       ;
 +9        SET VVERR=$GET(VVERR,"^TMP(""HMPJERR"",$J)")
 +10      ; set error info
           IF '$LENGTH($GET(VVROOT))
 +11      ; set error info
           IF '$LENGTH($GET(VVJSON))
 +12       NEW VVLINE,VVMAX,VVERRORS
 +13      ; 96 more bytes of wiggle room
           SET VVLINE=1
           SET VVMAX=4000
           SET VVERRORS=0
 +14       SET @VVJSON@(VVLINE)=""
 +15       DO SEROBJ(VVROOT)
 +16       QUIT 
 +17      ;
SEROBJ(VVROOT) ; Serialize into a JSON object
 +1        NEW VVFIRST,VVSUB,VVNXT
 +2        SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"{"
 +3        SET VVFIRST=1
 +4        SET VVSUB=""
           FOR 
               SET VVSUB=$ORDER(@VVROOT@(VVSUB))
               if VVSUB=""
                   QUIT 
               Begin DoDot:1
 +5                if 'VVFIRST
                       SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_","
                   SET VVFIRST=0
 +6       ; get the name part
 +7                DO SERNAME(VVSUB)
 +8       ; if this is a value, serialize it
 +9                IF $$ISVALUE(VVROOT,VVSUB)
                       DO SERVAL(VVROOT,VVSUB)
                       QUIT 
 +10      ; otherwise navigate to the next child object or array
 +11               IF $DATA(@VVROOT@(VVSUB))=10
                       SET VVNXT=$ORDER(@VVROOT@(VVSUB,""))
                       Begin DoDot:2
 +12      ; Need to check if numeric representation matches string representation to decide if it is an array
 +13                       IF +VVNXT=VVNXT
                               DO SERARY($NAME(@VVROOT@(VVSUB)))
                               IF 1
 +14                      IF '$TEST
                               DO SEROBJ($NAME(@VVROOT@(VVSUB)))
                       End DoDot:2
                       QUIT 
 +15      ; should quit loop before here
                   DO ERRX("SOB",VVSUB)
               End DoDot:1
 +16       SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"}"
 +17       QUIT 
SERARY(VVROOT) ; Serialize into a JSON array
 +1        NEW VVFIRST,VVI,VVNXT
 +2        SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"["
 +3        SET VVFIRST=1
 +4        SET VVI=0
           FOR 
               SET VVI=$ORDER(@VVROOT@(VVI))
               if 'VVI
                   QUIT 
               Begin DoDot:1
 +5                if 'VVFIRST
                       SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_","
                   SET VVFIRST=0
 +6       ; write value
                   IF $$ISVALUE(VVROOT,VVI)
                       DO SERVAL(VVROOT,VVI)
                       QUIT 
 +7                IF $DATA(@VVROOT@(VVI))=10
                       SET VVNXT=$ORDER(@VVROOT@(VVI,""))
                       Begin DoDot:2
 +8       ; Need to check if numeric representation matches string representation to decide if it is an array
 +9                        IF +VVNXT=VVNXT
                               DO SERARY($NAME(@VVROOT@(VVI)))
                               IF 1
 +10                      IF '$TEST
                               DO SEROBJ($NAME(@VVROOT@(VVI)))
                       End DoDot:2
                       QUIT 
 +11      ; should quit loop before here
                   DO ERRX("SAR",VVI)
               End DoDot:1
 +12       SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_"]"
 +13       QUIT 
SERNAME(VVSUB) ; Serialize the object name into JSON string
 +1       ; quote indicates numeric label
           IF $EXTRACT(VVSUB)=""""
               SET VVSUB=$EXTRACT(VVSUB,2,$LENGTH(VVSUB))
 +2        IF ($LENGTH(VVSUB)+$LENGTH(@VVJSON@(VVLINE)))>VVMAX
               SET VVLINE=VVLINE+1
               SET @VVJSON@(VVLINE)=""
 +3        SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_""""_VVSUB_""""_":"
 +4        QUIT 
SERVAL(VVROOT,VVSUB) ; Serialize X into appropriate JSON representation
 +1        NEW VVX,VVI,VVDONE
 +2       ; if the node is already in JSON format, just add it
 +3       ; <-- jump out here if preformatted
           IF $DATA(@VVROOT@(VVSUB,":"))
               Begin DoDot:1
 +4                SET VVX=$GET(@VVROOT@(VVSUB,":"))
                   if $LENGTH(VVX)
                       DO CONCAT
 +5                SET VVI=0
                   FOR 
                       SET VVI=$ORDER(@VVROOT@(VVSUB,":",VVI))
                       if 'VVI
                           QUIT 
                       SET VVX=@VVROOT@(VVSUB,":",VVI)
                       DO CONCAT
               End DoDot:1
               QUIT 
 +6       ;
 +7        SET VVX=$GET(@VVROOT@(VVSUB))
           SET VVDONE=0
 +8       ; handle the numeric, boolean, and null types
 +9       ; when +X'=X
           IF $DATA(@VVROOT@(VVSUB,"\n"))
               if $LENGTH(@VVROOT@(VVSUB,"\n"))
                   SET VVX=@VVROOT@(VVSUB,"\n")
               DO CONCAT
               QUIT 
 +10       IF '$DATA(@VVROOT@(VVSUB,"\s"))
               IF $LENGTH(VVX)
                   Begin DoDot:1
 +11                   IF VVX']]$CHAR(1)
                           SET VVX=$$JNUM(VVX)
                           DO CONCAT
                           SET VVDONE=1
                           QUIT 
 +12                   IF VVX="true"!(VVX="false")!(VVX="null")
                           DO CONCAT
                           SET VVDONE=1
                           QUIT 
                   End DoDot:1
                   if VVDONE
                       QUIT 
 +13      ; otherwise treat it as a string type
 +14      ; open quote
           SET VVX=""""_$$ESC(VVX)
 +15       DO CONCAT
 +16      ; handle continuation nodes
           IF $DATA(@VVROOT@(VVSUB,"\"))
               Begin DoDot:1
 +17               SET VVI=0
                   FOR 
                       SET VVI=$ORDER(@VVROOT@(VVSUB,"\",VVI))
                       if 'VVI
                           QUIT 
                       Begin DoDot:2
 +18                       SET VVX=$$ESC(@VVROOT@(VVSUB,"\",VVI))
 +19                       DO CONCAT
                       End DoDot:2
               End DoDot:1
 +20      ; close quote
           SET VVX=""""
           DO CONCAT
 +21       QUIT 
CONCAT    ; come here to concatenate to JSON string
 +1        IF ($LENGTH(VVX)+$LENGTH(@VVJSON@(VVLINE)))>VVMAX
               SET VVLINE=VVLINE+1
               SET @VVJSON@(VVLINE)=""
 +2        SET @VVJSON@(VVLINE)=@VVJSON@(VVLINE)_VVX
 +3        QUIT 
ISVALUE(VVROOT,VVSUB) ; Return true if this is a value node
 +1        IF $DATA(@VVROOT@(VVSUB))#2
               QUIT 1
 +2        NEW VVX
           SET VVX=$ORDER(@VVROOT@(VVSUB,""))
 +3       ; word processing continuation node
           if VVX="\"
               QUIT 1
 +4       ; pre-formatted JSON node
           if VVX="
               QUIT 1
 +5        QUIT 0
 +6       ;
NUMERIC(X) ; Return true if the numeric
 +1       ; string (too long for numeric)
           IF $LENGTH(X)>18
               QUIT 0
 +2       ; numeric (value is zero)
           IF X=0
               QUIT 1
 +3       ; string
           IF +X=0
               QUIT 0
 +4       ; not a JSON number (although numeric in M)
           IF $EXTRACT(X,1)="."
               QUIT 0
 +5       ; not a JSON number
           IF $EXTRACT(X,1,2)="-."
               QUIT 0
 +6       ; numeric
           IF +X=X
               QUIT 1
 +7       ; positive fraction
           IF X?1"0."1.n
               QUIT 1
 +8       ; negative fraction
           IF X?1"-0."1.N
               QUIT 1
 +9        SET X=$TRANSLATE(X,"e","E")
 +10      ; {-}99{.99}E{+}99
           IF X?.1"-"1.N.1".".N1"E".1"+"1.N
               QUIT 1
 +11      ; {-}99{.99}E-99
           IF X?.1"-"1.N.1".".N1"E-"1.N
               QUIT 1
 +12       QUIT 0
 +13      ;
ESC(X)    ; Escape string for JSON
 +1        NEW Y,I,PAIR,FROM,TO
 +2        SET Y=X
 +3        FOR PAIR="\\","""""","//",$CHAR(8,98),$CHAR(12,102),$CHAR(10,110),$CHAR(13,114),$CHAR(9,116)
               Begin DoDot:1
 +4                SET FROM=$EXTRACT(PAIR)
                   SET TO=$EXTRACT(PAIR,2)
 +5                SET X=Y
                   SET Y=$PIECE(X,FROM)
                   FOR I=2:1:$LENGTH(X,FROM)
                       SET Y=Y_"\"_TO_$PIECE(X,FROM,I)
               End DoDot:1
 +6        IF Y?.E1.C.E
               SET X=Y
               SET Y=""
               FOR I=1:1:$LENGTH(X)
                   SET FROM=$ASCII(X,I)
                   Begin DoDot:1
 +7       ; skip NUL character, otherwise encode ctrl-char
 +8                    IF FROM<32
                           if FROM=0
                               QUIT 
                           SET Y=Y_$$UCODE(FROM)
                           QUIT 
 +9                    IF FROM>126
                           IF (FROM<160)
                               SET Y=Y_$$UCODE(FROM)
                               QUIT 
 +10                   SET Y=Y_$EXTRACT(X,I)
                   End DoDot:1
 +11       QUIT Y
 +12      ;
JNUM(N)   ; Return JSON representation of a number
 +1        IF N'<1
               QUIT N
 +2        IF N'>-1
               QUIT N
 +3        IF N>0
               QUIT "0"_N
 +4        IF N<0
               QUIT "-0"_$PIECE(N,"-",2,9)
 +5        QUIT N
 +6       ;
UCODE(C)  ; Return \u00nn representation of decimal character value
 +1        NEW H
           SET H="0000"_$$CNV^XLFUTL(C,16)
 +2        QUIT "\u"_$EXTRACT(H,$LENGTH(H)-3,$LENGTH(H))
 +3       ;
ERRX(ID,VAL) ; Set the appropriate error message
 +1        DO ERRX^HMPJSON(ID,$GET(VAL))
 +2        QUIT