Skip to content
Irwin1985Public

About

JSON parser and serializer for VFP in a single PRG

Topics

Resources

Stars

68 stars

Watchers

20 watching

Forks

Repository files navigation

JSONFox

JSONFox is a free JSON / XML parser for Visual FoxPro 9.0

Formas de apoyar:

  1. Donativo en Paypal DONATE!

  2. Patrocinio en Patreon

  3. Seguir este proyecto (es gratis) Stargazer

    Gracias por tu apoyo!

Project Manager

Irwin Rodríguez (Toledo, Spain)

New DLL JSONFoxHelper.dll

If you want to speed up the lexing process then you should use the JSONFoxHelper dll built in C# that scans the tokens faster. In order to use this scanner instead of the native one you just need to activate the NetScanner property like the example below:

// activate the property before using any routine from json class.
_Screen.Json.netScanner = .T.
_Screen.Json.Parse(myJSONString)

How fast is the scanning process against the native way?

I did a timing parsing a json file which size was 8mb. The native scanner lasts more than 15 minutes and the new scanner just 93 seconds. Pretty amazing isn't it?

NOTE

Remember register the JSONFoxHelper.dll before using it. Check this article if you got stuck: https://stackoverflow.com/questions/7092553/turn-a-simple-c-sharp-dll-into-a-com-interop-component

What's new in Version 9

  1. Faster Lexical Analizer
  • I've optimised the string recogniser which is faster now.
  • Special characters and Hex values should be faster
  1. Faster Parser
  • The whole algorith has been rewriten and now creates a real array instead an object.
  1. Faster DATE and DATETIME recogniser.
  2. Faster JSON to cursor conversion
  • Now it used sorted dictionaries instead of plain arrays

Basic Usage

* Now you can use JSONFox as a compiled App...
 Do LocFile("JSONFox", "app")

* Parse a string into an object.
 MyObj = _Screen.Json.Parse('{"foo": "bar"}')
 ?MyObj.foo

**New** you may also parse any valid JSON string.
 ?_Screen.Json.Parse('"bar"') && bar
 ?_Screen.Json.Parse('true')  && .T.
 ?_Screen.Json.Parse('false') && .F.
 ?_Screen.Json.Parse('null')  && .NULL.
 ?_Screen.Json.Parse('1985')  && 1985
 
* Convert Cursor into JSON string.
Create Cursor cGames (game c(25), launched i(4))
Insert into cGames Values('Pac-Man', 1980)
Insert into cGames Values('Super Mario Bros', 1985)
Insert into cGames Values('Space Invaders', 1978)
Insert into cGames Values('The Legend of Zelda', 1986)

?_Screen.Json.CursorToJson('cGames')

* Convert any cursor structure into JSON
?_Screen.Json.CursorStructure('cGames')

Self-contained PRG/FXP

If you prefer a lightweight, silent solution, use JsonFox.fxp. A single compiled file containing the entire JSON pipeline — no external dependencies, no _Screen pollution, no DLL registration needed.

Why it exists: perfect for API integrations where you need to parse/stringify JSON in the background — inside a stored procedure, a VFP service, or any context where loading a full APP is overkill.

* Instantiate — one file, nothing else
oJson = NEWOBJECT("JSONFox", "JsonFox.fxp")

* ── Same API as the APP version ──

* Parse
loObj   = oJson.Parse('{"name":"JSONFox","version":13}')
?loObj.name        && JSONFox
?loObj.version     && 13

* Root values
?oJson.Parse('true')     && .T.
?oJson.Parse('null')     && .NULL.
?oJson.Parse('"Hello"')  && Hello
?oJson.Parse('2025')     && 2025

* Stringify (pretty-print)
?oJson.Stringify('{"compact":true,"items":[1,2,3]}')

* Serialize a VFP object to JSON
loVfp = CREATEOBJECT("Empty")
ADDPROPERTY(loVfp, "lang", "VFP")
ADDPROPERTY(loVfp, "year", 1992)
?oJson.Stringify(@loVfp)

* Cursor → JSON
CREATE CURSOR qProds (id I, name C(20))
INSERT INTO qProds VALUES (1, "Widget")
INSERT INTO qProds VALUES (2, "Gadget")
?oJson.CursorToJSON("qProds")

