usando la ia acabo de crear estas funciones por si a alguien le sirve, lo poco que probe esta OK
PROCEDURE TestJSON()
LOCAL h2
local cJson
LOCAL h := {=>}
h["nombre"] := "Juan"
h["edad"] := 30
h["activo"] := .T.
h["emoji"] := "\u263A"
h["items"] := { "uno", "dos", 123 }
cJson := wg_hb_jsonEncode( h )
? "JSON:", cJson
c = '{"factura":{"numero":"FAC-20260321-0654","fecha_emision":"2026-03-21T21:55:54.790Z","cliente":{"documento":"1600167793","tipo_documento":"CEDULA","nombres":"GAMBOA WILSON","telefono":"099474062","email":"WILSON.JOSENET@GMAIL.COM","direccion":"QUITO"},"items":[{"cod_prod":"00006","descripcion":"FRITADA LIBRA","cantidad":1,"precio_unitario":9.56,"iva":0.15,"subtotal":9.56,"valor_iva":1.434,"total":10.994}],"subtotal":9.56,"iva":1.434,"total":10.994,"pago":{"metodo":"efectivo","monto":10.994,"fecha_pago":"2026-03-21T21:55:54.790Z"},"establecimiento":"002","punto_emision":"001","secuencial":"025502402"}}'
h2 := wg_hb_jsonDecode( c )
?valtype( h2 )
? wg_hb_jsonEncode( h2 )
RETURN
/* =========================================================
JSON para xHarbour (compatible tipo hb_jsonEncode/Decode)
========================================================= */
FUNCTION wg_hb_jsonEncode( xValue )
LOCAL cType := ValType( xValue )
DO CASE
CASE cType == "C"
RETURN '"' + __jsonEscape( xValue ) + '"'
CASE cType == "N"
RETURN LTrim(Str(xValue))
CASE cType == "L"
RETURN IIF( xValue, "true", "false" )
CASE cType == "U"
RETURN "null"
CASE cType == "A"
RETURN __jsonEncodeArray( xValue )
CASE cType == "H"
RETURN __jsonEncodeHash( xValue )
OTHERWISE
RETURN "null"
ENDCASE
RETURN NIL
/* ================= ENCODE HELPERS ================= */
FUNCTION __jsonEscape( cText )
cText := StrTran( cText, "\", "\\" )
cText := StrTran( cText, '"', '\"' )
cText := StrTran( cText, Chr(10), "\n" )
cText := StrTran( cText, Chr(13), "\r" )
cText := StrTran( cText, Chr(9), "\t" )
RETURN cText
FUNCTION __jsonEncodeArray( aData )
LOCAL i, cJson := "["
FOR i := 1 TO Len(aData)
cJson += hb_jsonEncode( aData[i] )
IF i < Len(aData)
cJson += ","
ENDIF
NEXT
RETURN cJson + "]"
FUNCTION __jsonEncodeHash( hData )
LOCAL aKeys := HB_HKeys( hData )
LOCAL i, k, cJson := "{"
FOR i := 1 TO Len(aKeys)
k := aKeys[i]
cJson += '"' + k + '":' + hb_jsonEncode( hData[k] )
IF i < Len(aKeys)
cJson += ","
ENDIF
NEXT
RETURN cJson + "}"
/* ================= DECODE ================= */
FUNCTION wg_hb_jsonDecode( cJson )
LOCAL nPos := 1
LOCAL xVal := __parseValue( cJson, @nPos )
__skipSpaces( cJson, @nPos )
IF nPos <= Len(cJson)
__jsonError( "Datos extra después del JSON", nPos )
ENDIF
RETURN xVal
/* ================= PARSER ================= */
FUNCTION __parseValue( cJson, nPos )
__skipSpaces( cJson, @nPos )
IF nPos > Len(cJson)
__jsonError( "Fin inesperado", nPos )
ENDIF
DO CASE
CASE SubStr(cJson, nPos, 1) == "{"
RETURN __parseObject( cJson, @nPos )
CASE SubStr(cJson, nPos, 1) == "["
RETURN __parseArray( cJson, @nPos )
CASE SubStr(cJson, nPos, 1) == '"'
RETURN __parseString( cJson, @nPos )
CASE SubStr(cJson, nPos, 4) == "true"
nPos += 4
RETURN .T.
CASE SubStr(cJson, nPos, 5) == "false"
nPos += 5
RETURN .F.
CASE SubStr(cJson, nPos, 4) == "null"
nPos += 4
RETURN NIL
OTHERWISE
RETURN __parseNumber( cJson, @nPos )
ENDCASE
RETURN NIL
/* ================= OBJECT ================= */
FUNCTION __parseObject( cJson, nPos )
LOCAL h := {=>}
LOCAL cKey, xVal
nPos++ // {
__skipSpaces( cJson, @nPos )
IF SubStr(cJson, nPos, 1) == "}"
nPos++
RETURN h
ENDIF
DO WHILE .T.
IF SubStr(cJson, nPos, 1) != '"'
__jsonError( "Se esperaba string como clave", nPos )
ENDIF
cKey := __parseString( cJson, @nPos )
__skipSpaces( cJson, @nPos )
IF SubStr(cJson, nPos, 1) != ":"
__jsonError( "Se esperaba ':'", nPos )
ENDIF
nPos++
xVal := __parseValue( cJson, @nPos )
h[ cKey ] := xVal
__skipSpaces( cJson, @nPos )
DO CASE
CASE SubStr(cJson, nPos, 1) == "}"
nPos++
EXIT
CASE SubStr(cJson, nPos, 1) == ","
nPos++
OTHERWISE
__jsonError( "Se esperaba ',' o '}'", nPos )
ENDCASE
ENDDO
RETURN h
/* ================= ARRAY ================= */
FUNCTION __parseArray( cJson, nPos )
LOCAL a := {}
nPos++ // [
__skipSpaces( cJson, @nPos )
IF SubStr(cJson, nPos, 1) == "]"
nPos++
RETURN a
ENDIF
DO WHILE .T.
AAdd( a, __parseValue( cJson, @nPos ) )
__skipSpaces( cJson, @nPos )
DO CASE
CASE SubStr(cJson, nPos, 1) == "]"
nPos++
EXIT
CASE SubStr(cJson, nPos, 1) == ","
nPos++
OTHERWISE
__jsonError( "Se esperaba ',' o ']'", nPos )
ENDCASE
ENDDO
RETURN a
/* ================= STRING ================= */
FUNCTION __parseString( cJson, nPos )
LOCAL c := "", ch, cHex
nPos++ // "
DO WHILE nPos <= Len(cJson)
ch := SubStr(cJson, nPos, 1)
IF ch == '"'
nPos++
RETURN c
ENDIF
IF ch == "\"
nPos++
ch := SubStr(cJson, nPos, 1)
DO CASE
CASE ch == '"'
c += '"'
CASE ch == "\"
c += "\"
CASE ch == "/"
c += "/"
CASE ch == "n"
c += Chr(10)
CASE ch == "r"
c += Chr(13)
CASE ch == "t"
c += Chr(9)
CASE ch == "u"
cHex := SubStr(cJson, nPos+1, 4)
c += Chr( __hexToDec(cHex) )
nPos += 4
OTHERWISE
__jsonError( "Escape inválido", nPos )
ENDCASE
ELSE
c += ch
ENDIF
nPos++
ENDDO
__jsonError( "String sin cerrar", nPos )
RETURN NIL
/* ================= NUMBER ================= */
FUNCTION __parseNumber( cJson, nPos )
LOCAL nStart := nPos
LOCAL cNum
DO WHILE nPos <= Len(cJson) .AND. ;
SubStr(cJson, nPos, 1) $ "-0123456789.eE"
nPos++
ENDDO
cNum := SubStr( cJson, nStart, nPos - nStart )
IF Empty(cNum)
__jsonError( "Número inválido", nPos )
ENDIF
RETURN Val( cNum )
/* ================= UTILS ================= */
FUNCTION __skipSpaces( cJson, nPos )
DO WHILE nPos <= Len(cJson) .AND. ;
SubStr(cJson, nPos, 1) $ " " + Chr(9) + Chr(10) + Chr(13)
nPos++
ENDDO
RETURN NIL
FUNCTION __hexToDec( cHex )
LOCAL n := 0, i, c
FOR i := 1 TO Len(cHex)
c := Upper(SubStr(cHex,i,1))
n *= 16
IF c >= "0" .AND. c <= "9"
n += Val(c)
ELSE
n += Asc(c) - Asc("A") + 10
ENDIF
NEXT
RETURN n
FUNCTION __jsonError( cMsg, nPos )
? "ERROR JSON:", cMsg, "POS:", nPos
BREAK
RETURN NIL
/* ================= TEST ================= */