FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Clase tJson
Posts: 199
Joined: Fri Apr 18, 2008 04:21 PM
Clase tJson
Posted: Sat Mar 21, 2026 04:02 AM

Hola a todos,
Quiero compartir con la comunidad una clase que desarrollé llamada tJson, orientada a facilitar el trabajo con JSON directamente desde FiveWin, sin depender de librerías externas ni DLLs adicionales.
¿Qué hace?

Parsea JSON desde un string o desde un archivo, convirtiéndolo en estructuras nativas de Harbour (arrays, hashes, valores escalares)
Genera/serializa JSON a partir de datos Harbour de forma sencilla
Mapea JSON a objetos o arrays de Harbour para facilitar su manipulación en el código

¿Por qué la desarrollé?
En varios proyectos necesitaba consumir y generar JSON para integraciones con servicios externos (APIs REST, FEL, etc.) y quería algo que funcionara tanto en xHarbour como en Harbour, sin complicaciones de compatibilidad ni dependencias adicionales que hubiera que distribuir con el ejecutable.
Características principales:

Sintaxis simple e intuitiva, fácil de integrar en proyectos existentes
Compatible con xHarbour y Harbour
Sin dependencias externas – no requiere DLLs adicionales

https://cgtecsa.com/Proyectos/tJson.zip dejo la documentación y los fuentes en este link para que lo puedan descargar.

Hice unas mejoras a la clase tArray y la clase que uso para conexion a Mysql es Eagle, por lo que los ejemplos o la construccion de las nuevas funciones se basan en esta clase.

Espero que le sea de utilidad a alguien más en la comunidad. Cualquier sugerencia, mejora o reporte de bug es bienvenido.
Saludos desde Guatemala 🇬🇹

Posts: 476
Joined: Sat Feb 03, 2007 06:36 AM
Re: Clase tJson
Posted: Sun Mar 22, 2026 12:59 PM

Estimado Julio, gracias por tu aporte!

Carlos.

Posts: 537
Joined: Mon Jan 16, 2006 03:42 PM
Re: Clase tJson
Posted: Mon Mar 23, 2026 06:10 PM

Muchas gracias

Posts: 1144
Joined: Mon Feb 05, 2007 07:15 PM
Re: Clase tJson
Posted: Mon Mar 23, 2026 07:08 PM

Hay que probar, gracias!!

Cesar Cortes Cruz

SysCtrl Software

Mexico



' Sin +- FWH es mejor "
Posts: 1144
Joined: Mon Feb 05, 2007 07:15 PM
Re: Clase tJson
Posted: Mon Mar 23, 2026 07:13 PM

una pregunta porqué no usas:

     hData := hb_jsondecode( oResp:BodyStr )
     xbrowser( hData )

Saludos!

Cesar Cortes Cruz

SysCtrl Software

Mexico



' Sin +- FWH es mejor "
Posts: 670
Joined: Wed Oct 19, 2005 06:41 PM
Re: Clase tJson
Posted: Mon Mar 23, 2026 09:41 PM

Cesar buenas tardes en xHarbour creo que no hay esas funciones saludos wilson

Wilson 'W' Gamboa A
Wilson.josenet@gmail.com
Posts: 9020
Joined: Thu Oct 06, 2005 08:17 PM
Re: Clase tJson
Posted: Tue Mar 24, 2026 10:13 PM

Yes, xHarbour has the function HB_JSONDECODE().

Posts: 670
Joined: Wed Oct 19, 2005 06:41 PM
Re: Clase tJson
Posted: Thu Mar 26, 2026 04:37 AM

Dear Enrico I use this version
Copyright (C) 2000-2005 xHarbour project - http://www.xharbour.org
xHarbour build 0.99.51 Intl. (SimpLex)
is for an big big and stable program realy work well i never upgrade this version because de tbrowse object not work the same
in this version not exist this function hb_jsonxxxx() for this I Thing this class is an Joy
regards
Wilson

Wilson 'W' Gamboa A
Wilson.josenet@gmail.com
Posts: 670
Joined: Wed Oct 19, 2005 06:41 PM
Re: Clase tJson
Posted: Thu Mar 26, 2026 05:22 AM

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 ================= */
Wilson 'W' Gamboa A
Wilson.josenet@gmail.com
Posts: 9020
Joined: Thu Oct 06, 2005 08:17 PM
Re: Clase tJson
Posted: Thu Mar 26, 2026 08:31 AM
wilsongamboa wrote:

Dear Enrico I use this version
Copyright (C) 2000-2005 xHarbour project - http://www.xharbour.org
xHarbour build 0.99.51 Intl. (SimpLex)

You should absolutely upgrade to the last version. You can't seriously use such an older xHarbour release!

Posts: 670
Joined: Wed Oct 19, 2005 06:41 PM
Re: Clase tJson
Posted: Thu Mar 26, 2026 03:18 PM

Enrico the big program works well is an console app works with 300 + users cocurrentli when i try to upgrade appears bugs in much places and imposible to try to fix best regards Wilson

Wilson 'W' Gamboa A
Wilson.josenet@gmail.com

Continue the discussion