* JSON array → Cursor
oJson.JSONToCursor('[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]', "qUsers")

* Error handling (silent, no UI prompts)
IF oJson.lError
    ?oJson.cLastError
ENDIF

* Multiple independent instances (each with its own datasession)
oApi1 = NEWOBJECT("JSONFox", "JsonFox.fxp")
oApi2 = NEWOBJECT("JSONFox", "JsonFox.fxp")

Full API: Parse, Stringify, CursorToJSON, JSONToCursor, CursorToJSONObject, CursorStructure, MasterDetailToJSON, ArrayToXML, XMLToJson.

Properties: lError, cLastError, UseArrayObjects (.T. by default), version.


Full Documentation

  • _Screen.Json.CursorToJSON(tcCursor As String [, tbCurrentRow, tnDataSession, tbJustArray, tbParseUTF8, tbTrimChars])
  • tcCursor: the name of your cursor.
  • tbCurrentRow: ¿Would you like to serialize the current row? .F. as default.
  • tnDataSession: Provide this parameter if you're working in a private session.
  • tbJustArray: if is set to .T. then you get just an array with the data, otherwise you'll get an object containing both the cursor name and the array data.
  • tbParseUTF8: kept for compatibility. Since 13.2 every character outside printable ASCII is written as \uXXXX of its Unicode code point, whatever this parameter says. Eg: 'é' => '\u00E9', '€' => '\u20AC'
  • tbTrimChars: if is set to .T. then all right blank spaces will be trimed.

Field types in CursorToJSON and JSONToCursor (13.2)

Field CursorToJSON writes JSONToCursor reads it back as
C, V, M string; non-ASCII as \uXXXX of the Unicode character (€ is \u20AC), control characters as \u00XX C (M past 254)
N, F, Y number with the field's decimals (1234.50) N(w,d) when every row has the same decimals
I integer (42) I (N(w,0) past 2147483647)
B the shortest decimal that reads back as the same Double (0.30000000000000004, 0.3333333333333333) B, bit for bit
D, T ISO 8601 ("1990-05-17", "2026-10-30T10:20:30"); empty: null D, T
L true / false L
Q, W base64 ("QUIAQw==") C with the base64 text: decode it with STRCONV(x, 14)
NULL null NULL

JSONToCursor puts a number in a B field when it has an exponent (1e-7), more than 15 significant digits or more than 20 characters, and when the rows of a column have different decimals (3.14159 and 0). So CursorToJSON → JSONToCursor → CursorToJSON gives the same JSON. The conversion to and from \uXXXX uses CPCURRENT(); a character the code page does not have comes back as ?.

ES. Desde la 13.2, CursorToJSON escribe Varbinary y Blob en base64, cada carácter fuera del ASCII como \uXXXX de su código Unicode (el € es \u20AC) y un Double con el decimal más corto que vuelve al mismo Double. JSONToCursor manda a un campo B los números con exponente, con más de 15 cifras significativas o de más de 20 de ancho, y las columnas con decimales distintos en cada fila: la ida y la vuelta dan el mismo JSON.

DE. Seit 13.2 schreibt CursorToJSON Varbinary und Blob als Base64, jedes Zeichen außerhalb von ASCII als \uXXXX seines Unicode-Codepoints (das € ist \u20AC) und ein Double mit der kürzesten Dezimalzahl, die wieder denselben Double ergibt. JSONToCursor legt Zahlen mit Exponent, mit mehr als 15 signifikanten Stellen oder mehr als 20 Zeichen sowie Spalten mit unterschiedlichen Nachkommastellen in einem B-Feld an: Hin- und Rückweg ergeben dasselbe JSON.


Properties

  • LastErrorText: Stores the possible error generated in the current sentence.

Methods


Examples

 * Sample 1: Serialize JSON String
 Do LocFile("JSONFox", "app")
 * Parse from string
 Text To lcJsonStr NoShow
   {
    "name":"Irwin",
    "surname": "Rodriguez",
    "birth":"1985-11-15",
    "current_year": 2019,
    "wife": "Serelys Fonseca",
    "music_band":"The Beatles",
    "size": 1.79,
    "isgamer": false,
    "isprogrammer": true, 
    "hascar": null
   }
 EndText
 obj = _Screen.Json.Parse(lcJsonStr)
 
 * Don't forget check the LastErrorText
 If _Screen.Json.lError
   MessageBox(_Screen.Json.LastErrorText, 48, "Something went wrong")
   Return
 EndIf
 
 ?obj.name
 ?obj.size
 
 * Deserialize and Indent
 cJSONStr = _Screen.Json.Stringify(obj)
 ?cJSONStr
 
 * Sample 2: JSONArray to Cursor
 Text To lcStr NoShow
	 {
	  "status": "success",
	  "data": [
	    {
	      "id": 2,
	      "email": "rodriguez.irwin@gmail.com",
	      "name": "Irwin1985",
	      "lastName": "Rodriguez",
	      "sex": "1",
	      "salary": 2278.45,
	      "profesion_id": 1,
	      "birthDate": "1985-11-15",
	      "createdAt": "2019-03-31",
	      "single": true
	    },
      {
          "id": 3,
          "email": "oscaraguero85@gmail.com",
          "name": "oscar",
          "lastName": "aguero",
          "sex": "1",
          "salary": 1500.45,
          "profesion_id": 1,
          "birthDate": "1985-06-18",
          "createdAt": "2021-10-11",
          "single": true
      }      
	  ],
	  "code": 200,
	  "message": "success"
	}
ENDTEXT

obj = _Screen.Json.Parse(lcStr)

* Make a copy of the internal array from obj.data
Acopy(obj.data, aEmployeeList)

* Now pass the aEmployeeList by reference with '@'
lcJsonArray = _Screen.Json.Encode(@aEmployeeList)

* Convert the JSONArray into VFP CURSOR **(this is cool)**
_Screen.Json.JSONToCursor(lcJsonArray, "qEmployees")

* Modify some fields
Select qEmployees
Replace salary With 5.000 In qEmployees

* Now serialize the cursor to JSON
cJson = _Screen.json.CursorToJson("qEmployees")
?cJson

* Sample 3: Stringify Example
cJson = '{"age":45,"birthdate":"1985-11-15","created":"2020-07-28 09:29:41 PM","fullname":"Jhon Doe","gender":"Male","married":true,"soports":["running","swiming","basket-ball"]}'
?_Screen.Json.Stringify(cJson)

CursorStructure() function

Use Home(2) + "NorthWind\Customers.dbf"
?_Screen.Json.CursorStructure('Customers', Set("Datasession"), .T.)
// Function Signature
// CursorStructure(tcAlias, tnDataSessionID, tlCopyExtended)
// 1. tcAlias is the alias given.
// 2. tnDataSessionID is the current Datasession where Alias() lives (for private data sessions).
// 3. tlCopyExtended if .F. then copy FIELD_NAME, FIELD_TYPE, FIELD_LENGTH and FIELD_DECIMAL_PLACES. 
//    if is .T. then parse all the alias data structure.

JSONViewer() function

 Text To lcStr NoShow
  {
    "array": [
      1,
      2,
      3
    ],
    "boolean": false,
    "color": "gold",
    "null": null,
    "number": 123,
    "object": null,
    "string": "Hello World"
  }
ENDTEXT
_Screen.Json.JSONViewer(lcStr)

SIN GARANTÍA

EL SOFTWARE SE PROPORCIONA "TAL CUAL", SIN GARANTÍA DE NINGÚN TIPO, EXPRESA O IMPLÍCITA, INCLUYENDO PERO NO LIMITADO A LAS GARANTÍAS DE COMERCIABILIDAD, IDONEIDAD PARA UN PROPÓSITO PARTICULAR Y NO INFRACCIÓN. EN NINGÚN CASO LOS AUTORES O TITULARES DE DERECHOS DE AUTOR SERÁN RESPONSABLES DE NINGÚN RECLAMO, DAÑO U OTRA RESPONSABILIDAD, YA SEA EN UNA ACCIÓN CONTRACTUAL, AGRAVIO O DE OTRA MANERA, QUE SURJA DE, FUERA DE O EN RELACIÓN CON EL SOFTWARE O EL USO U OTRAS NEGOCIOS EN EL SOFTWARE.

License

JSONFox is released under the MIT License.

About

JSON parser and serializer for VFP in a single PRG

Topics

Resources

Stars

68 stars

Watchers

20 watching

Forks

Releases

Packages

Used by

Contributors

Languages