Facebook
From Thorvaldsen, 8 Years ago, written in Visual FoxPro.
Embed
Download Paste or View Raw
Hits: 528
  1. *
  2. * vfpjson
  3. *
  4. * ----------------------------------
  5. * Ignacio Gutiérrez Torrero
  6. * SAIT Software Administrativo
  7. * www.sait.com.mx
  8. * +52(653)534-8800
  9. * Monterrey México
  10. * -----------------------------------
  11. *
  12. * Libreria para el manejo de JSON en VFP
  13. *
  14. * Gracias a Google por el codigo de Json de Dart
  15. * Thanks Google for the code in Json Dart
  16. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  17. *
  18. *
  19. * Codificar y Decodificar JSON
  20. *
  21. * Puedes usar las funciones:
  22. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  23. *               json_decode(cJson)  te regresa el objeto representado en cJson
  24. *
  25. * Tambien puedes usar directamente la clase:
  26. *
  27. *       oJson = newobject('json','json.prg')
  28. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  29. *       ? oJson.encode(oCliente)
  30. *       ? oCliente.get('nombre')
  31. *       ? oCliente.get('apellido')
  32. *
  33. *
  34. * VFPJSON  Encode and Decode JSON for VFP
  35. * Examples:
  36. *       oJson = newobject('json','json.prg')
  37. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  38. *       ? oJson.encode(oCustomer)
  39. *       ? oCustomer.get('name')
  40. *       ? oCustomer.get('lastname')
  41. *
  42. *
  43. lRunTest = .f.
  44. if lRunTest
  45.         testJsonClass()
  46. endif
  47. return
  48.  
  49.  
  50. function json_encode(xExpr)
  51.         if vartype(_json)<>'O'
  52.                 public _json
  53.                 _json = newobject('json')
  54.         endif
  55. return _json.encode(@xExpr)
  56.  
  57.  
  58. function json_decode(cJson)
  59. local retval
  60.         if vartype(_json)<>'O'
  61.                 public _json
  62.                 _json = newobject('json')
  63.         endif
  64.         retval = _json.decode(cJson)
  65.         if not empty(_json.cError)
  66.                 return null
  67.         endif
  68. return retval
  69.  
  70. function json_getErrorMsg()
  71. return _json.cError
  72.        
  73.  
  74.  
  75. *
  76. * json class
  77. *
  78. *
  79. define class json as custom
  80.  
  81.  
  82.         nPos=0
  83.         nLen=0
  84.         cJson=''
  85.         cError=''
  86.  
  87.  
  88.         *
  89.         * Genera el codigo cJson para parametro que se manda
  90.         *
  91.         function encode(xExpr)
  92.         local cTipo
  93.                 * Cuando se manda una arreglo,
  94.                 if type('ALen(xExpr)')=='N'
  95.                         cTipo = 'A'
  96.                 Else
  97.                         cTipo = VarType(xExpr)
  98.                 Endif
  99.                
  100.                 Do Case
  101.                 Case cTipo=='D'
  102.                         return '"'+dtos(xExpr)+'"'
  103.                 Case cTipo=='N'
  104.                         return Transform(xExpr)
  105.                 Case cTipo=='L'
  106.                         return iif(xExpr,'true','false')
  107.                 Case cTipo=='X'
  108.                         return 'null'
  109.                 Case cTipo=='C'
  110.                         xExpr = allt(xExpr)
  111.                         xExpr = StrTran(xExpr, '\', '\\' )
  112.                         xExpr = StrTran(xExpr, '/', '\/' )
  113.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  114.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  115.                         xExpr = StrTran(xExpr, '"', '\"' )
  116.                         return '"'+xExpr+'"'
  117.  
  118.                 case cTipo=='O'
  119.                         local cProp, cJsonValue, cRetVal, aProp[1]
  120.                         =AMembers(aProp,xExpr)
  121.                         cRetVal = ''
  122.                         for each cProp in aProp
  123.                                 *? cProp
  124.                                 *? cRetVal
  125.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  126.                                         * algunas propiedades pueden no estar definidas
  127.                                         * como: activecontrol, parent, etc
  128.                                         loop
  129.                                 endif
  130.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  131.                                         *
  132.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  133.                                         *
  134.                                         Local i,nTotElem
  135.                                         cJsonValue = ''
  136.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  137.                                         For i=1 to nTotElem
  138.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  139.                                         Next
  140.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  141.                                 else
  142.                                         *
  143.                                         * es otro tipo de dato normal C, N, L
  144.                                         *
  145.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  146.                                 endif
  147.                                 if left(cProp,1)=='_'
  148.                                         cProp = substr(cProp,2)
  149.                                 endif
  150.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  151.                         next
  152.                         return '{' + substr(cRetVal,2) + '}'
  153.  
  154.                 case cTipo=='A'
  155.                         local valor, cRetVal
  156.                         cRetVal = ''   
  157.                         for each valor in xExpr
  158.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  159.                         next
  160.                         return  '[' + substr(cRetVal,2) + ']'
  161.                        
  162.                 endcase
  163.  
  164.         return ''
  165.  
  166.  
  167.  
  168.  
  169.  
  170.         *
  171.         * regresa un elemento representado por la cadena json que se manda
  172.         *
  173.        
  174.         function decode(cJson)
  175.         local retValue
  176.                 cJson = StrTran(cJson,chr(9),'')
  177.                 cJson = StrTran(cJson,chr(10),'')
  178.                 cJson = StrTran(cJson,chr(13),'')
  179.                 cJson = this.fixUnicode(cJson)
  180.                 this.nPos  = 1
  181.                 this.cJson = cJson
  182.                 this.nLen  = len(cJson)
  183.                 this.cError = ''
  184.                 retValue = this.parsevalue()
  185.                 if not empty(this.cError)
  186.                         return null
  187.                 endif
  188.                 if this.getToken()<>null
  189.                         this.setError('Junk at the end of JSON input')
  190.                         return null
  191.                 endif
  192.         return retValue
  193.                
  194.        
  195.         function parseValue()
  196.         local token
  197.                 token = this.getToken()
  198.                 if token==null
  199.                         this.setError('Nothing to parse')
  200.                         return null
  201.                 endif
  202.                 do case
  203.                 case token=='"'
  204.                         return this.parseString()
  205.                 case isdigit(token) or token=='-'
  206.                         return this.parseNumber()
  207.                 case token=='n'
  208.                         return this.expectedKeyword('null',null)
  209.                 case token=='f'
  210.                         return this.expectedKeyword('false',.f.)
  211.                 case token=='t'
  212.                         return this.expectedKeyword('true',.t.)
  213.                 case token=='{'
  214.                         return this.parseObject()
  215.                 case token=='['
  216.                         return this.parseArray()
  217.                 otherwise
  218.                         this.setError('Unexpected token')
  219.                 endcase
  220.         return
  221.                
  222.        
  223.         function expectedKeyword(cWord,eValue)
  224.                 for i=1 to len(cWord)
  225.                         cChar = this.getChar()
  226.                         if cChar <> substr(cWord,i,1)
  227.                                 this.setError("Expected keyword '" + cWord + "'")
  228.                                 return ''
  229.                         endif
  230.                         this.nPos = this.nPos + 1
  231.                 next
  232.         return eValue
  233.        
  234.  
  235.         function parseObject()
  236.         local retval, cPropName, xValue
  237.                 retval = createObject('myObj')
  238.                 this.nPos = this.nPos + 1 && Eat {
  239.                 if this.getToken()<>'}'
  240.                         do while .t.
  241.                                 cPropName = this.parseString()
  242.                                 if not empty(this.cError)
  243.                                         return null
  244.                                 endif
  245.                                 if this.getToken()<>':'
  246.                                         this.setError("Expected ':' when parsing object")
  247.                                         return null
  248.                                 endif
  249.                                 this.nPos = this.nPos + 1
  250.                                 xValue = this.parseValue()
  251.                                 if not empty(this.cError)
  252.                                         return null
  253.                                 endif                          
  254.                                 ** Debug ? cPropName, type('xValue')
  255.                                 retval.set(cPropName, xValue)
  256.                                 if this.getToken()<>','
  257.                                         exit
  258.                                 endif
  259.                                 this.nPos = this.nPos + 1
  260.                         enddo
  261.                 endif
  262.                 if this.getToken()<>'}'
  263.                         this.setError("Expected '}' at the end of object")
  264.                         return null
  265.                 endif
  266.                 this.nPos = this.nPos + 1
  267.         return retval
  268.  
  269.  
  270.         function parseArray()
  271.         local retVal, xValue
  272.                 retval = createObject('MyArray')
  273.                 this.nPos = this.nPos + 1       && Eat [
  274.                 if this.getToken() <> ']'
  275.                         do while .t.
  276.                                 xValue = this.parseValue()
  277.                                 if not empty(this.cError)
  278.                                         return null
  279.                                 endif
  280.                                 retval.add( xValue )
  281.                                 if this.getToken()<>','
  282.                                         exit
  283.                                 endif
  284.                                 this.nPos = this.nPos + 1
  285.                         enddo
  286.                         if this.getToken() <> ']'
  287.                                 this.setError('Expected ] at the end of array')
  288.                                 return null
  289.                         endif
  290.                 endif
  291.                 this.nPos = this.nPos + 1
  292.         return retval
  293.        
  294.  
  295.         function parseString()
  296.         local cRetVal, c
  297.                 if this.getToken()<>'"'
  298.                         this.setError('Expected "')
  299.                         return ''
  300.                 endif
  301.                 this.nPos = this.nPos + 1       && Eat "
  302.                 cRetVal = ''
  303.                 do while .t.
  304.                         c = this.getChar()
  305.                         if c==''
  306.                                 return ''
  307.                         endif
  308.                         if c == '"'
  309.                                 this.nPos = this.nPos + 1
  310.                                 exit
  311.                         endif
  312.                         if c == '\'
  313.                                 this.nPos = this.nPos + 1
  314.                                 if (this.nPos>this.nLen)
  315.                                         this.setError('\\ at the end of input')
  316.                                         return ''
  317.                                 endif
  318.                                 c = this.getChar()
  319.                                 if c==''
  320.                                         return ''
  321.                                 endif
  322.                                 do case
  323.                                 case c=='"'
  324.                                         c='"'
  325.                                 case c=='\'
  326.                                         c='\'
  327.                                 case c=='/'
  328.                                         c='/'
  329.                                 case c=='b'
  330.                                         c=chr(8)
  331.                                 case c=='t'
  332.                                         c=chr(9)
  333.                                 case c=='n'
  334.                                         c=chr(10)
  335.                                 case c=='f'
  336.                                         c=chr(12)
  337.                                 case c=='r'
  338.                                         c=chr(13)
  339.                                 otherwise
  340.                                         ******* FALTAN LOS UNICODE
  341.                                         this.setError('Invalid escape sequence in string literal')
  342.                                         return ''
  343.                                 endcase
  344.                         endif
  345.                         cRetVal = cRetVal + c
  346.                         this.nPos = this.nPos + 1
  347.                 enddo
  348.         return cRetVal
  349.                                        
  350.  
  351.         **** Pendiente numeros con E
  352.         function parseNumber()
  353.         local nStartPos,c, isInt, cNumero
  354.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  355.                         this.setError('Expected number literal')
  356.                         return 0
  357.                 endif
  358.                 nStartPos = this.nPos
  359.                 c = this.getChar()
  360.                 if c == '-'
  361.                         c = this.nextChar()
  362.                 endif
  363.                 if c == '0'
  364.                         c = this.nextChar()
  365.                 else
  366.                         if isdigit(c)
  367.                                 c = this.nextChar()
  368.                                 do while isdigit(c)
  369.                                         c = this.nextChar()
  370.                                 enddo
  371.                         else
  372.                                 this.setError('Expected digit when parsing number')
  373.                                 return 0
  374.                         endif
  375.                 endif
  376.                
  377.                 isInt = .t.
  378.                 if c=='.'
  379.                         c = this.nextChar()
  380.                         if isdigit(c)
  381.                                 c = this.nextChar()
  382.                                 isInt = .f.
  383.                                 do while isDigit(c)
  384.                                         c = this.nextChar()
  385.                                 enddo
  386.                         else
  387.                                 this.setError('Expected digit following dot comma')
  388.                                 return 0
  389.                         endif
  390.                 endif
  391.                
  392.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  393.         return val(cNumero)
  394.  
  395.  
  396.  
  397.         function getToken()
  398.         local char1
  399.                 do while .t.
  400.                         if this.nPos > this.nLen
  401.                                 return null
  402.                         endif
  403.                         char1 = substr(this.cJson, this.nPos, 1)
  404.                         if char1==' '
  405.                                 this.nPos = this.nPos + 1
  406.                                 loop
  407.                         endif
  408.                         return char1
  409.                 enddo
  410.         return
  411.        
  412.                
  413.                
  414.         function getChar()
  415.                 if this.nPos > this.nLen
  416.                         this.setError('Unexpected end of JSON stream')
  417.                         return ''
  418.                 endif
  419.         return substr(this.cJson, this.nPos, 1)
  420.        
  421.         function nextChar()
  422.                 this.nPos = this.nPos + 1
  423.                 if this.nPos > this.nLen
  424.                         return ''
  425.                 endif
  426.         return substr(this.cJson, this.nPos, 1)
  427.        
  428.         function setError(cMsg)
  429.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  430.         return
  431.  
  432.  
  433.         function fixUnicode(cStr)
  434.                 cStr = StrTran(cStr,'\u00e1','á')
  435.                 cStr = StrTran(cStr,'\u00e9','é')
  436.                 cStr = StrTran(cStr,'\u00ed','í')
  437.                 cStr = StrTran(cStr,'\u00f3','ó')
  438.                 cStr = StrTran(cStr,'\u00fa','ú')
  439.                 cStr = StrTran(cStr,'\u00c1','Á')
  440.                 cStr = StrTran(cStr,'\u00c9','É')
  441.                 cStr = StrTran(cStr,'\u00cd','Í')
  442.                 cStr = StrTran(cStr,'\u00d3','Ó')
  443.                 cStr = StrTran(cStr,'\u00da','Ú')
  444.                 cStr = StrTran(cStr,'\u00f1','ń')
  445.                 cStr = StrTran(cStr,'\u00d1','Ń')
  446.         return cStr
  447.  
  448.  
  449.  
  450. enddefine
  451.  
  452.  
  453.  
  454.  
  455.  
  456. *
  457. * class used to return an array
  458. *
  459. define class myArray as custom
  460.         nSize = 0
  461.         dimension array[1]
  462.  
  463.         function add(xExpr)
  464.                 this.nSize = this.nSize + 1
  465.                 dimension this.array[this.nSize]
  466.                 this.array[this.nSize] = xExpr
  467.         return
  468.  
  469.         function get(n)
  470.         return this.array[n]
  471.  
  472. enddefine
  473.  
  474.  
  475.  
  476. *
  477. * class used to simulate an object
  478. * all properties are prefixed with 'prop' to permit property names like: error, init
  479. * that already exists like vfp methods
  480. *
  481. define class myObj as custom
  482. Hidden ;
  483.         ClassLibrary,Comment, ;
  484.         BaseClass,ControlCount, ;
  485.         Controls,Objects,Object,;
  486.         Height,HelpContextID,Left,Name, ;
  487.         Parent,ParentClass,Picture, ;
  488.         Tag,Top,WhatsThisHelpID,Width
  489.                
  490.         function set(cPropName, xValue)
  491.                 cPropName = '_'+cPropName
  492.                 if type('this.'+cPropName)=='U'
  493.                         this.addProperty(cPropName,xValue)
  494.                 else
  495.                         local cmd
  496.                         cmd = 'this.'+cPropName+'=xValue'
  497.                         &cmd
  498.                 endif
  499.         return
  500.        
  501.         procedure get (cPropName)
  502.                 cPropName = '_'+cPropName
  503.                 If type('this.'+cPropName)=='U'
  504.                         return ''
  505.                 Else
  506.                         local cmd
  507.                         cmd = 'return this.'+cPropName
  508.                         &cmd
  509.                 endif
  510.         return ''
  511. enddefine
  512.  
  513.  
  514.  
  515.  
  516.  
  517. function testJsonClass
  518.         clear
  519.         set decimal to 10
  520.         oJson = newObject('json')
  521.        
  522.        
  523.         ? 'Test Basic Types'
  524.         ? '----------------'
  525.         ? oJson.decode('null')
  526.         ? oJson.decode('true')
  527.         ? oJson.decode('false')
  528.         ?
  529.         ? oJson.decode('791123')
  530.         ? oJson.decode('791123.45')
  531.         ? oJson.decode('791123.45.')
  532.         ? oJson.decode('"nacho gtz"')
  533.         if not empty(oJson.cError)
  534.                 ? oJson.cError
  535.                 return
  536.         endif
  537.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  538.         if not empty(oJson.cError)
  539.                 ? oJson.cError
  540.                 return
  541.         endif
  542.        
  543.         ? 'Test Array'
  544.         ? '----------'
  545.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  546.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  547.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  548.         nombres = arr.get(1)
  549.         edades  = arr.get(2)
  550.         ? nombres.get(1), edades.get(1)
  551.         ? nombres.get(2), edades.get(2)
  552.         ? nombres.get(3), edades.get(3)
  553.         ?
  554.         ? 'Test Object'
  555.         ? '-----------'
  556.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  557.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  558.         ? obj._Nombre, obj._Edad, obj._IsGood
  559.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  560.         ? obj.get('jsonrpc'), obj._jsonrpc
  561.         ? obj.get('id'), obj._id
  562.         ? obj.get('method'), obj._method
  563.         ? obj._Params.array[1], obj._Params.get(1)
  564.  
  565.         ?
  566.         ? 'Test nested object'
  567.         ? '------------------'
  568.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  569.         obj = oJson.decode(cJson)
  570.         if not empty(oJson.cError)
  571.                 ? oJson.cError
  572.                 return
  573.         endif
  574.         ? cJson
  575.         ? 'method -->',obj._method
  576.         ? 'usrkey -->',obj._params._data._usrkey
  577.         ? 'sendto -->',obj._params._data._sendto
  578.         ? 'name  --->',obj._params._data._name
  579.         ? 'expires ->',obj._params._data._expires
  580.  
  581.         ?
  582.         ? 'Test empty object'
  583.         ? '-----------------'
  584.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  585.         obj = oJson.decode(cJson)
  586.         if not empty(oJson.cError)     
  587.                 ? oJson.cError
  588.                 return
  589.         endif
  590.         ? cJson
  591.         ? 'result -->',obj._result, obj.get('result')
  592.         oError = obj.get('error')
  593.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  594.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  595.         ? 'id  ----->',obj._id, obj.get('id')
  596.         ?  type("oError._code")
  597.  
  598.         ?
  599.         ? 'Probar decode-enconde-decode-encode'
  600.         ? '------------------------------------'
  601.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  602.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  603.         ? cJson
  604.         oSmtp = json_decode(cJson)
  605.         cJson =  json_encode(oSmtp)
  606.         ? cJson
  607.         oSmtp = json_decode(cJson)
  608.         cJson =  json_encode(oSmtp)
  609.         ? cJson
  610.  
  611.         * Probar falla
  612.         ?
  613.         ? 'Probar una falla en el json'
  614.         ? '---------------------------'
  615.         cJson = ' {"server":"", "user":"", "password":"" ,'
  616.         oSmtp = json_decode(cJson)
  617.         if not empty(json_getErrorMsg())
  618.                 ? json_getErrorMsg()
  619.         endif
  620.  
  621.         ?
  622.         ? 'Pruebas Finalizadas'
  623. retur
  624. * vfpjson
  625. *
  626. * ----------------------------------
  627. * Ignacio Gutiérrez Torrero
  628. * SAIT Software Administrativo
  629. * www.sait.com.mx
  630. * +52(653)534-8800
  631. * Monterrey México
  632. * -----------------------------------
  633. *
  634. * Libreria para el manejo de JSON en VFP
  635. *
  636. * Gracias a Google por el codigo de Json de Dart
  637. * Thanks Google for the code in Json Dart
  638. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  639. *
  640. *
  641. * Codificar y Decodificar JSON
  642. *
  643. * Puedes usar las funciones:
  644. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  645. *               json_decode(cJson)  te regresa el objeto representado en cJson
  646. *
  647. * Tambien puedes usar directamente la clase:
  648. *
  649. *       oJson = newobject('json','json.prg')
  650. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  651. *       ? oJson.encode(oCliente)
  652. *       ? oCliente.get('nombre')
  653. *       ? oCliente.get('apellido')
  654. *
  655. *
  656. * VFPJSON  Encode and Decode JSON for VFP
  657. * Examples:
  658. *       oJson = newobject('json','json.prg')
  659. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  660. *       ? oJson.encode(oCustomer)
  661. *       ? oCustomer.get('name')
  662. *       ? oCustomer.get('lastname')
  663. *
  664. *
  665. lRunTest = .f.
  666. if lRunTest
  667.         testJsonClass()
  668. endif
  669. return
  670.  
  671.  
  672. function json_encode(xExpr)
  673.         if vartype(_json)<>'O'
  674.                 public _json
  675.                 _json = newobject('json')
  676.         endif
  677. return _json.encode(@xExpr)
  678.  
  679.  
  680. function json_decode(cJson)
  681. local retval
  682.         if vartype(_json)<>'O'
  683.                 public _json
  684.                 _json = newobject('json')
  685.         endif
  686.         retval = _json.decode(cJson)
  687.         if not empty(_json.cError)
  688.                 return null
  689.         endif
  690. return retval
  691.  
  692. function json_getErrorMsg()
  693. return _json.cError
  694.        
  695.  
  696.  
  697. *
  698. * json class
  699. *
  700. *
  701. define class json as custom
  702.  
  703.  
  704.         nPos=0
  705.         nLen=0
  706.         cJson=''
  707.         cError=''
  708.  
  709.  
  710.         *
  711.         * Genera el codigo cJson para parametro que se manda
  712.         *
  713.         function encode(xExpr)
  714.         local cTipo
  715.                 * Cuando se manda una arreglo,
  716.                 if type('ALen(xExpr)')=='N'
  717.                         cTipo = 'A'
  718.                 Else
  719.                         cTipo = VarType(xExpr)
  720.                 Endif
  721.                
  722.                 Do Case
  723.                 Case cTipo=='D'
  724.                         return '"'+dtos(xExpr)+'"'
  725.                 Case cTipo=='N'
  726.                         return Transform(xExpr)
  727.                 Case cTipo=='L'
  728.                         return iif(xExpr,'true','false')
  729.                 Case cTipo=='X'
  730.                         return 'null'
  731.                 Case cTipo=='C'
  732.                         xExpr = allt(xExpr)
  733.                         xExpr = StrTran(xExpr, '\', '\\' )
  734.                         xExpr = StrTran(xExpr, '/', '\/' )
  735.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  736.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  737.                         xExpr = StrTran(xExpr, '"', '\"' )
  738.                         return '"'+xExpr+'"'
  739.  
  740.                 case cTipo=='O'
  741.                         local cProp, cJsonValue, cRetVal, aProp[1]
  742.                         =AMembers(aProp,xExpr)
  743.                         cRetVal = ''
  744.                         for each cProp in aProp
  745.                                 *? cProp
  746.                                 *? cRetVal
  747.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  748.                                         * algunas propiedades pueden no estar definidas
  749.                                         * como: activecontrol, parent, etc
  750.                                         loop
  751.                                 endif
  752.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  753.                                         *
  754.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  755.                                         *
  756.                                         Local i,nTotElem
  757.                                         cJsonValue = ''
  758.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  759.                                         For i=1 to nTotElem
  760.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  761.                                         Next
  762.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  763.                                 else
  764.                                         *
  765.                                         * es otro tipo de dato normal C, N, L
  766.                                         *
  767.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  768.                                 endif
  769.                                 if left(cProp,1)=='_'
  770.                                         cProp = substr(cProp,2)
  771.                                 endif
  772.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  773.                         next
  774.                         return '{' + substr(cRetVal,2) + '}'
  775.  
  776.                 case cTipo=='A'
  777.                         local valor, cRetVal
  778.                         cRetVal = ''   
  779.                         for each valor in xExpr
  780.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  781.                         next
  782.                         return  '[' + substr(cRetVal,2) + ']'
  783.                        
  784.                 endcase
  785.  
  786.         return ''
  787.  
  788.  
  789.  
  790.  
  791.  
  792.         *
  793.         * regresa un elemento representado por la cadena json que se manda
  794.         *
  795.        
  796.         function decode(cJson)
  797.         local retValue
  798.                 cJson = StrTran(cJson,chr(9),'')
  799.                 cJson = StrTran(cJson,chr(10),'')
  800.                 cJson = StrTran(cJson,chr(13),'')
  801.                 cJson = this.fixUnicode(cJson)
  802.                 this.nPos  = 1
  803.                 this.cJson = cJson
  804.                 this.nLen  = len(cJson)
  805.                 this.cError = ''
  806.                 retValue = this.parsevalue()
  807.                 if not empty(this.cError)
  808.                         return null
  809.                 endif
  810.                 if this.getToken()<>null
  811.                         this.setError('Junk at the end of JSON input')
  812.                         return null
  813.                 endif
  814.         return retValue
  815.                
  816.        
  817.         function parseValue()
  818.         local token
  819.                 token = this.getToken()
  820.                 if token==null
  821.                         this.setError('Nothing to parse')
  822.                         return null
  823.                 endif
  824.                 do case
  825.                 case token=='"'
  826.                         return this.parseString()
  827.                 case isdigit(token) or token=='-'
  828.                         return this.parseNumber()
  829.                 case token=='n'
  830.                         return this.expectedKeyword('null',null)
  831.                 case token=='f'
  832.                         return this.expectedKeyword('false',.f.)
  833.                 case token=='t'
  834.                         return this.expectedKeyword('true',.t.)
  835.                 case token=='{'
  836.                         return this.parseObject()
  837.                 case token=='['
  838.                         return this.parseArray()
  839.                 otherwise
  840.                         this.setError('Unexpected token')
  841.                 endcase
  842.         return
  843.                
  844.        
  845.         function expectedKeyword(cWord,eValue)
  846.                 for i=1 to len(cWord)
  847.                         cChar = this.getChar()
  848.                         if cChar <> substr(cWord,i,1)
  849.                                 this.setError("Expected keyword '" + cWord + "'")
  850.                                 return ''
  851.                         endif
  852.                         this.nPos = this.nPos + 1
  853.                 next
  854.         return eValue
  855.        
  856.  
  857.         function parseObject()
  858.         local retval, cPropName, xValue
  859.                 retval = createObject('myObj')
  860.                 this.nPos = this.nPos + 1 && Eat {
  861.                 if this.getToken()<>'}'
  862.                         do while .t.
  863.                                 cPropName = this.parseString()
  864.                                 if not empty(this.cError)
  865.                                         return null
  866.                                 endif
  867.                                 if this.getToken()<>':'
  868.                                         this.setError("Expected ':' when parsing object")
  869.                                         return null
  870.                                 endif
  871.                                 this.nPos = this.nPos + 1
  872.                                 xValue = this.parseValue()
  873.                                 if not empty(this.cError)
  874.                                         return null
  875.                                 endif                          
  876.                                 ** Debug ? cPropName, type('xValue')
  877.                                 retval.set(cPropName, xValue)
  878.                                 if this.getToken()<>','
  879.                                         exit
  880.                                 endif
  881.                                 this.nPos = this.nPos + 1
  882.                         enddo
  883.                 endif
  884.                 if this.getToken()<>'}'
  885.                         this.setError("Expected '}' at the end of object")
  886.                         return null
  887.                 endif
  888.                 this.nPos = this.nPos + 1
  889.         return retval
  890.  
  891.  
  892.         function parseArray()
  893.         local retVal, xValue
  894.                 retval = createObject('MyArray')
  895.                 this.nPos = this.nPos + 1       && Eat [
  896.                 if this.getToken() <> ']'
  897.                         do while .t.
  898.                                 xValue = this.parseValue()
  899.                                 if not empty(this.cError)
  900.                                         return null
  901.                                 endif
  902.                                 retval.add( xValue )
  903.                                 if this.getToken()<>','
  904.                                         exit
  905.                                 endif
  906.                                 this.nPos = this.nPos + 1
  907.                         enddo
  908.                         if this.getToken() <> ']'
  909.                                 this.setError('Expected ] at the end of array')
  910.                                 return null
  911.                         endif
  912.                 endif
  913.                 this.nPos = this.nPos + 1
  914.         return retval
  915.        
  916.  
  917.         function parseString()
  918.         local cRetVal, c
  919.                 if this.getToken()<>'"'
  920.                         this.setError('Expected "')
  921.                         return ''
  922.                 endif
  923.                 this.nPos = this.nPos + 1       && Eat "
  924.                 cRetVal = ''
  925.                 do while .t.
  926.                         c = this.getChar()
  927.                         if c==''
  928.                                 return ''
  929.                         endif
  930.                         if c == '"'
  931.                                 this.nPos = this.nPos + 1
  932.                                 exit
  933.                         endif
  934.                         if c == '\'
  935.                                 this.nPos = this.nPos + 1
  936.                                 if (this.nPos>this.nLen)
  937.                                         this.setError('\\ at the end of input')
  938.                                         return ''
  939.                                 endif
  940.                                 c = this.getChar()
  941.                                 if c==''
  942.                                         return ''
  943.                                 endif
  944.                                 do case
  945.                                 case c=='"'
  946.                                         c='"'
  947.                                 case c=='\'
  948.                                         c='\'
  949.                                 case c=='/'
  950.                                         c='/'
  951.                                 case c=='b'
  952.                                         c=chr(8)
  953.                                 case c=='t'
  954.                                         c=chr(9)
  955.                                 case c=='n'
  956.                                         c=chr(10)
  957.                                 case c=='f'
  958.                                         c=chr(12)
  959.                                 case c=='r'
  960.                                         c=chr(13)
  961.                                 otherwise
  962.                                         ******* FALTAN LOS UNICODE
  963.                                         this.setError('Invalid escape sequence in string literal')
  964.                                         return ''
  965.                                 endcase
  966.                         endif
  967.                         cRetVal = cRetVal + c
  968.                         this.nPos = this.nPos + 1
  969.                 enddo
  970.         return cRetVal
  971.                                        
  972.  
  973.         **** Pendiente numeros con E
  974.         function parseNumber()
  975.         local nStartPos,c, isInt, cNumero
  976.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  977.                         this.setError('Expected number literal')
  978.                         return 0
  979.                 endif
  980.                 nStartPos = this.nPos
  981.                 c = this.getChar()
  982.                 if c == '-'
  983.                         c = this.nextChar()
  984.                 endif
  985.                 if c == '0'
  986.                         c = this.nextChar()
  987.                 else
  988.                         if isdigit(c)
  989.                                 c = this.nextChar()
  990.                                 do while isdigit(c)
  991.                                         c = this.nextChar()
  992.                                 enddo
  993.                         else
  994.                                 this.setError('Expected digit when parsing number')
  995.                                 return 0
  996.                         endif
  997.                 endif
  998.                
  999.                 isInt = .t.
  1000.                 if c=='.'
  1001.                         c = this.nextChar()
  1002.                         if isdigit(c)
  1003.                                 c = this.nextChar()
  1004.                                 isInt = .f.
  1005.                                 do while isDigit(c)
  1006.                                         c = this.nextChar()
  1007.                                 enddo
  1008.                         else
  1009.                                 this.setError('Expected digit following dot comma')
  1010.                                 return 0
  1011.                         endif
  1012.                 endif
  1013.                
  1014.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  1015.         return val(cNumero)
  1016.  
  1017.  
  1018.  
  1019.         function getToken()
  1020.         local char1
  1021.                 do while .t.
  1022.                         if this.nPos > this.nLen
  1023.                                 return null
  1024.                         endif
  1025.                         char1 = substr(this.cJson, this.nPos, 1)
  1026.                         if char1==' '
  1027.                                 this.nPos = this.nPos + 1
  1028.                                 loop
  1029.                         endif
  1030.                         return char1
  1031.                 enddo
  1032.         return
  1033.        
  1034.                
  1035.                
  1036.         function getChar()
  1037.                 if this.nPos > this.nLen
  1038.                         this.setError('Unexpected end of JSON stream')
  1039.                         return ''
  1040.                 endif
  1041.         return substr(this.cJson, this.nPos, 1)
  1042.        
  1043.         function nextChar()
  1044.                 this.nPos = this.nPos + 1
  1045.                 if this.nPos > this.nLen
  1046.                         return ''
  1047.                 endif
  1048.         return substr(this.cJson, this.nPos, 1)
  1049.        
  1050.         function setError(cMsg)
  1051.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  1052.         return
  1053.  
  1054.  
  1055.         function fixUnicode(cStr)
  1056.                 cStr = StrTran(cStr,'\u00e1','á')
  1057.                 cStr = StrTran(cStr,'\u00e9','é')
  1058.                 cStr = StrTran(cStr,'\u00ed','í')
  1059.                 cStr = StrTran(cStr,'\u00f3','ó')
  1060.                 cStr = StrTran(cStr,'\u00fa','ú')
  1061.                 cStr = StrTran(cStr,'\u00c1','Á')
  1062.                 cStr = StrTran(cStr,'\u00c9','É')
  1063.                 cStr = StrTran(cStr,'\u00cd','Í')
  1064.                 cStr = StrTran(cStr,'\u00d3','Ó')
  1065.                 cStr = StrTran(cStr,'\u00da','Ú')
  1066.                 cStr = StrTran(cStr,'\u00f1','ń')
  1067.                 cStr = StrTran(cStr,'\u00d1','Ń')
  1068.         return cStr
  1069.  
  1070.  
  1071.  
  1072. enddefine
  1073.  
  1074.  
  1075.  
  1076.  
  1077.  
  1078. *
  1079. * class used to return an array
  1080. *
  1081. define class myArray as custom
  1082.         nSize = 0
  1083.         dimension array[1]
  1084.  
  1085.         function add(xExpr)
  1086.                 this.nSize = this.nSize + 1
  1087.                 dimension this.array[this.nSize]
  1088.                 this.array[this.nSize] = xExpr
  1089.         return
  1090.  
  1091.         function get(n)
  1092.         return this.array[n]
  1093.  
  1094. enddefine
  1095.  
  1096.  
  1097.  
  1098. *
  1099. * class used to simulate an object
  1100. * all properties are prefixed with 'prop' to permit property names like: error, init
  1101. * that already exists like vfp methods
  1102. *
  1103. define class myObj as custom
  1104. Hidden ;
  1105.         ClassLibrary,Comment, ;
  1106.         BaseClass,ControlCount, ;
  1107.         Controls,Objects,Object,;
  1108.         Height,HelpContextID,Left,Name, ;
  1109.         Parent,ParentClass,Picture, ;
  1110.         Tag,Top,WhatsThisHelpID,Width
  1111.                
  1112.         function set(cPropName, xValue)
  1113.                 cPropName = '_'+cPropName
  1114.                 if type('this.'+cPropName)=='U'
  1115.                         this.addProperty(cPropName,xValue)
  1116.                 else
  1117.                         local cmd
  1118.                         cmd = 'this.'+cPropName+'=xValue'
  1119.                         &cmd
  1120.                 endif
  1121.         return
  1122.        
  1123.         procedure get (cPropName)
  1124.                 cPropName = '_'+cPropName
  1125.                 If type('this.'+cPropName)=='U'
  1126.                         return ''
  1127.                 Else
  1128.                         local cmd
  1129.                         cmd = 'return this.'+cPropName
  1130.                         &cmd
  1131.                 endif
  1132.         return ''
  1133. enddefine
  1134.  
  1135.  
  1136.  
  1137.  
  1138.  
  1139. function testJsonClass
  1140.         clear
  1141.         set decimal to 10
  1142.         oJson = newObject('json')
  1143.        
  1144.        
  1145.         ? 'Test Basic Types'
  1146.         ? '----------------'
  1147.         ? oJson.decode('null')
  1148.         ? oJson.decode('true')
  1149.         ? oJson.decode('false')
  1150.         ?
  1151.         ? oJson.decode('791123')
  1152.         ? oJson.decode('791123.45')
  1153.         ? oJson.decode('791123.45.')
  1154.         ? oJson.decode('"nacho gtz"')
  1155.         if not empty(oJson.cError)
  1156.                 ? oJson.cError
  1157.                 return
  1158.         endif
  1159.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  1160.         if not empty(oJson.cError)
  1161.                 ? oJson.cError
  1162.                 return
  1163.         endif
  1164.        
  1165.         ? 'Test Array'
  1166.         ? '----------'
  1167.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  1168.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  1169.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  1170.         nombres = arr.get(1)
  1171.         edades  = arr.get(2)
  1172.         ? nombres.get(1), edades.get(1)
  1173.         ? nombres.get(2), edades.get(2)
  1174.         ? nombres.get(3), edades.get(3)
  1175.         ?
  1176.         ? 'Test Object'
  1177.         ? '-----------'
  1178.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  1179.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  1180.         ? obj._Nombre, obj._Edad, obj._IsGood
  1181.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  1182.         ? obj.get('jsonrpc'), obj._jsonrpc
  1183.         ? obj.get('id'), obj._id
  1184.         ? obj.get('method'), obj._method
  1185.         ? obj._Params.array[1], obj._Params.get(1)
  1186.  
  1187.         ?
  1188.         ? 'Test nested object'
  1189.         ? '------------------'
  1190.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  1191.         obj = oJson.decode(cJson)
  1192.         if not empty(oJson.cError)
  1193.                 ? oJson.cError
  1194.                 return
  1195.         endif
  1196.         ? cJson
  1197.         ? 'method -->',obj._method
  1198.         ? 'usrkey -->',obj._params._data._usrkey
  1199.         ? 'sendto -->',obj._params._data._sendto
  1200.         ? 'name  --->',obj._params._data._name
  1201.         ? 'expires ->',obj._params._data._expires
  1202.  
  1203.         ?
  1204.         ? 'Test empty object'
  1205.         ? '-----------------'
  1206.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  1207.         obj = oJson.decode(cJson)
  1208.         if not empty(oJson.cError)     
  1209.                 ? oJson.cError
  1210.                 return
  1211.         endif
  1212.         ? cJson
  1213.         ? 'result -->',obj._result, obj.get('result')
  1214.         oError = obj.get('error')
  1215.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  1216.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  1217.         ? 'id  ----->',obj._id, obj.get('id')
  1218.         ?  type("oError._code")
  1219.  
  1220.         ?
  1221.         ? 'Probar decode-enconde-decode-encode'
  1222.         ? '------------------------------------'
  1223.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  1224.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  1225.         ? cJson
  1226.         oSmtp = json_decode(cJson)
  1227.         cJson =  json_encode(oSmtp)
  1228.         ? cJson
  1229.         oSmtp = json_decode(cJson)
  1230.         cJson =  json_encode(oSmtp)
  1231.         ? cJson
  1232.  
  1233.         * Probar falla
  1234.         ?
  1235.         ? 'Probar una falla en el json'
  1236.         ? '---------------------------'
  1237.         cJson = ' {"server":"", "user":"", "password":"" ,'
  1238.         oSmtp = json_decode(cJson)
  1239.         if not empty(json_getErrorMsg())
  1240.                 ? json_getErrorMsg()
  1241.         endif
  1242.  
  1243.         ?
  1244.         ? 'Pruebas Finalizadas'
  1245. retur
  1246. *
  1247. * ----------------------------------
  1248. * Ignacio Gutiérrez Torrero
  1249. * SAIT Software Administrativo
  1250. * www.sait.com.mx
  1251. * +52(653)534-8800
  1252. * Monterrey México
  1253. * -----------------------------------
  1254. *
  1255. * Libreria para el manejo de JSON en VFP
  1256. *
  1257. * Gracias a Google por el codigo de Json de Dart
  1258. * Thanks Google for the code in Json Dart
  1259. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  1260. *
  1261. *
  1262. * Codificar y Decodificar JSON
  1263. *
  1264. * Puedes usar las funciones:
  1265. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  1266. *               json_decode(cJson)  te regresa el objeto representado en cJson
  1267. *
  1268. * Tambien puedes usar directamente la clase:
  1269. *
  1270. *       oJson = newobject('json','json.prg')
  1271. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  1272. *       ? oJson.encode(oCliente)
  1273. *       ? oCliente.get('nombre')
  1274. *       ? oCliente.get('apellido')
  1275. *
  1276. *
  1277. * VFPJSON  Encode and Decode JSON for VFP
  1278. * Examples:
  1279. *       oJson = newobject('json','json.prg')
  1280. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  1281. *       ? oJson.encode(oCustomer)
  1282. *       ? oCustomer.get('name')
  1283. *       ? oCustomer.get('lastname')
  1284. *
  1285. *
  1286. lRunTest = .f.
  1287. if lRunTest
  1288.         testJsonClass()
  1289. endif
  1290. return
  1291.  
  1292.  
  1293. function json_encode(xExpr)
  1294.         if vartype(_json)<>'O'
  1295.                 public _json
  1296.                 _json = newobject('json')
  1297.         endif
  1298. return _json.encode(@xExpr)
  1299.  
  1300.  
  1301. function json_decode(cJson)
  1302. local retval
  1303.         if vartype(_json)<>'O'
  1304.                 public _json
  1305.                 _json = newobject('json')
  1306.         endif
  1307.         retval = _json.decode(cJson)
  1308.         if not empty(_json.cError)
  1309.                 return null
  1310.         endif
  1311. return retval
  1312.  
  1313. function json_getErrorMsg()
  1314. return _json.cError
  1315.        
  1316.  
  1317.  
  1318. *
  1319. * json class
  1320. *
  1321. *
  1322. define class json as custom
  1323.  
  1324.  
  1325.         nPos=0
  1326.         nLen=0
  1327.         cJson=''
  1328.         cError=''
  1329.  
  1330.  
  1331.         *
  1332.         * Genera el codigo cJson para parametro que se manda
  1333.         *
  1334.         function encode(xExpr)
  1335.         local cTipo
  1336.                 * Cuando se manda una arreglo,
  1337.                 if type('ALen(xExpr)')=='N'
  1338.                         cTipo = 'A'
  1339.                 Else
  1340.                         cTipo = VarType(xExpr)
  1341.                 Endif
  1342.                
  1343.                 Do Case
  1344.                 Case cTipo=='D'
  1345.                         return '"'+dtos(xExpr)+'"'
  1346.                 Case cTipo=='N'
  1347.                         return Transform(xExpr)
  1348.                 Case cTipo=='L'
  1349.                         return iif(xExpr,'true','false')
  1350.                 Case cTipo=='X'
  1351.                         return 'null'
  1352.                 Case cTipo=='C'
  1353.                         xExpr = allt(xExpr)
  1354.                         xExpr = StrTran(xExpr, '\', '\\' )
  1355.                         xExpr = StrTran(xExpr, '/', '\/' )
  1356.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  1357.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  1358.                         xExpr = StrTran(xExpr, '"', '\"' )
  1359.                         return '"'+xExpr+'"'
  1360.  
  1361.                 case cTipo=='O'
  1362.                         local cProp, cJsonValue, cRetVal, aProp[1]
  1363.                         =AMembers(aProp,xExpr)
  1364.                         cRetVal = ''
  1365.                         for each cProp in aProp
  1366.                                 *? cProp
  1367.                                 *? cRetVal
  1368.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  1369.                                         * algunas propiedades pueden no estar definidas
  1370.                                         * como: activecontrol, parent, etc
  1371.                                         loop
  1372.                                 endif
  1373.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  1374.                                         *
  1375.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  1376.                                         *
  1377.                                         Local i,nTotElem
  1378.                                         cJsonValue = ''
  1379.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  1380.                                         For i=1 to nTotElem
  1381.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  1382.                                         Next
  1383.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  1384.                                 else
  1385.                                         *
  1386.                                         * es otro tipo de dato normal C, N, L
  1387.                                         *
  1388.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  1389.                                 endif
  1390.                                 if left(cProp,1)=='_'
  1391.                                         cProp = substr(cProp,2)
  1392.                                 endif
  1393.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  1394.                         next
  1395.                         return '{' + substr(cRetVal,2) + '}'
  1396.  
  1397.                 case cTipo=='A'
  1398.                         local valor, cRetVal
  1399.                         cRetVal = ''   
  1400.                         for each valor in xExpr
  1401.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  1402.                         next
  1403.                         return  '[' + substr(cRetVal,2) + ']'
  1404.                        
  1405.                 endcase
  1406.  
  1407.         return ''
  1408.  
  1409.  
  1410.  
  1411.  
  1412.  
  1413.         *
  1414.         * regresa un elemento representado por la cadena json que se manda
  1415.         *
  1416.        
  1417.         function decode(cJson)
  1418.         local retValue
  1419.                 cJson = StrTran(cJson,chr(9),'')
  1420.                 cJson = StrTran(cJson,chr(10),'')
  1421.                 cJson = StrTran(cJson,chr(13),'')
  1422.                 cJson = this.fixUnicode(cJson)
  1423.                 this.nPos  = 1
  1424.                 this.cJson = cJson
  1425.                 this.nLen  = len(cJson)
  1426.                 this.cError = ''
  1427.                 retValue = this.parsevalue()
  1428.                 if not empty(this.cError)
  1429.                         return null
  1430.                 endif
  1431.                 if this.getToken()<>null
  1432.                         this.setError('Junk at the end of JSON input')
  1433.                         return null
  1434.                 endif
  1435.         return retValue
  1436.                
  1437.        
  1438.         function parseValue()
  1439.         local token
  1440.                 token = this.getToken()
  1441.                 if token==null
  1442.                         this.setError('Nothing to parse')
  1443.                         return null
  1444.                 endif
  1445.                 do case
  1446.                 case token=='"'
  1447.                         return this.parseString()
  1448.                 case isdigit(token) or token=='-'
  1449.                         return this.parseNumber()
  1450.                 case token=='n'
  1451.                         return this.expectedKeyword('null',null)
  1452.                 case token=='f'
  1453.                         return this.expectedKeyword('false',.f.)
  1454.                 case token=='t'
  1455.                         return this.expectedKeyword('true',.t.)
  1456.                 case token=='{'
  1457.                         return this.parseObject()
  1458.                 case token=='['
  1459.                         return this.parseArray()
  1460.                 otherwise
  1461.                         this.setError('Unexpected token')
  1462.                 endcase
  1463.         return
  1464.                
  1465.        
  1466.         function expectedKeyword(cWord,eValue)
  1467.                 for i=1 to len(cWord)
  1468.                         cChar = this.getChar()
  1469.                         if cChar <> substr(cWord,i,1)
  1470.                                 this.setError("Expected keyword '" + cWord + "'")
  1471.                                 return ''
  1472.                         endif
  1473.                         this.nPos = this.nPos + 1
  1474.                 next
  1475.         return eValue
  1476.        
  1477.  
  1478.         function parseObject()
  1479.         local retval, cPropName, xValue
  1480.                 retval = createObject('myObj')
  1481.                 this.nPos = this.nPos + 1 && Eat {
  1482.                 if this.getToken()<>'}'
  1483.                         do while .t.
  1484.                                 cPropName = this.parseString()
  1485.                                 if not empty(this.cError)
  1486.                                         return null
  1487.                                 endif
  1488.                                 if this.getToken()<>':'
  1489.                                         this.setError("Expected ':' when parsing object")
  1490.                                         return null
  1491.                                 endif
  1492.                                 this.nPos = this.nPos + 1
  1493.                                 xValue = this.parseValue()
  1494.                                 if not empty(this.cError)
  1495.                                         return null
  1496.                                 endif                          
  1497.                                 ** Debug ? cPropName, type('xValue')
  1498.                                 retval.set(cPropName, xValue)
  1499.                                 if this.getToken()<>','
  1500.                                         exit
  1501.                                 endif
  1502.                                 this.nPos = this.nPos + 1
  1503.                         enddo
  1504.                 endif
  1505.                 if this.getToken()<>'}'
  1506.                         this.setError("Expected '}' at the end of object")
  1507.                         return null
  1508.                 endif
  1509.                 this.nPos = this.nPos + 1
  1510.         return retval
  1511.  
  1512.  
  1513.         function parseArray()
  1514.         local retVal, xValue
  1515.                 retval = createObject('MyArray')
  1516.                 this.nPos = this.nPos + 1       && Eat [
  1517.                 if this.getToken() <> ']'
  1518.                         do while .t.
  1519.                                 xValue = this.parseValue()
  1520.                                 if not empty(this.cError)
  1521.                                         return null
  1522.                                 endif
  1523.                                 retval.add( xValue )
  1524.                                 if this.getToken()<>','
  1525.                                         exit
  1526.                                 endif
  1527.                                 this.nPos = this.nPos + 1
  1528.                         enddo
  1529.                         if this.getToken() <> ']'
  1530.                                 this.setError('Expected ] at the end of array')
  1531.                                 return null
  1532.                         endif
  1533.                 endif
  1534.                 this.nPos = this.nPos + 1
  1535.         return retval
  1536.        
  1537.  
  1538.         function parseString()
  1539.         local cRetVal, c
  1540.                 if this.getToken()<>'"'
  1541.                         this.setError('Expected "')
  1542.                         return ''
  1543.                 endif
  1544.                 this.nPos = this.nPos + 1       && Eat "
  1545.                 cRetVal = ''
  1546.                 do while .t.
  1547.                         c = this.getChar()
  1548.                         if c==''
  1549.                                 return ''
  1550.                         endif
  1551.                         if c == '"'
  1552.                                 this.nPos = this.nPos + 1
  1553.                                 exit
  1554.                         endif
  1555.                         if c == '\'
  1556.                                 this.nPos = this.nPos + 1
  1557.                                 if (this.nPos>this.nLen)
  1558.                                         this.setError('\\ at the end of input')
  1559.                                         return ''
  1560.                                 endif
  1561.                                 c = this.getChar()
  1562.                                 if c==''
  1563.                                         return ''
  1564.                                 endif
  1565.                                 do case
  1566.                                 case c=='"'
  1567.                                         c='"'
  1568.                                 case c=='\'
  1569.                                         c='\'
  1570.                                 case c=='/'
  1571.                                         c='/'
  1572.                                 case c=='b'
  1573.                                         c=chr(8)
  1574.                                 case c=='t'
  1575.                                         c=chr(9)
  1576.                                 case c=='n'
  1577.                                         c=chr(10)
  1578.                                 case c=='f'
  1579.                                         c=chr(12)
  1580.                                 case c=='r'
  1581.                                         c=chr(13)
  1582.                                 otherwise
  1583.                                         ******* FALTAN LOS UNICODE
  1584.                                         this.setError('Invalid escape sequence in string literal')
  1585.                                         return ''
  1586.                                 endcase
  1587.                         endif
  1588.                         cRetVal = cRetVal + c
  1589.                         this.nPos = this.nPos + 1
  1590.                 enddo
  1591.         return cRetVal
  1592.                                        
  1593.  
  1594.         **** Pendiente numeros con E
  1595.         function parseNumber()
  1596.         local nStartPos,c, isInt, cNumero
  1597.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  1598.                         this.setError('Expected number literal')
  1599.                         return 0
  1600.                 endif
  1601.                 nStartPos = this.nPos
  1602.                 c = this.getChar()
  1603.                 if c == '-'
  1604.                         c = this.nextChar()
  1605.                 endif
  1606.                 if c == '0'
  1607.                         c = this.nextChar()
  1608.                 else
  1609.                         if isdigit(c)
  1610.                                 c = this.nextChar()
  1611.                                 do while isdigit(c)
  1612.                                         c = this.nextChar()
  1613.                                 enddo
  1614.                         else
  1615.                                 this.setError('Expected digit when parsing number')
  1616.                                 return 0
  1617.                         endif
  1618.                 endif
  1619.                
  1620.                 isInt = .t.
  1621.                 if c=='.'
  1622.                         c = this.nextChar()
  1623.                         if isdigit(c)
  1624.                                 c = this.nextChar()
  1625.                                 isInt = .f.
  1626.                                 do while isDigit(c)
  1627.                                         c = this.nextChar()
  1628.                                 enddo
  1629.                         else
  1630.                                 this.setError('Expected digit following dot comma')
  1631.                                 return 0
  1632.                         endif
  1633.                 endif
  1634.                
  1635.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  1636.         return val(cNumero)
  1637.  
  1638.  
  1639.  
  1640.         function getToken()
  1641.         local char1
  1642.                 do while .t.
  1643.                         if this.nPos > this.nLen
  1644.                                 return null
  1645.                         endif
  1646.                         char1 = substr(this.cJson, this.nPos, 1)
  1647.                         if char1==' '
  1648.                                 this.nPos = this.nPos + 1
  1649.                                 loop
  1650.                         endif
  1651.                         return char1
  1652.                 enddo
  1653.         return
  1654.        
  1655.                
  1656.                
  1657.         function getChar()
  1658.                 if this.nPos > this.nLen
  1659.                         this.setError('Unexpected end of JSON stream')
  1660.                         return ''
  1661.                 endif
  1662.         return substr(this.cJson, this.nPos, 1)
  1663.        
  1664.         function nextChar()
  1665.                 this.nPos = this.nPos + 1
  1666.                 if this.nPos > this.nLen
  1667.                         return ''
  1668.                 endif
  1669.         return substr(this.cJson, this.nPos, 1)
  1670.        
  1671.         function setError(cMsg)
  1672.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  1673.         return
  1674.  
  1675.  
  1676.         function fixUnicode(cStr)
  1677.                 cStr = StrTran(cStr,'\u00e1','á')
  1678.                 cStr = StrTran(cStr,'\u00e9','é')
  1679.                 cStr = StrTran(cStr,'\u00ed','í')
  1680.                 cStr = StrTran(cStr,'\u00f3','ó')
  1681.                 cStr = StrTran(cStr,'\u00fa','ú')
  1682.                 cStr = StrTran(cStr,'\u00c1','Á')
  1683.                 cStr = StrTran(cStr,'\u00c9','É')
  1684.                 cStr = StrTran(cStr,'\u00cd','Í')
  1685.                 cStr = StrTran(cStr,'\u00d3','Ó')
  1686.                 cStr = StrTran(cStr,'\u00da','Ú')
  1687.                 cStr = StrTran(cStr,'\u00f1','ń')
  1688.                 cStr = StrTran(cStr,'\u00d1','Ń')
  1689.         return cStr
  1690.  
  1691.  
  1692.  
  1693. enddefine
  1694.  
  1695.  
  1696.  
  1697.  
  1698.  
  1699. *
  1700. * class used to return an array
  1701. *
  1702. define class myArray as custom
  1703.         nSize = 0
  1704.         dimension array[1]
  1705.  
  1706.         function add(xExpr)
  1707.                 this.nSize = this.nSize + 1
  1708.                 dimension this.array[this.nSize]
  1709.                 this.array[this.nSize] = xExpr
  1710.         return
  1711.  
  1712.         function get(n)
  1713.         return this.array[n]
  1714.  
  1715. enddefine
  1716.  
  1717.  
  1718.  
  1719. *
  1720. * class used to simulate an object
  1721. * all properties are prefixed with 'prop' to permit property names like: error, init
  1722. * that already exists like vfp methods
  1723. *
  1724. define class myObj as custom
  1725. Hidden ;
  1726.         ClassLibrary,Comment, ;
  1727.         BaseClass,ControlCount, ;
  1728.         Controls,Objects,Object,;
  1729.         Height,HelpContextID,Left,Name, ;
  1730.         Parent,ParentClass,Picture, ;
  1731.         Tag,Top,WhatsThisHelpID,Width
  1732.                
  1733.         function set(cPropName, xValue)
  1734.                 cPropName = '_'+cPropName
  1735.                 if type('this.'+cPropName)=='U'
  1736.                         this.addProperty(cPropName,xValue)
  1737.                 else
  1738.                         local cmd
  1739.                         cmd = 'this.'+cPropName+'=xValue'
  1740.                         &cmd
  1741.                 endif
  1742.         return
  1743.        
  1744.         procedure get (cPropName)
  1745.                 cPropName = '_'+cPropName
  1746.                 If type('this.'+cPropName)=='U'
  1747.                         return ''
  1748.                 Else
  1749.                         local cmd
  1750.                         cmd = 'return this.'+cPropName
  1751.                         &cmd
  1752.                 endif
  1753.         return ''
  1754. enddefine
  1755.  
  1756.  
  1757.  
  1758.  
  1759.  
  1760. function testJsonClass
  1761.         clear
  1762.         set decimal to 10
  1763.         oJson = newObject('json')
  1764.        
  1765.        
  1766.         ? 'Test Basic Types'
  1767.         ? '----------------'
  1768.         ? oJson.decode('null')
  1769.         ? oJson.decode('true')
  1770.         ? oJson.decode('false')
  1771.         ?
  1772.         ? oJson.decode('791123')
  1773.         ? oJson.decode('791123.45')
  1774.         ? oJson.decode('791123.45.')
  1775.         ? oJson.decode('"nacho gtz"')
  1776.         if not empty(oJson.cError)
  1777.                 ? oJson.cError
  1778.                 return
  1779.         endif
  1780.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  1781.         if not empty(oJson.cError)
  1782.                 ? oJson.cError
  1783.                 return
  1784.         endif
  1785.        
  1786.         ? 'Test Array'
  1787.         ? '----------'
  1788.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  1789.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  1790.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  1791.         nombres = arr.get(1)
  1792.         edades  = arr.get(2)
  1793.         ? nombres.get(1), edades.get(1)
  1794.         ? nombres.get(2), edades.get(2)
  1795.         ? nombres.get(3), edades.get(3)
  1796.         ?
  1797.         ? 'Test Object'
  1798.         ? '-----------'
  1799.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  1800.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  1801.         ? obj._Nombre, obj._Edad, obj._IsGood
  1802.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  1803.         ? obj.get('jsonrpc'), obj._jsonrpc
  1804.         ? obj.get('id'), obj._id
  1805.         ? obj.get('method'), obj._method
  1806.         ? obj._Params.array[1], obj._Params.get(1)
  1807.  
  1808.         ?
  1809.         ? 'Test nested object'
  1810.         ? '------------------'
  1811.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  1812.         obj = oJson.decode(cJson)
  1813.         if not empty(oJson.cError)
  1814.                 ? oJson.cError
  1815.                 return
  1816.         endif
  1817.         ? cJson
  1818.         ? 'method -->',obj._method
  1819.         ? 'usrkey -->',obj._params._data._usrkey
  1820.         ? 'sendto -->',obj._params._data._sendto
  1821.         ? 'name  --->',obj._params._data._name
  1822.         ? 'expires ->',obj._params._data._expires
  1823.  
  1824.         ?
  1825.         ? 'Test empty object'
  1826.         ? '-----------------'
  1827.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  1828.         obj = oJson.decode(cJson)
  1829.         if not empty(oJson.cError)     
  1830.                 ? oJson.cError
  1831.                 return
  1832.         endif
  1833.         ? cJson
  1834.         ? 'result -->',obj._result, obj.get('result')
  1835.         oError = obj.get('error')
  1836.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  1837.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  1838.         ? 'id  ----->',obj._id, obj.get('id')
  1839.         ?  type("oError._code")
  1840.  
  1841.         ?
  1842.         ? 'Probar decode-enconde-decode-encode'
  1843.         ? '------------------------------------'
  1844.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  1845.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  1846.         ? cJson
  1847.         oSmtp = json_decode(cJson)
  1848.         cJson =  json_encode(oSmtp)
  1849.         ? cJson
  1850.         oSmtp = json_decode(cJson)
  1851.         cJson =  json_encode(oSmtp)
  1852.         ? cJson
  1853.  
  1854.         * Probar falla
  1855.         ?
  1856.         ? 'Probar una falla en el json'
  1857.         ? '---------------------------'
  1858.         cJson = ' {"server":"", "user":"", "password":"" ,'
  1859.         oSmtp = json_decode(cJson)
  1860.         if not empty(json_getErrorMsg())
  1861.                 ? json_getErrorMsg()
  1862.         endif
  1863.  
  1864.         ?
  1865.         ? 'Pruebas Finalizadas'
  1866. retur
  1867. * ----------------------------------
  1868. * Ignacio Gutiérrez Torrero
  1869. * SAIT Software Administrativo
  1870. * www.sait.com.mx
  1871. * +52(653)534-8800
  1872. * Monterrey México
  1873. * -----------------------------------
  1874. *
  1875. * Libreria para el manejo de JSON en VFP
  1876. *
  1877. * Gracias a Google por el codigo de Json de Dart
  1878. * Thanks Google for the code in Json Dart
  1879. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  1880. *
  1881. *
  1882. * Codificar y Decodificar JSON
  1883. *
  1884. * Puedes usar las funciones:
  1885. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  1886. *               json_decode(cJson)  te regresa el objeto representado en cJson
  1887. *
  1888. * Tambien puedes usar directamente la clase:
  1889. *
  1890. *       oJson = newobject('json','json.prg')
  1891. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  1892. *       ? oJson.encode(oCliente)
  1893. *       ? oCliente.get('nombre')
  1894. *       ? oCliente.get('apellido')
  1895. *
  1896. *
  1897. * VFPJSON  Encode and Decode JSON for VFP
  1898. * Examples:
  1899. *       oJson = newobject('json','json.prg')
  1900. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  1901. *       ? oJson.encode(oCustomer)
  1902. *       ? oCustomer.get('name')
  1903. *       ? oCustomer.get('lastname')
  1904. *
  1905. *
  1906. lRunTest = .f.
  1907. if lRunTest
  1908.         testJsonClass()
  1909. endif
  1910. return
  1911.  
  1912.  
  1913. function json_encode(xExpr)
  1914.         if vartype(_json)<>'O'
  1915.                 public _json
  1916.                 _json = newobject('json')
  1917.         endif
  1918. return _json.encode(@xExpr)
  1919.  
  1920.  
  1921. function json_decode(cJson)
  1922. local retval
  1923.         if vartype(_json)<>'O'
  1924.                 public _json
  1925.                 _json = newobject('json')
  1926.         endif
  1927.         retval = _json.decode(cJson)
  1928.         if not empty(_json.cError)
  1929.                 return null
  1930.         endif
  1931. return retval
  1932.  
  1933. function json_getErrorMsg()
  1934. return _json.cError
  1935.        
  1936.  
  1937.  
  1938. *
  1939. * json class
  1940. *
  1941. *
  1942. define class json as custom
  1943.  
  1944.  
  1945.         nPos=0
  1946.         nLen=0
  1947.         cJson=''
  1948.         cError=''
  1949.  
  1950.  
  1951.         *
  1952.         * Genera el codigo cJson para parametro que se manda
  1953.         *
  1954.         function encode(xExpr)
  1955.         local cTipo
  1956.                 * Cuando se manda una arreglo,
  1957.                 if type('ALen(xExpr)')=='N'
  1958.                         cTipo = 'A'
  1959.                 Else
  1960.                         cTipo = VarType(xExpr)
  1961.                 Endif
  1962.                
  1963.                 Do Case
  1964.                 Case cTipo=='D'
  1965.                         return '"'+dtos(xExpr)+'"'
  1966.                 Case cTipo=='N'
  1967.                         return Transform(xExpr)
  1968.                 Case cTipo=='L'
  1969.                         return iif(xExpr,'true','false')
  1970.                 Case cTipo=='X'
  1971.                         return 'null'
  1972.                 Case cTipo=='C'
  1973.                         xExpr = allt(xExpr)
  1974.                         xExpr = StrTran(xExpr, '\', '\\' )
  1975.                         xExpr = StrTran(xExpr, '/', '\/' )
  1976.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  1977.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  1978.                         xExpr = StrTran(xExpr, '"', '\"' )
  1979.                         return '"'+xExpr+'"'
  1980.  
  1981.                 case cTipo=='O'
  1982.                         local cProp, cJsonValue, cRetVal, aProp[1]
  1983.                         =AMembers(aProp,xExpr)
  1984.                         cRetVal = ''
  1985.                         for each cProp in aProp
  1986.                                 *? cProp
  1987.                                 *? cRetVal
  1988.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  1989.                                         * algunas propiedades pueden no estar definidas
  1990.                                         * como: activecontrol, parent, etc
  1991.                                         loop
  1992.                                 endif
  1993.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  1994.                                         *
  1995.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  1996.                                         *
  1997.                                         Local i,nTotElem
  1998.                                         cJsonValue = ''
  1999.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  2000.                                         For i=1 to nTotElem
  2001.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  2002.                                         Next
  2003.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  2004.                                 else
  2005.                                         *
  2006.                                         * es otro tipo de dato normal C, N, L
  2007.                                         *
  2008.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  2009.                                 endif
  2010.                                 if left(cProp,1)=='_'
  2011.                                         cProp = substr(cProp,2)
  2012.                                 endif
  2013.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  2014.                         next
  2015.                         return '{' + substr(cRetVal,2) + '}'
  2016.  
  2017.                 case cTipo=='A'
  2018.                         local valor, cRetVal
  2019.                         cRetVal = ''   
  2020.                         for each valor in xExpr
  2021.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  2022.                         next
  2023.                         return  '[' + substr(cRetVal,2) + ']'
  2024.                        
  2025.                 endcase
  2026.  
  2027.         return ''
  2028.  
  2029.  
  2030.  
  2031.  
  2032.  
  2033.         *
  2034.         * regresa un elemento representado por la cadena json que se manda
  2035.         *
  2036.        
  2037.         function decode(cJson)
  2038.         local retValue
  2039.                 cJson = StrTran(cJson,chr(9),'')
  2040.                 cJson = StrTran(cJson,chr(10),'')
  2041.                 cJson = StrTran(cJson,chr(13),'')
  2042.                 cJson = this.fixUnicode(cJson)
  2043.                 this.nPos  = 1
  2044.                 this.cJson = cJson
  2045.                 this.nLen  = len(cJson)
  2046.                 this.cError = ''
  2047.                 retValue = this.parsevalue()
  2048.                 if not empty(this.cError)
  2049.                         return null
  2050.                 endif
  2051.                 if this.getToken()<>null
  2052.                         this.setError('Junk at the end of JSON input')
  2053.                         return null
  2054.                 endif
  2055.         return retValue
  2056.                
  2057.        
  2058.         function parseValue()
  2059.         local token
  2060.                 token = this.getToken()
  2061.                 if token==null
  2062.                         this.setError('Nothing to parse')
  2063.                         return null
  2064.                 endif
  2065.                 do case
  2066.                 case token=='"'
  2067.                         return this.parseString()
  2068.                 case isdigit(token) or token=='-'
  2069.                         return this.parseNumber()
  2070.                 case token=='n'
  2071.                         return this.expectedKeyword('null',null)
  2072.                 case token=='f'
  2073.                         return this.expectedKeyword('false',.f.)
  2074.                 case token=='t'
  2075.                         return this.expectedKeyword('true',.t.)
  2076.                 case token=='{'
  2077.                         return this.parseObject()
  2078.                 case token=='['
  2079.                         return this.parseArray()
  2080.                 otherwise
  2081.                         this.setError('Unexpected token')
  2082.                 endcase
  2083.         return
  2084.                
  2085.        
  2086.         function expectedKeyword(cWord,eValue)
  2087.                 for i=1 to len(cWord)
  2088.                         cChar = this.getChar()
  2089.                         if cChar <> substr(cWord,i,1)
  2090.                                 this.setError("Expected keyword '" + cWord + "'")
  2091.                                 return ''
  2092.                         endif
  2093.                         this.nPos = this.nPos + 1
  2094.                 next
  2095.         return eValue
  2096.        
  2097.  
  2098.         function parseObject()
  2099.         local retval, cPropName, xValue
  2100.                 retval = createObject('myObj')
  2101.                 this.nPos = this.nPos + 1 && Eat {
  2102.                 if this.getToken()<>'}'
  2103.                         do while .t.
  2104.                                 cPropName = this.parseString()
  2105.                                 if not empty(this.cError)
  2106.                                         return null
  2107.                                 endif
  2108.                                 if this.getToken()<>':'
  2109.                                         this.setError("Expected ':' when parsing object")
  2110.                                         return null
  2111.                                 endif
  2112.                                 this.nPos = this.nPos + 1
  2113.                                 xValue = this.parseValue()
  2114.                                 if not empty(this.cError)
  2115.                                         return null
  2116.                                 endif                          
  2117.                                 ** Debug ? cPropName, type('xValue')
  2118.                                 retval.set(cPropName, xValue)
  2119.                                 if this.getToken()<>','
  2120.                                         exit
  2121.                                 endif
  2122.                                 this.nPos = this.nPos + 1
  2123.                         enddo
  2124.                 endif
  2125.                 if this.getToken()<>'}'
  2126.                         this.setError("Expected '}' at the end of object")
  2127.                         return null
  2128.                 endif
  2129.                 this.nPos = this.nPos + 1
  2130.         return retval
  2131.  
  2132.  
  2133.         function parseArray()
  2134.         local retVal, xValue
  2135.                 retval = createObject('MyArray')
  2136.                 this.nPos = this.nPos + 1       && Eat [
  2137.                 if this.getToken() <> ']'
  2138.                         do while .t.
  2139.                                 xValue = this.parseValue()
  2140.                                 if not empty(this.cError)
  2141.                                         return null
  2142.                                 endif
  2143.                                 retval.add( xValue )
  2144.                                 if this.getToken()<>','
  2145.                                         exit
  2146.                                 endif
  2147.                                 this.nPos = this.nPos + 1
  2148.                         enddo
  2149.                         if this.getToken() <> ']'
  2150.                                 this.setError('Expected ] at the end of array')
  2151.                                 return null
  2152.                         endif
  2153.                 endif
  2154.                 this.nPos = this.nPos + 1
  2155.         return retval
  2156.        
  2157.  
  2158.         function parseString()
  2159.         local cRetVal, c
  2160.                 if this.getToken()<>'"'
  2161.                         this.setError('Expected "')
  2162.                         return ''
  2163.                 endif
  2164.                 this.nPos = this.nPos + 1       && Eat "
  2165.                 cRetVal = ''
  2166.                 do while .t.
  2167.                         c = this.getChar()
  2168.                         if c==''
  2169.                                 return ''
  2170.                         endif
  2171.                         if c == '"'
  2172.                                 this.nPos = this.nPos + 1
  2173.                                 exit
  2174.                         endif
  2175.                         if c == '\'
  2176.                                 this.nPos = this.nPos + 1
  2177.                                 if (this.nPos>this.nLen)
  2178.                                         this.setError('\\ at the end of input')
  2179.                                         return ''
  2180.                                 endif
  2181.                                 c = this.getChar()
  2182.                                 if c==''
  2183.                                         return ''
  2184.                                 endif
  2185.                                 do case
  2186.                                 case c=='"'
  2187.                                         c='"'
  2188.                                 case c=='\'
  2189.                                         c='\'
  2190.                                 case c=='/'
  2191.                                         c='/'
  2192.                                 case c=='b'
  2193.                                         c=chr(8)
  2194.                                 case c=='t'
  2195.                                         c=chr(9)
  2196.                                 case c=='n'
  2197.                                         c=chr(10)
  2198.                                 case c=='f'
  2199.                                         c=chr(12)
  2200.                                 case c=='r'
  2201.                                         c=chr(13)
  2202.                                 otherwise
  2203.                                         ******* FALTAN LOS UNICODE
  2204.                                         this.setError('Invalid escape sequence in string literal')
  2205.                                         return ''
  2206.                                 endcase
  2207.                         endif
  2208.                         cRetVal = cRetVal + c
  2209.                         this.nPos = this.nPos + 1
  2210.                 enddo
  2211.         return cRetVal
  2212.                                        
  2213.  
  2214.         **** Pendiente numeros con E
  2215.         function parseNumber()
  2216.         local nStartPos,c, isInt, cNumero
  2217.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  2218.                         this.setError('Expected number literal')
  2219.                         return 0
  2220.                 endif
  2221.                 nStartPos = this.nPos
  2222.                 c = this.getChar()
  2223.                 if c == '-'
  2224.                         c = this.nextChar()
  2225.                 endif
  2226.                 if c == '0'
  2227.                         c = this.nextChar()
  2228.                 else
  2229.                         if isdigit(c)
  2230.                                 c = this.nextChar()
  2231.                                 do while isdigit(c)
  2232.                                         c = this.nextChar()
  2233.                                 enddo
  2234.                         else
  2235.                                 this.setError('Expected digit when parsing number')
  2236.                                 return 0
  2237.                         endif
  2238.                 endif
  2239.                
  2240.                 isInt = .t.
  2241.                 if c=='.'
  2242.                         c = this.nextChar()
  2243.                         if isdigit(c)
  2244.                                 c = this.nextChar()
  2245.                                 isInt = .f.
  2246.                                 do while isDigit(c)
  2247.                                         c = this.nextChar()
  2248.                                 enddo
  2249.                         else
  2250.                                 this.setError('Expected digit following dot comma')
  2251.                                 return 0
  2252.                         endif
  2253.                 endif
  2254.                
  2255.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  2256.         return val(cNumero)
  2257.  
  2258.  
  2259.  
  2260.         function getToken()
  2261.         local char1
  2262.                 do while .t.
  2263.                         if this.nPos > this.nLen
  2264.                                 return null
  2265.                         endif
  2266.                         char1 = substr(this.cJson, this.nPos, 1)
  2267.                         if char1==' '
  2268.                                 this.nPos = this.nPos + 1
  2269.                                 loop
  2270.                         endif
  2271.                         return char1
  2272.                 enddo
  2273.         return
  2274.        
  2275.                
  2276.                
  2277.         function getChar()
  2278.                 if this.nPos > this.nLen
  2279.                         this.setError('Unexpected end of JSON stream')
  2280.                         return ''
  2281.                 endif
  2282.         return substr(this.cJson, this.nPos, 1)
  2283.        
  2284.         function nextChar()
  2285.                 this.nPos = this.nPos + 1
  2286.                 if this.nPos > this.nLen
  2287.                         return ''
  2288.                 endif
  2289.         return substr(this.cJson, this.nPos, 1)
  2290.        
  2291.         function setError(cMsg)
  2292.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  2293.         return
  2294.  
  2295.  
  2296.         function fixUnicode(cStr)
  2297.                 cStr = StrTran(cStr,'\u00e1','á')
  2298.                 cStr = StrTran(cStr,'\u00e9','é')
  2299.                 cStr = StrTran(cStr,'\u00ed','í')
  2300.                 cStr = StrTran(cStr,'\u00f3','ó')
  2301.                 cStr = StrTran(cStr,'\u00fa','ú')
  2302.                 cStr = StrTran(cStr,'\u00c1','Á')
  2303.                 cStr = StrTran(cStr,'\u00c9','É')
  2304.                 cStr = StrTran(cStr,'\u00cd','Í')
  2305.                 cStr = StrTran(cStr,'\u00d3','Ó')
  2306.                 cStr = StrTran(cStr,'\u00da','Ú')
  2307.                 cStr = StrTran(cStr,'\u00f1','ń')
  2308.                 cStr = StrTran(cStr,'\u00d1','Ń')
  2309.         return cStr
  2310.  
  2311.  
  2312.  
  2313. enddefine
  2314.  
  2315.  
  2316.  
  2317.  
  2318.  
  2319. *
  2320. * class used to return an array
  2321. *
  2322. define class myArray as custom
  2323.         nSize = 0
  2324.         dimension array[1]
  2325.  
  2326.         function add(xExpr)
  2327.                 this.nSize = this.nSize + 1
  2328.                 dimension this.array[this.nSize]
  2329.                 this.array[this.nSize] = xExpr
  2330.         return
  2331.  
  2332.         function get(n)
  2333.         return this.array[n]
  2334.  
  2335. enddefine
  2336.  
  2337.  
  2338.  
  2339. *
  2340. * class used to simulate an object
  2341. * all properties are prefixed with 'prop' to permit property names like: error, init
  2342. * that already exists like vfp methods
  2343. *
  2344. define class myObj as custom
  2345. Hidden ;
  2346.         ClassLibrary,Comment, ;
  2347.         BaseClass,ControlCount, ;
  2348.         Controls,Objects,Object,;
  2349.         Height,HelpContextID,Left,Name, ;
  2350.         Parent,ParentClass,Picture, ;
  2351.         Tag,Top,WhatsThisHelpID,Width
  2352.                
  2353.         function set(cPropName, xValue)
  2354.                 cPropName = '_'+cPropName
  2355.                 if type('this.'+cPropName)=='U'
  2356.                         this.addProperty(cPropName,xValue)
  2357.                 else
  2358.                         local cmd
  2359.                         cmd = 'this.'+cPropName+'=xValue'
  2360.                         &cmd
  2361.                 endif
  2362.         return
  2363.        
  2364.         procedure get (cPropName)
  2365.                 cPropName = '_'+cPropName
  2366.                 If type('this.'+cPropName)=='U'
  2367.                         return ''
  2368.                 Else
  2369.                         local cmd
  2370.                         cmd = 'return this.'+cPropName
  2371.                         &cmd
  2372.                 endif
  2373.         return ''
  2374. enddefine
  2375.  
  2376.  
  2377.  
  2378.  
  2379.  
  2380. function testJsonClass
  2381.         clear
  2382.         set decimal to 10
  2383.         oJson = newObject('json')
  2384.        
  2385.        
  2386.         ? 'Test Basic Types'
  2387.         ? '----------------'
  2388.         ? oJson.decode('null')
  2389.         ? oJson.decode('true')
  2390.         ? oJson.decode('false')
  2391.         ?
  2392.         ? oJson.decode('791123')
  2393.         ? oJson.decode('791123.45')
  2394.         ? oJson.decode('791123.45.')
  2395.         ? oJson.decode('"nacho gtz"')
  2396.         if not empty(oJson.cError)
  2397.                 ? oJson.cError
  2398.                 return
  2399.         endif
  2400.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  2401.         if not empty(oJson.cError)
  2402.                 ? oJson.cError
  2403.                 return
  2404.         endif
  2405.        
  2406.         ? 'Test Array'
  2407.         ? '----------'
  2408.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  2409.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  2410.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  2411.         nombres = arr.get(1)
  2412.         edades  = arr.get(2)
  2413.         ? nombres.get(1), edades.get(1)
  2414.         ? nombres.get(2), edades.get(2)
  2415.         ? nombres.get(3), edades.get(3)
  2416.         ?
  2417.         ? 'Test Object'
  2418.         ? '-----------'
  2419.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  2420.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  2421.         ? obj._Nombre, obj._Edad, obj._IsGood
  2422.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  2423.         ? obj.get('jsonrpc'), obj._jsonrpc
  2424.         ? obj.get('id'), obj._id
  2425.         ? obj.get('method'), obj._method
  2426.         ? obj._Params.array[1], obj._Params.get(1)
  2427.  
  2428.         ?
  2429.         ? 'Test nested object'
  2430.         ? '------------------'
  2431.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  2432.         obj = oJson.decode(cJson)
  2433.         if not empty(oJson.cError)
  2434.                 ? oJson.cError
  2435.                 return
  2436.         endif
  2437.         ? cJson
  2438.         ? 'method -->',obj._method
  2439.         ? 'usrkey -->',obj._params._data._usrkey
  2440.         ? 'sendto -->',obj._params._data._sendto
  2441.         ? 'name  --->',obj._params._data._name
  2442.         ? 'expires ->',obj._params._data._expires
  2443.  
  2444.         ?
  2445.         ? 'Test empty object'
  2446.         ? '-----------------'
  2447.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  2448.         obj = oJson.decode(cJson)
  2449.         if not empty(oJson.cError)     
  2450.                 ? oJson.cError
  2451.                 return
  2452.         endif
  2453.         ? cJson
  2454.         ? 'result -->',obj._result, obj.get('result')
  2455.         oError = obj.get('error')
  2456.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  2457.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  2458.         ? 'id  ----->',obj._id, obj.get('id')
  2459.         ?  type("oError._code")
  2460.  
  2461.         ?
  2462.         ? 'Probar decode-enconde-decode-encode'
  2463.         ? '------------------------------------'
  2464.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  2465.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  2466.         ? cJson
  2467.         oSmtp = json_decode(cJson)
  2468.         cJson =  json_encode(oSmtp)
  2469.         ? cJson
  2470.         oSmtp = json_decode(cJson)
  2471.         cJson =  json_encode(oSmtp)
  2472.         ? cJson
  2473.  
  2474.         * Probar falla
  2475.         ?
  2476.         ? 'Probar una falla en el json'
  2477.         ? '---------------------------'
  2478.         cJson = ' {"server":"", "user":"", "password":"" ,'
  2479.         oSmtp = json_decode(cJson)
  2480.         if not empty(json_getErrorMsg())
  2481.                 ? json_getErrorMsg()
  2482.         endif
  2483.  
  2484.         ?
  2485.         ? 'Pruebas Finalizadas'
  2486. retur
  2487. * Ignacio Gutiérrez Torrero
  2488. * SAIT Software Administrativo
  2489. * www.sait.com.mx
  2490. * +52(653)534-8800
  2491. * Monterrey México
  2492. * -----------------------------------
  2493. *
  2494. * Libreria para el manejo de JSON en VFP
  2495. *
  2496. * Gracias a Google por el codigo de Json de Dart
  2497. * Thanks Google for the code in Json Dart
  2498. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  2499. *
  2500. *
  2501. * Codificar y Decodificar JSON
  2502. *
  2503. * Puedes usar las funciones:
  2504. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  2505. *               json_decode(cJson)  te regresa el objeto representado en cJson
  2506. *
  2507. * Tambien puedes usar directamente la clase:
  2508. *
  2509. *       oJson = newobject('json','json.prg')
  2510. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  2511. *       ? oJson.encode(oCliente)
  2512. *       ? oCliente.get('nombre')
  2513. *       ? oCliente.get('apellido')
  2514. *
  2515. *
  2516. * VFPJSON  Encode and Decode JSON for VFP
  2517. * Examples:
  2518. *       oJson = newobject('json','json.prg')
  2519. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  2520. *       ? oJson.encode(oCustomer)
  2521. *       ? oCustomer.get('name')
  2522. *       ? oCustomer.get('lastname')
  2523. *
  2524. *
  2525. lRunTest = .f.
  2526. if lRunTest
  2527.         testJsonClass()
  2528. endif
  2529. return
  2530.  
  2531.  
  2532. function json_encode(xExpr)
  2533.         if vartype(_json)<>'O'
  2534.                 public _json
  2535.                 _json = newobject('json')
  2536.         endif
  2537. return _json.encode(@xExpr)
  2538.  
  2539.  
  2540. function json_decode(cJson)
  2541. local retval
  2542.         if vartype(_json)<>'O'
  2543.                 public _json
  2544.                 _json = newobject('json')
  2545.         endif
  2546.         retval = _json.decode(cJson)
  2547.         if not empty(_json.cError)
  2548.                 return null
  2549.         endif
  2550. return retval
  2551.  
  2552. function json_getErrorMsg()
  2553. return _json.cError
  2554.        
  2555.  
  2556.  
  2557. *
  2558. * json class
  2559. *
  2560. *
  2561. define class json as custom
  2562.  
  2563.  
  2564.         nPos=0
  2565.         nLen=0
  2566.         cJson=''
  2567.         cError=''
  2568.  
  2569.  
  2570.         *
  2571.         * Genera el codigo cJson para parametro que se manda
  2572.         *
  2573.         function encode(xExpr)
  2574.         local cTipo
  2575.                 * Cuando se manda una arreglo,
  2576.                 if type('ALen(xExpr)')=='N'
  2577.                         cTipo = 'A'
  2578.                 Else
  2579.                         cTipo = VarType(xExpr)
  2580.                 Endif
  2581.                
  2582.                 Do Case
  2583.                 Case cTipo=='D'
  2584.                         return '"'+dtos(xExpr)+'"'
  2585.                 Case cTipo=='N'
  2586.                         return Transform(xExpr)
  2587.                 Case cTipo=='L'
  2588.                         return iif(xExpr,'true','false')
  2589.                 Case cTipo=='X'
  2590.                         return 'null'
  2591.                 Case cTipo=='C'
  2592.                         xExpr = allt(xExpr)
  2593.                         xExpr = StrTran(xExpr, '\', '\\' )
  2594.                         xExpr = StrTran(xExpr, '/', '\/' )
  2595.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  2596.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  2597.                         xExpr = StrTran(xExpr, '"', '\"' )
  2598.                         return '"'+xExpr+'"'
  2599.  
  2600.                 case cTipo=='O'
  2601.                         local cProp, cJsonValue, cRetVal, aProp[1]
  2602.                         =AMembers(aProp,xExpr)
  2603.                         cRetVal = ''
  2604.                         for each cProp in aProp
  2605.                                 *? cProp
  2606.                                 *? cRetVal
  2607.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  2608.                                         * algunas propiedades pueden no estar definidas
  2609.                                         * como: activecontrol, parent, etc
  2610.                                         loop
  2611.                                 endif
  2612.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  2613.                                         *
  2614.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  2615.                                         *
  2616.                                         Local i,nTotElem
  2617.                                         cJsonValue = ''
  2618.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  2619.                                         For i=1 to nTotElem
  2620.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  2621.                                         Next
  2622.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  2623.                                 else
  2624.                                         *
  2625.                                         * es otro tipo de dato normal C, N, L
  2626.                                         *
  2627.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  2628.                                 endif
  2629.                                 if left(cProp,1)=='_'
  2630.                                         cProp = substr(cProp,2)
  2631.                                 endif
  2632.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  2633.                         next
  2634.                         return '{' + substr(cRetVal,2) + '}'
  2635.  
  2636.                 case cTipo=='A'
  2637.                         local valor, cRetVal
  2638.                         cRetVal = ''   
  2639.                         for each valor in xExpr
  2640.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  2641.                         next
  2642.                         return  '[' + substr(cRetVal,2) + ']'
  2643.                        
  2644.                 endcase
  2645.  
  2646.         return ''
  2647.  
  2648.  
  2649.  
  2650.  
  2651.  
  2652.         *
  2653.         * regresa un elemento representado por la cadena json que se manda
  2654.         *
  2655.        
  2656.         function decode(cJson)
  2657.         local retValue
  2658.                 cJson = StrTran(cJson,chr(9),'')
  2659.                 cJson = StrTran(cJson,chr(10),'')
  2660.                 cJson = StrTran(cJson,chr(13),'')
  2661.                 cJson = this.fixUnicode(cJson)
  2662.                 this.nPos  = 1
  2663.                 this.cJson = cJson
  2664.                 this.nLen  = len(cJson)
  2665.                 this.cError = ''
  2666.                 retValue = this.parsevalue()
  2667.                 if not empty(this.cError)
  2668.                         return null
  2669.                 endif
  2670.                 if this.getToken()<>null
  2671.                         this.setError('Junk at the end of JSON input')
  2672.                         return null
  2673.                 endif
  2674.         return retValue
  2675.                
  2676.        
  2677.         function parseValue()
  2678.         local token
  2679.                 token = this.getToken()
  2680.                 if token==null
  2681.                         this.setError('Nothing to parse')
  2682.                         return null
  2683.                 endif
  2684.                 do case
  2685.                 case token=='"'
  2686.                         return this.parseString()
  2687.                 case isdigit(token) or token=='-'
  2688.                         return this.parseNumber()
  2689.                 case token=='n'
  2690.                         return this.expectedKeyword('null',null)
  2691.                 case token=='f'
  2692.                         return this.expectedKeyword('false',.f.)
  2693.                 case token=='t'
  2694.                         return this.expectedKeyword('true',.t.)
  2695.                 case token=='{'
  2696.                         return this.parseObject()
  2697.                 case token=='['
  2698.                         return this.parseArray()
  2699.                 otherwise
  2700.                         this.setError('Unexpected token')
  2701.                 endcase
  2702.         return
  2703.                
  2704.        
  2705.         function expectedKeyword(cWord,eValue)
  2706.                 for i=1 to len(cWord)
  2707.                         cChar = this.getChar()
  2708.                         if cChar <> substr(cWord,i,1)
  2709.                                 this.setError("Expected keyword '" + cWord + "'")
  2710.                                 return ''
  2711.                         endif
  2712.                         this.nPos = this.nPos + 1
  2713.                 next
  2714.         return eValue
  2715.        
  2716.  
  2717.         function parseObject()
  2718.         local retval, cPropName, xValue
  2719.                 retval = createObject('myObj')
  2720.                 this.nPos = this.nPos + 1 && Eat {
  2721.                 if this.getToken()<>'}'
  2722.                         do while .t.
  2723.                                 cPropName = this.parseString()
  2724.                                 if not empty(this.cError)
  2725.                                         return null
  2726.                                 endif
  2727.                                 if this.getToken()<>':'
  2728.                                         this.setError("Expected ':' when parsing object")
  2729.                                         return null
  2730.                                 endif
  2731.                                 this.nPos = this.nPos + 1
  2732.                                 xValue = this.parseValue()
  2733.                                 if not empty(this.cError)
  2734.                                         return null
  2735.                                 endif                          
  2736.                                 ** Debug ? cPropName, type('xValue')
  2737.                                 retval.set(cPropName, xValue)
  2738.                                 if this.getToken()<>','
  2739.                                         exit
  2740.                                 endif
  2741.                                 this.nPos = this.nPos + 1
  2742.                         enddo
  2743.                 endif
  2744.                 if this.getToken()<>'}'
  2745.                         this.setError("Expected '}' at the end of object")
  2746.                         return null
  2747.                 endif
  2748.                 this.nPos = this.nPos + 1
  2749.         return retval
  2750.  
  2751.  
  2752.         function parseArray()
  2753.         local retVal, xValue
  2754.                 retval = createObject('MyArray')
  2755.                 this.nPos = this.nPos + 1       && Eat [
  2756.                 if this.getToken() <> ']'
  2757.                         do while .t.
  2758.                                 xValue = this.parseValue()
  2759.                                 if not empty(this.cError)
  2760.                                         return null
  2761.                                 endif
  2762.                                 retval.add( xValue )
  2763.                                 if this.getToken()<>','
  2764.                                         exit
  2765.                                 endif
  2766.                                 this.nPos = this.nPos + 1
  2767.                         enddo
  2768.                         if this.getToken() <> ']'
  2769.                                 this.setError('Expected ] at the end of array')
  2770.                                 return null
  2771.                         endif
  2772.                 endif
  2773.                 this.nPos = this.nPos + 1
  2774.         return retval
  2775.        
  2776.  
  2777.         function parseString()
  2778.         local cRetVal, c
  2779.                 if this.getToken()<>'"'
  2780.                         this.setError('Expected "')
  2781.                         return ''
  2782.                 endif
  2783.                 this.nPos = this.nPos + 1       && Eat "
  2784.                 cRetVal = ''
  2785.                 do while .t.
  2786.                         c = this.getChar()
  2787.                         if c==''
  2788.                                 return ''
  2789.                         endif
  2790.                         if c == '"'
  2791.                                 this.nPos = this.nPos + 1
  2792.                                 exit
  2793.                         endif
  2794.                         if c == '\'
  2795.                                 this.nPos = this.nPos + 1
  2796.                                 if (this.nPos>this.nLen)
  2797.                                         this.setError('\\ at the end of input')
  2798.                                         return ''
  2799.                                 endif
  2800.                                 c = this.getChar()
  2801.                                 if c==''
  2802.                                         return ''
  2803.                                 endif
  2804.                                 do case
  2805.                                 case c=='"'
  2806.                                         c='"'
  2807.                                 case c=='\'
  2808.                                         c='\'
  2809.                                 case c=='/'
  2810.                                         c='/'
  2811.                                 case c=='b'
  2812.                                         c=chr(8)
  2813.                                 case c=='t'
  2814.                                         c=chr(9)
  2815.                                 case c=='n'
  2816.                                         c=chr(10)
  2817.                                 case c=='f'
  2818.                                         c=chr(12)
  2819.                                 case c=='r'
  2820.                                         c=chr(13)
  2821.                                 otherwise
  2822.                                         ******* FALTAN LOS UNICODE
  2823.                                         this.setError('Invalid escape sequence in string literal')
  2824.                                         return ''
  2825.                                 endcase
  2826.                         endif
  2827.                         cRetVal = cRetVal + c
  2828.                         this.nPos = this.nPos + 1
  2829.                 enddo
  2830.         return cRetVal
  2831.                                        
  2832.  
  2833.         **** Pendiente numeros con E
  2834.         function parseNumber()
  2835.         local nStartPos,c, isInt, cNumero
  2836.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  2837.                         this.setError('Expected number literal')
  2838.                         return 0
  2839.                 endif
  2840.                 nStartPos = this.nPos
  2841.                 c = this.getChar()
  2842.                 if c == '-'
  2843.                         c = this.nextChar()
  2844.                 endif
  2845.                 if c == '0'
  2846.                         c = this.nextChar()
  2847.                 else
  2848.                         if isdigit(c)
  2849.                                 c = this.nextChar()
  2850.                                 do while isdigit(c)
  2851.                                         c = this.nextChar()
  2852.                                 enddo
  2853.                         else
  2854.                                 this.setError('Expected digit when parsing number')
  2855.                                 return 0
  2856.                         endif
  2857.                 endif
  2858.                
  2859.                 isInt = .t.
  2860.                 if c=='.'
  2861.                         c = this.nextChar()
  2862.                         if isdigit(c)
  2863.                                 c = this.nextChar()
  2864.                                 isInt = .f.
  2865.                                 do while isDigit(c)
  2866.                                         c = this.nextChar()
  2867.                                 enddo
  2868.                         else
  2869.                                 this.setError('Expected digit following dot comma')
  2870.                                 return 0
  2871.                         endif
  2872.                 endif
  2873.                
  2874.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  2875.         return val(cNumero)
  2876.  
  2877.  
  2878.  
  2879.         function getToken()
  2880.         local char1
  2881.                 do while .t.
  2882.                         if this.nPos > this.nLen
  2883.                                 return null
  2884.                         endif
  2885.                         char1 = substr(this.cJson, this.nPos, 1)
  2886.                         if char1==' '
  2887.                                 this.nPos = this.nPos + 1
  2888.                                 loop
  2889.                         endif
  2890.                         return char1
  2891.                 enddo
  2892.         return
  2893.        
  2894.                
  2895.                
  2896.         function getChar()
  2897.                 if this.nPos > this.nLen
  2898.                         this.setError('Unexpected end of JSON stream')
  2899.                         return ''
  2900.                 endif
  2901.         return substr(this.cJson, this.nPos, 1)
  2902.        
  2903.         function nextChar()
  2904.                 this.nPos = this.nPos + 1
  2905.                 if this.nPos > this.nLen
  2906.                         return ''
  2907.                 endif
  2908.         return substr(this.cJson, this.nPos, 1)
  2909.        
  2910.         function setError(cMsg)
  2911.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  2912.         return
  2913.  
  2914.  
  2915.         function fixUnicode(cStr)
  2916.                 cStr = StrTran(cStr,'\u00e1','á')
  2917.                 cStr = StrTran(cStr,'\u00e9','é')
  2918.                 cStr = StrTran(cStr,'\u00ed','í')
  2919.                 cStr = StrTran(cStr,'\u00f3','ó')
  2920.                 cStr = StrTran(cStr,'\u00fa','ú')
  2921.                 cStr = StrTran(cStr,'\u00c1','Á')
  2922.                 cStr = StrTran(cStr,'\u00c9','É')
  2923.                 cStr = StrTran(cStr,'\u00cd','Í')
  2924.                 cStr = StrTran(cStr,'\u00d3','Ó')
  2925.                 cStr = StrTran(cStr,'\u00da','Ú')
  2926.                 cStr = StrTran(cStr,'\u00f1','ń')
  2927.                 cStr = StrTran(cStr,'\u00d1','Ń')
  2928.         return cStr
  2929.  
  2930.  
  2931.  
  2932. enddefine
  2933.  
  2934.  
  2935.  
  2936.  
  2937.  
  2938. *
  2939. * class used to return an array
  2940. *
  2941. define class myArray as custom
  2942.         nSize = 0
  2943.         dimension array[1]
  2944.  
  2945.         function add(xExpr)
  2946.                 this.nSize = this.nSize + 1
  2947.                 dimension this.array[this.nSize]
  2948.                 this.array[this.nSize] = xExpr
  2949.         return
  2950.  
  2951.         function get(n)
  2952.         return this.array[n]
  2953.  
  2954. enddefine
  2955.  
  2956.  
  2957.  
  2958. *
  2959. * class used to simulate an object
  2960. * all properties are prefixed with 'prop' to permit property names like: error, init
  2961. * that already exists like vfp methods
  2962. *
  2963. define class myObj as custom
  2964. Hidden ;
  2965.         ClassLibrary,Comment, ;
  2966.         BaseClass,ControlCount, ;
  2967.         Controls,Objects,Object,;
  2968.         Height,HelpContextID,Left,Name, ;
  2969.         Parent,ParentClass,Picture, ;
  2970.         Tag,Top,WhatsThisHelpID,Width
  2971.                
  2972.         function set(cPropName, xValue)
  2973.                 cPropName = '_'+cPropName
  2974.                 if type('this.'+cPropName)=='U'
  2975.                         this.addProperty(cPropName,xValue)
  2976.                 else
  2977.                         local cmd
  2978.                         cmd = 'this.'+cPropName+'=xValue'
  2979.                         &cmd
  2980.                 endif
  2981.         return
  2982.        
  2983.         procedure get (cPropName)
  2984.                 cPropName = '_'+cPropName
  2985.                 If type('this.'+cPropName)=='U'
  2986.                         return ''
  2987.                 Else
  2988.                         local cmd
  2989.                         cmd = 'return this.'+cPropName
  2990.                         &cmd
  2991.                 endif
  2992.         return ''
  2993. enddefine
  2994.  
  2995.  
  2996.  
  2997.  
  2998.  
  2999. function testJsonClass
  3000.         clear
  3001.         set decimal to 10
  3002.         oJson = newObject('json')
  3003.        
  3004.        
  3005.         ? 'Test Basic Types'
  3006.         ? '----------------'
  3007.         ? oJson.decode('null')
  3008.         ? oJson.decode('true')
  3009.         ? oJson.decode('false')
  3010.         ?
  3011.         ? oJson.decode('791123')
  3012.         ? oJson.decode('791123.45')
  3013.         ? oJson.decode('791123.45.')
  3014.         ? oJson.decode('"nacho gtz"')
  3015.         if not empty(oJson.cError)
  3016.                 ? oJson.cError
  3017.                 return
  3018.         endif
  3019.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  3020.         if not empty(oJson.cError)
  3021.                 ? oJson.cError
  3022.                 return
  3023.         endif
  3024.        
  3025.         ? 'Test Array'
  3026.         ? '----------'
  3027.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  3028.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  3029.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  3030.         nombres = arr.get(1)
  3031.         edades  = arr.get(2)
  3032.         ? nombres.get(1), edades.get(1)
  3033.         ? nombres.get(2), edades.get(2)
  3034.         ? nombres.get(3), edades.get(3)
  3035.         ?
  3036.         ? 'Test Object'
  3037.         ? '-----------'
  3038.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  3039.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  3040.         ? obj._Nombre, obj._Edad, obj._IsGood
  3041.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  3042.         ? obj.get('jsonrpc'), obj._jsonrpc
  3043.         ? obj.get('id'), obj._id
  3044.         ? obj.get('method'), obj._method
  3045.         ? obj._Params.array[1], obj._Params.get(1)
  3046.  
  3047.         ?
  3048.         ? 'Test nested object'
  3049.         ? '------------------'
  3050.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  3051.         obj = oJson.decode(cJson)
  3052.         if not empty(oJson.cError)
  3053.                 ? oJson.cError
  3054.                 return
  3055.         endif
  3056.         ? cJson
  3057.         ? 'method -->',obj._method
  3058.         ? 'usrkey -->',obj._params._data._usrkey
  3059.         ? 'sendto -->',obj._params._data._sendto
  3060.         ? 'name  --->',obj._params._data._name
  3061.         ? 'expires ->',obj._params._data._expires
  3062.  
  3063.         ?
  3064.         ? 'Test empty object'
  3065.         ? '-----------------'
  3066.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  3067.         obj = oJson.decode(cJson)
  3068.         if not empty(oJson.cError)     
  3069.                 ? oJson.cError
  3070.                 return
  3071.         endif
  3072.         ? cJson
  3073.         ? 'result -->',obj._result, obj.get('result')
  3074.         oError = obj.get('error')
  3075.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  3076.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  3077.         ? 'id  ----->',obj._id, obj.get('id')
  3078.         ?  type("oError._code")
  3079.  
  3080.         ?
  3081.         ? 'Probar decode-enconde-decode-encode'
  3082.         ? '------------------------------------'
  3083.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  3084.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  3085.         ? cJson
  3086.         oSmtp = json_decode(cJson)
  3087.         cJson =  json_encode(oSmtp)
  3088.         ? cJson
  3089.         oSmtp = json_decode(cJson)
  3090.         cJson =  json_encode(oSmtp)
  3091.         ? cJson
  3092.  
  3093.         * Probar falla
  3094.         ?
  3095.         ? 'Probar una falla en el json'
  3096.         ? '---------------------------'
  3097.         cJson = ' {"server":"", "user":"", "password":"" ,'
  3098.         oSmtp = json_decode(cJson)
  3099.         if not empty(json_getErrorMsg())
  3100.                 ? json_getErrorMsg()
  3101.         endif
  3102.  
  3103.         ?
  3104.         ? 'Pruebas Finalizadas'
  3105. retur
  3106. * SAIT Software Administrativo
  3107. * www.sait.com.mx
  3108. * +52(653)534-8800
  3109. * Monterrey México
  3110. * -----------------------------------
  3111. *
  3112. * Libreria para el manejo de JSON en VFP
  3113. *
  3114. * Gracias a Google por el codigo de Json de Dart
  3115. * Thanks Google for the code in Json Dart
  3116. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  3117. *
  3118. *
  3119. * Codificar y Decodificar JSON
  3120. *
  3121. * Puedes usar las funciones:
  3122. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  3123. *               json_decode(cJson)  te regresa el objeto representado en cJson
  3124. *
  3125. * Tambien puedes usar directamente la clase:
  3126. *
  3127. *       oJson = newobject('json','json.prg')
  3128. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  3129. *       ? oJson.encode(oCliente)
  3130. *       ? oCliente.get('nombre')
  3131. *       ? oCliente.get('apellido')
  3132. *
  3133. *
  3134. * VFPJSON  Encode and Decode JSON for VFP
  3135. * Examples:
  3136. *       oJson = newobject('json','json.prg')
  3137. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  3138. *       ? oJson.encode(oCustomer)
  3139. *       ? oCustomer.get('name')
  3140. *       ? oCustomer.get('lastname')
  3141. *
  3142. *
  3143. lRunTest = .f.
  3144. if lRunTest
  3145.         testJsonClass()
  3146. endif
  3147. return
  3148.  
  3149.  
  3150. function json_encode(xExpr)
  3151.         if vartype(_json)<>'O'
  3152.                 public _json
  3153.                 _json = newobject('json')
  3154.         endif
  3155. return _json.encode(@xExpr)
  3156.  
  3157.  
  3158. function json_decode(cJson)
  3159. local retval
  3160.         if vartype(_json)<>'O'
  3161.                 public _json
  3162.                 _json = newobject('json')
  3163.         endif
  3164.         retval = _json.decode(cJson)
  3165.         if not empty(_json.cError)
  3166.                 return null
  3167.         endif
  3168. return retval
  3169.  
  3170. function json_getErrorMsg()
  3171. return _json.cError
  3172.        
  3173.  
  3174.  
  3175. *
  3176. * json class
  3177. *
  3178. *
  3179. define class json as custom
  3180.  
  3181.  
  3182.         nPos=0
  3183.         nLen=0
  3184.         cJson=''
  3185.         cError=''
  3186.  
  3187.  
  3188.         *
  3189.         * Genera el codigo cJson para parametro que se manda
  3190.         *
  3191.         function encode(xExpr)
  3192.         local cTipo
  3193.                 * Cuando se manda una arreglo,
  3194.                 if type('ALen(xExpr)')=='N'
  3195.                         cTipo = 'A'
  3196.                 Else
  3197.                         cTipo = VarType(xExpr)
  3198.                 Endif
  3199.                
  3200.                 Do Case
  3201.                 Case cTipo=='D'
  3202.                         return '"'+dtos(xExpr)+'"'
  3203.                 Case cTipo=='N'
  3204.                         return Transform(xExpr)
  3205.                 Case cTipo=='L'
  3206.                         return iif(xExpr,'true','false')
  3207.                 Case cTipo=='X'
  3208.                         return 'null'
  3209.                 Case cTipo=='C'
  3210.                         xExpr = allt(xExpr)
  3211.                         xExpr = StrTran(xExpr, '\', '\\' )
  3212.                         xExpr = StrTran(xExpr, '/', '\/' )
  3213.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  3214.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  3215.                         xExpr = StrTran(xExpr, '"', '\"' )
  3216.                         return '"'+xExpr+'"'
  3217.  
  3218.                 case cTipo=='O'
  3219.                         local cProp, cJsonValue, cRetVal, aProp[1]
  3220.                         =AMembers(aProp,xExpr)
  3221.                         cRetVal = ''
  3222.                         for each cProp in aProp
  3223.                                 *? cProp
  3224.                                 *? cRetVal
  3225.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  3226.                                         * algunas propiedades pueden no estar definidas
  3227.                                         * como: activecontrol, parent, etc
  3228.                                         loop
  3229.                                 endif
  3230.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  3231.                                         *
  3232.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  3233.                                         *
  3234.                                         Local i,nTotElem
  3235.                                         cJsonValue = ''
  3236.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  3237.                                         For i=1 to nTotElem
  3238.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  3239.                                         Next
  3240.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  3241.                                 else
  3242.                                         *
  3243.                                         * es otro tipo de dato normal C, N, L
  3244.                                         *
  3245.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  3246.                                 endif
  3247.                                 if left(cProp,1)=='_'
  3248.                                         cProp = substr(cProp,2)
  3249.                                 endif
  3250.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  3251.                         next
  3252.                         return '{' + substr(cRetVal,2) + '}'
  3253.  
  3254.                 case cTipo=='A'
  3255.                         local valor, cRetVal
  3256.                         cRetVal = ''   
  3257.                         for each valor in xExpr
  3258.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  3259.                         next
  3260.                         return  '[' + substr(cRetVal,2) + ']'
  3261.                        
  3262.                 endcase
  3263.  
  3264.         return ''
  3265.  
  3266.  
  3267.  
  3268.  
  3269.  
  3270.         *
  3271.         * regresa un elemento representado por la cadena json que se manda
  3272.         *
  3273.        
  3274.         function decode(cJson)
  3275.         local retValue
  3276.                 cJson = StrTran(cJson,chr(9),'')
  3277.                 cJson = StrTran(cJson,chr(10),'')
  3278.                 cJson = StrTran(cJson,chr(13),'')
  3279.                 cJson = this.fixUnicode(cJson)
  3280.                 this.nPos  = 1
  3281.                 this.cJson = cJson
  3282.                 this.nLen  = len(cJson)
  3283.                 this.cError = ''
  3284.                 retValue = this.parsevalue()
  3285.                 if not empty(this.cError)
  3286.                         return null
  3287.                 endif
  3288.                 if this.getToken()<>null
  3289.                         this.setError('Junk at the end of JSON input')
  3290.                         return null
  3291.                 endif
  3292.         return retValue
  3293.                
  3294.        
  3295.         function parseValue()
  3296.         local token
  3297.                 token = this.getToken()
  3298.                 if token==null
  3299.                         this.setError('Nothing to parse')
  3300.                         return null
  3301.                 endif
  3302.                 do case
  3303.                 case token=='"'
  3304.                         return this.parseString()
  3305.                 case isdigit(token) or token=='-'
  3306.                         return this.parseNumber()
  3307.                 case token=='n'
  3308.                         return this.expectedKeyword('null',null)
  3309.                 case token=='f'
  3310.                         return this.expectedKeyword('false',.f.)
  3311.                 case token=='t'
  3312.                         return this.expectedKeyword('true',.t.)
  3313.                 case token=='{'
  3314.                         return this.parseObject()
  3315.                 case token=='['
  3316.                         return this.parseArray()
  3317.                 otherwise
  3318.                         this.setError('Unexpected token')
  3319.                 endcase
  3320.         return
  3321.                
  3322.        
  3323.         function expectedKeyword(cWord,eValue)
  3324.                 for i=1 to len(cWord)
  3325.                         cChar = this.getChar()
  3326.                         if cChar <> substr(cWord,i,1)
  3327.                                 this.setError("Expected keyword '" + cWord + "'")
  3328.                                 return ''
  3329.                         endif
  3330.                         this.nPos = this.nPos + 1
  3331.                 next
  3332.         return eValue
  3333.        
  3334.  
  3335.         function parseObject()
  3336.         local retval, cPropName, xValue
  3337.                 retval = createObject('myObj')
  3338.                 this.nPos = this.nPos + 1 && Eat {
  3339.                 if this.getToken()<>'}'
  3340.                         do while .t.
  3341.                                 cPropName = this.parseString()
  3342.                                 if not empty(this.cError)
  3343.                                         return null
  3344.                                 endif
  3345.                                 if this.getToken()<>':'
  3346.                                         this.setError("Expected ':' when parsing object")
  3347.                                         return null
  3348.                                 endif
  3349.                                 this.nPos = this.nPos + 1
  3350.                                 xValue = this.parseValue()
  3351.                                 if not empty(this.cError)
  3352.                                         return null
  3353.                                 endif                          
  3354.                                 ** Debug ? cPropName, type('xValue')
  3355.                                 retval.set(cPropName, xValue)
  3356.                                 if this.getToken()<>','
  3357.                                         exit
  3358.                                 endif
  3359.                                 this.nPos = this.nPos + 1
  3360.                         enddo
  3361.                 endif
  3362.                 if this.getToken()<>'}'
  3363.                         this.setError("Expected '}' at the end of object")
  3364.                         return null
  3365.                 endif
  3366.                 this.nPos = this.nPos + 1
  3367.         return retval
  3368.  
  3369.  
  3370.         function parseArray()
  3371.         local retVal, xValue
  3372.                 retval = createObject('MyArray')
  3373.                 this.nPos = this.nPos + 1       && Eat [
  3374.                 if this.getToken() <> ']'
  3375.                         do while .t.
  3376.                                 xValue = this.parseValue()
  3377.                                 if not empty(this.cError)
  3378.                                         return null
  3379.                                 endif
  3380.                                 retval.add( xValue )
  3381.                                 if this.getToken()<>','
  3382.                                         exit
  3383.                                 endif
  3384.                                 this.nPos = this.nPos + 1
  3385.                         enddo
  3386.                         if this.getToken() <> ']'
  3387.                                 this.setError('Expected ] at the end of array')
  3388.                                 return null
  3389.                         endif
  3390.                 endif
  3391.                 this.nPos = this.nPos + 1
  3392.         return retval
  3393.        
  3394.  
  3395.         function parseString()
  3396.         local cRetVal, c
  3397.                 if this.getToken()<>'"'
  3398.                         this.setError('Expected "')
  3399.                         return ''
  3400.                 endif
  3401.                 this.nPos = this.nPos + 1       && Eat "
  3402.                 cRetVal = ''
  3403.                 do while .t.
  3404.                         c = this.getChar()
  3405.                         if c==''
  3406.                                 return ''
  3407.                         endif
  3408.                         if c == '"'
  3409.                                 this.nPos = this.nPos + 1
  3410.                                 exit
  3411.                         endif
  3412.                         if c == '\'
  3413.                                 this.nPos = this.nPos + 1
  3414.                                 if (this.nPos>this.nLen)
  3415.                                         this.setError('\\ at the end of input')
  3416.                                         return ''
  3417.                                 endif
  3418.                                 c = this.getChar()
  3419.                                 if c==''
  3420.                                         return ''
  3421.                                 endif
  3422.                                 do case
  3423.                                 case c=='"'
  3424.                                         c='"'
  3425.                                 case c=='\'
  3426.                                         c='\'
  3427.                                 case c=='/'
  3428.                                         c='/'
  3429.                                 case c=='b'
  3430.                                         c=chr(8)
  3431.                                 case c=='t'
  3432.                                         c=chr(9)
  3433.                                 case c=='n'
  3434.                                         c=chr(10)
  3435.                                 case c=='f'
  3436.                                         c=chr(12)
  3437.                                 case c=='r'
  3438.                                         c=chr(13)
  3439.                                 otherwise
  3440.                                         ******* FALTAN LOS UNICODE
  3441.                                         this.setError('Invalid escape sequence in string literal')
  3442.                                         return ''
  3443.                                 endcase
  3444.                         endif
  3445.                         cRetVal = cRetVal + c
  3446.                         this.nPos = this.nPos + 1
  3447.                 enddo
  3448.         return cRetVal
  3449.                                        
  3450.  
  3451.         **** Pendiente numeros con E
  3452.         function parseNumber()
  3453.         local nStartPos,c, isInt, cNumero
  3454.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  3455.                         this.setError('Expected number literal')
  3456.                         return 0
  3457.                 endif
  3458.                 nStartPos = this.nPos
  3459.                 c = this.getChar()
  3460.                 if c == '-'
  3461.                         c = this.nextChar()
  3462.                 endif
  3463.                 if c == '0'
  3464.                         c = this.nextChar()
  3465.                 else
  3466.                         if isdigit(c)
  3467.                                 c = this.nextChar()
  3468.                                 do while isdigit(c)
  3469.                                         c = this.nextChar()
  3470.                                 enddo
  3471.                         else
  3472.                                 this.setError('Expected digit when parsing number')
  3473.                                 return 0
  3474.                         endif
  3475.                 endif
  3476.                
  3477.                 isInt = .t.
  3478.                 if c=='.'
  3479.                         c = this.nextChar()
  3480.                         if isdigit(c)
  3481.                                 c = this.nextChar()
  3482.                                 isInt = .f.
  3483.                                 do while isDigit(c)
  3484.                                         c = this.nextChar()
  3485.                                 enddo
  3486.                         else
  3487.                                 this.setError('Expected digit following dot comma')
  3488.                                 return 0
  3489.                         endif
  3490.                 endif
  3491.                
  3492.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  3493.         return val(cNumero)
  3494.  
  3495.  
  3496.  
  3497.         function getToken()
  3498.         local char1
  3499.                 do while .t.
  3500.                         if this.nPos > this.nLen
  3501.                                 return null
  3502.                         endif
  3503.                         char1 = substr(this.cJson, this.nPos, 1)
  3504.                         if char1==' '
  3505.                                 this.nPos = this.nPos + 1
  3506.                                 loop
  3507.                         endif
  3508.                         return char1
  3509.                 enddo
  3510.         return
  3511.        
  3512.                
  3513.                
  3514.         function getChar()
  3515.                 if this.nPos > this.nLen
  3516.                         this.setError('Unexpected end of JSON stream')
  3517.                         return ''
  3518.                 endif
  3519.         return substr(this.cJson, this.nPos, 1)
  3520.        
  3521.         function nextChar()
  3522.                 this.nPos = this.nPos + 1
  3523.                 if this.nPos > this.nLen
  3524.                         return ''
  3525.                 endif
  3526.         return substr(this.cJson, this.nPos, 1)
  3527.        
  3528.         function setError(cMsg)
  3529.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  3530.         return
  3531.  
  3532.  
  3533.         function fixUnicode(cStr)
  3534.                 cStr = StrTran(cStr,'\u00e1','á')
  3535.                 cStr = StrTran(cStr,'\u00e9','é')
  3536.                 cStr = StrTran(cStr,'\u00ed','í')
  3537.                 cStr = StrTran(cStr,'\u00f3','ó')
  3538.                 cStr = StrTran(cStr,'\u00fa','ú')
  3539.                 cStr = StrTran(cStr,'\u00c1','Á')
  3540.                 cStr = StrTran(cStr,'\u00c9','É')
  3541.                 cStr = StrTran(cStr,'\u00cd','Í')
  3542.                 cStr = StrTran(cStr,'\u00d3','Ó')
  3543.                 cStr = StrTran(cStr,'\u00da','Ú')
  3544.                 cStr = StrTran(cStr,'\u00f1','ń')
  3545.                 cStr = StrTran(cStr,'\u00d1','Ń')
  3546.         return cStr
  3547.  
  3548.  
  3549.  
  3550. enddefine
  3551.  
  3552.  
  3553.  
  3554.  
  3555.  
  3556. *
  3557. * class used to return an array
  3558. *
  3559. define class myArray as custom
  3560.         nSize = 0
  3561.         dimension array[1]
  3562.  
  3563.         function add(xExpr)
  3564.                 this.nSize = this.nSize + 1
  3565.                 dimension this.array[this.nSize]
  3566.                 this.array[this.nSize] = xExpr
  3567.         return
  3568.  
  3569.         function get(n)
  3570.         return this.array[n]
  3571.  
  3572. enddefine
  3573.  
  3574.  
  3575.  
  3576. *
  3577. * class used to simulate an object
  3578. * all properties are prefixed with 'prop' to permit property names like: error, init
  3579. * that already exists like vfp methods
  3580. *
  3581. define class myObj as custom
  3582. Hidden ;
  3583.         ClassLibrary,Comment, ;
  3584.         BaseClass,ControlCount, ;
  3585.         Controls,Objects,Object,;
  3586.         Height,HelpContextID,Left,Name, ;
  3587.         Parent,ParentClass,Picture, ;
  3588.         Tag,Top,WhatsThisHelpID,Width
  3589.                
  3590.         function set(cPropName, xValue)
  3591.                 cPropName = '_'+cPropName
  3592.                 if type('this.'+cPropName)=='U'
  3593.                         this.addProperty(cPropName,xValue)
  3594.                 else
  3595.                         local cmd
  3596.                         cmd = 'this.'+cPropName+'=xValue'
  3597.                         &cmd
  3598.                 endif
  3599.         return
  3600.        
  3601.         procedure get (cPropName)
  3602.                 cPropName = '_'+cPropName
  3603.                 If type('this.'+cPropName)=='U'
  3604.                         return ''
  3605.                 Else
  3606.                         local cmd
  3607.                         cmd = 'return this.'+cPropName
  3608.                         &cmd
  3609.                 endif
  3610.         return ''
  3611. enddefine
  3612.  
  3613.  
  3614.  
  3615.  
  3616.  
  3617. function testJsonClass
  3618.         clear
  3619.         set decimal to 10
  3620.         oJson = newObject('json')
  3621.        
  3622.        
  3623.         ? 'Test Basic Types'
  3624.         ? '----------------'
  3625.         ? oJson.decode('null')
  3626.         ? oJson.decode('true')
  3627.         ? oJson.decode('false')
  3628.         ?
  3629.         ? oJson.decode('791123')
  3630.         ? oJson.decode('791123.45')
  3631.         ? oJson.decode('791123.45.')
  3632.         ? oJson.decode('"nacho gtz"')
  3633.         if not empty(oJson.cError)
  3634.                 ? oJson.cError
  3635.                 return
  3636.         endif
  3637.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  3638.         if not empty(oJson.cError)
  3639.                 ? oJson.cError
  3640.                 return
  3641.         endif
  3642.        
  3643.         ? 'Test Array'
  3644.         ? '----------'
  3645.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  3646.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  3647.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  3648.         nombres = arr.get(1)
  3649.         edades  = arr.get(2)
  3650.         ? nombres.get(1), edades.get(1)
  3651.         ? nombres.get(2), edades.get(2)
  3652.         ? nombres.get(3), edades.get(3)
  3653.         ?
  3654.         ? 'Test Object'
  3655.         ? '-----------'
  3656.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  3657.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  3658.         ? obj._Nombre, obj._Edad, obj._IsGood
  3659.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  3660.         ? obj.get('jsonrpc'), obj._jsonrpc
  3661.         ? obj.get('id'), obj._id
  3662.         ? obj.get('method'), obj._method
  3663.         ? obj._Params.array[1], obj._Params.get(1)
  3664.  
  3665.         ?
  3666.         ? 'Test nested object'
  3667.         ? '------------------'
  3668.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  3669.         obj = oJson.decode(cJson)
  3670.         if not empty(oJson.cError)
  3671.                 ? oJson.cError
  3672.                 return
  3673.         endif
  3674.         ? cJson
  3675.         ? 'method -->',obj._method
  3676.         ? 'usrkey -->',obj._params._data._usrkey
  3677.         ? 'sendto -->',obj._params._data._sendto
  3678.         ? 'name  --->',obj._params._data._name
  3679.         ? 'expires ->',obj._params._data._expires
  3680.  
  3681.         ?
  3682.         ? 'Test empty object'
  3683.         ? '-----------------'
  3684.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  3685.         obj = oJson.decode(cJson)
  3686.         if not empty(oJson.cError)     
  3687.                 ? oJson.cError
  3688.                 return
  3689.         endif
  3690.         ? cJson
  3691.         ? 'result -->',obj._result, obj.get('result')
  3692.         oError = obj.get('error')
  3693.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  3694.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  3695.         ? 'id  ----->',obj._id, obj.get('id')
  3696.         ?  type("oError._code")
  3697.  
  3698.         ?
  3699.         ? 'Probar decode-enconde-decode-encode'
  3700.         ? '------------------------------------'
  3701.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  3702.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  3703.         ? cJson
  3704.         oSmtp = json_decode(cJson)
  3705.         cJson =  json_encode(oSmtp)
  3706.         ? cJson
  3707.         oSmtp = json_decode(cJson)
  3708.         cJson =  json_encode(oSmtp)
  3709.         ? cJson
  3710.  
  3711.         * Probar falla
  3712.         ?
  3713.         ? 'Probar una falla en el json'
  3714.         ? '---------------------------'
  3715.         cJson = ' {"server":"", "user":"", "password":"" ,'
  3716.         oSmtp = json_decode(cJson)
  3717.         if not empty(json_getErrorMsg())
  3718.                 ? json_getErrorMsg()
  3719.         endif
  3720.  
  3721.         ?
  3722.         ? 'Pruebas Finalizadas'
  3723. retur
  3724. * www.sait.com.mx
  3725. * +52(653)534-8800
  3726. * Monterrey México
  3727. * -----------------------------------
  3728. *
  3729. * Libreria para el manejo de JSON en VFP
  3730. *
  3731. * Gracias a Google por el codigo de Json de Dart
  3732. * Thanks Google for the code in Json Dart
  3733. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  3734. *
  3735. *
  3736. * Codificar y Decodificar JSON
  3737. *
  3738. * Puedes usar las funciones:
  3739. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  3740. *               json_decode(cJson)  te regresa el objeto representado en cJson
  3741. *
  3742. * Tambien puedes usar directamente la clase:
  3743. *
  3744. *       oJson = newobject('json','json.prg')
  3745. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  3746. *       ? oJson.encode(oCliente)
  3747. *       ? oCliente.get('nombre')
  3748. *       ? oCliente.get('apellido')
  3749. *
  3750. *
  3751. * VFPJSON  Encode and Decode JSON for VFP
  3752. * Examples:
  3753. *       oJson = newobject('json','json.prg')
  3754. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  3755. *       ? oJson.encode(oCustomer)
  3756. *       ? oCustomer.get('name')
  3757. *       ? oCustomer.get('lastname')
  3758. *
  3759. *
  3760. lRunTest = .f.
  3761. if lRunTest
  3762.         testJsonClass()
  3763. endif
  3764. return
  3765.  
  3766.  
  3767. function json_encode(xExpr)
  3768.         if vartype(_json)<>'O'
  3769.                 public _json
  3770.                 _json = newobject('json')
  3771.         endif
  3772. return _json.encode(@xExpr)
  3773.  
  3774.  
  3775. function json_decode(cJson)
  3776. local retval
  3777.         if vartype(_json)<>'O'
  3778.                 public _json
  3779.                 _json = newobject('json')
  3780.         endif
  3781.         retval = _json.decode(cJson)
  3782.         if not empty(_json.cError)
  3783.                 return null
  3784.         endif
  3785. return retval
  3786.  
  3787. function json_getErrorMsg()
  3788. return _json.cError
  3789.        
  3790.  
  3791.  
  3792. *
  3793. * json class
  3794. *
  3795. *
  3796. define class json as custom
  3797.  
  3798.  
  3799.         nPos=0
  3800.         nLen=0
  3801.         cJson=''
  3802.         cError=''
  3803.  
  3804.  
  3805.         *
  3806.         * Genera el codigo cJson para parametro que se manda
  3807.         *
  3808.         function encode(xExpr)
  3809.         local cTipo
  3810.                 * Cuando se manda una arreglo,
  3811.                 if type('ALen(xExpr)')=='N'
  3812.                         cTipo = 'A'
  3813.                 Else
  3814.                         cTipo = VarType(xExpr)
  3815.                 Endif
  3816.                
  3817.                 Do Case
  3818.                 Case cTipo=='D'
  3819.                         return '"'+dtos(xExpr)+'"'
  3820.                 Case cTipo=='N'
  3821.                         return Transform(xExpr)
  3822.                 Case cTipo=='L'
  3823.                         return iif(xExpr,'true','false')
  3824.                 Case cTipo=='X'
  3825.                         return 'null'
  3826.                 Case cTipo=='C'
  3827.                         xExpr = allt(xExpr)
  3828.                         xExpr = StrTran(xExpr, '\', '\\' )
  3829.                         xExpr = StrTran(xExpr, '/', '\/' )
  3830.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  3831.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  3832.                         xExpr = StrTran(xExpr, '"', '\"' )
  3833.                         return '"'+xExpr+'"'
  3834.  
  3835.                 case cTipo=='O'
  3836.                         local cProp, cJsonValue, cRetVal, aProp[1]
  3837.                         =AMembers(aProp,xExpr)
  3838.                         cRetVal = ''
  3839.                         for each cProp in aProp
  3840.                                 *? cProp
  3841.                                 *? cRetVal
  3842.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  3843.                                         * algunas propiedades pueden no estar definidas
  3844.                                         * como: activecontrol, parent, etc
  3845.                                         loop
  3846.                                 endif
  3847.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  3848.                                         *
  3849.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  3850.                                         *
  3851.                                         Local i,nTotElem
  3852.                                         cJsonValue = ''
  3853.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  3854.                                         For i=1 to nTotElem
  3855.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  3856.                                         Next
  3857.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  3858.                                 else
  3859.                                         *
  3860.                                         * es otro tipo de dato normal C, N, L
  3861.                                         *
  3862.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  3863.                                 endif
  3864.                                 if left(cProp,1)=='_'
  3865.                                         cProp = substr(cProp,2)
  3866.                                 endif
  3867.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  3868.                         next
  3869.                         return '{' + substr(cRetVal,2) + '}'
  3870.  
  3871.                 case cTipo=='A'
  3872.                         local valor, cRetVal
  3873.                         cRetVal = ''   
  3874.                         for each valor in xExpr
  3875.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  3876.                         next
  3877.                         return  '[' + substr(cRetVal,2) + ']'
  3878.                        
  3879.                 endcase
  3880.  
  3881.         return ''
  3882.  
  3883.  
  3884.  
  3885.  
  3886.  
  3887.         *
  3888.         * regresa un elemento representado por la cadena json que se manda
  3889.         *
  3890.        
  3891.         function decode(cJson)
  3892.         local retValue
  3893.                 cJson = StrTran(cJson,chr(9),'')
  3894.                 cJson = StrTran(cJson,chr(10),'')
  3895.                 cJson = StrTran(cJson,chr(13),'')
  3896.                 cJson = this.fixUnicode(cJson)
  3897.                 this.nPos  = 1
  3898.                 this.cJson = cJson
  3899.                 this.nLen  = len(cJson)
  3900.                 this.cError = ''
  3901.                 retValue = this.parsevalue()
  3902.                 if not empty(this.cError)
  3903.                         return null
  3904.                 endif
  3905.                 if this.getToken()<>null
  3906.                         this.setError('Junk at the end of JSON input')
  3907.                         return null
  3908.                 endif
  3909.         return retValue
  3910.                
  3911.        
  3912.         function parseValue()
  3913.         local token
  3914.                 token = this.getToken()
  3915.                 if token==null
  3916.                         this.setError('Nothing to parse')
  3917.                         return null
  3918.                 endif
  3919.                 do case
  3920.                 case token=='"'
  3921.                         return this.parseString()
  3922.                 case isdigit(token) or token=='-'
  3923.                         return this.parseNumber()
  3924.                 case token=='n'
  3925.                         return this.expectedKeyword('null',null)
  3926.                 case token=='f'
  3927.                         return this.expectedKeyword('false',.f.)
  3928.                 case token=='t'
  3929.                         return this.expectedKeyword('true',.t.)
  3930.                 case token=='{'
  3931.                         return this.parseObject()
  3932.                 case token=='['
  3933.                         return this.parseArray()
  3934.                 otherwise
  3935.                         this.setError('Unexpected token')
  3936.                 endcase
  3937.         return
  3938.                
  3939.        
  3940.         function expectedKeyword(cWord,eValue)
  3941.                 for i=1 to len(cWord)
  3942.                         cChar = this.getChar()
  3943.                         if cChar <> substr(cWord,i,1)
  3944.                                 this.setError("Expected keyword '" + cWord + "'")
  3945.                                 return ''
  3946.                         endif
  3947.                         this.nPos = this.nPos + 1
  3948.                 next
  3949.         return eValue
  3950.        
  3951.  
  3952.         function parseObject()
  3953.         local retval, cPropName, xValue
  3954.                 retval = createObject('myObj')
  3955.                 this.nPos = this.nPos + 1 && Eat {
  3956.                 if this.getToken()<>'}'
  3957.                         do while .t.
  3958.                                 cPropName = this.parseString()
  3959.                                 if not empty(this.cError)
  3960.                                         return null
  3961.                                 endif
  3962.                                 if this.getToken()<>':'
  3963.                                         this.setError("Expected ':' when parsing object")
  3964.                                         return null
  3965.                                 endif
  3966.                                 this.nPos = this.nPos + 1
  3967.                                 xValue = this.parseValue()
  3968.                                 if not empty(this.cError)
  3969.                                         return null
  3970.                                 endif                          
  3971.                                 ** Debug ? cPropName, type('xValue')
  3972.                                 retval.set(cPropName, xValue)
  3973.                                 if this.getToken()<>','
  3974.                                         exit
  3975.                                 endif
  3976.                                 this.nPos = this.nPos + 1
  3977.                         enddo
  3978.                 endif
  3979.                 if this.getToken()<>'}'
  3980.                         this.setError("Expected '}' at the end of object")
  3981.                         return null
  3982.                 endif
  3983.                 this.nPos = this.nPos + 1
  3984.         return retval
  3985.  
  3986.  
  3987.         function parseArray()
  3988.         local retVal, xValue
  3989.                 retval = createObject('MyArray')
  3990.                 this.nPos = this.nPos + 1       && Eat [
  3991.                 if this.getToken() <> ']'
  3992.                         do while .t.
  3993.                                 xValue = this.parseValue()
  3994.                                 if not empty(this.cError)
  3995.                                         return null
  3996.                                 endif
  3997.                                 retval.add( xValue )
  3998.                                 if this.getToken()<>','
  3999.                                         exit
  4000.                                 endif
  4001.                                 this.nPos = this.nPos + 1
  4002.                         enddo
  4003.                         if this.getToken() <> ']'
  4004.                                 this.setError('Expected ] at the end of array')
  4005.                                 return null
  4006.                         endif
  4007.                 endif
  4008.                 this.nPos = this.nPos + 1
  4009.         return retval
  4010.        
  4011.  
  4012.         function parseString()
  4013.         local cRetVal, c
  4014.                 if this.getToken()<>'"'
  4015.                         this.setError('Expected "')
  4016.                         return ''
  4017.                 endif
  4018.                 this.nPos = this.nPos + 1       && Eat "
  4019.                 cRetVal = ''
  4020.                 do while .t.
  4021.                         c = this.getChar()
  4022.                         if c==''
  4023.                                 return ''
  4024.                         endif
  4025.                         if c == '"'
  4026.                                 this.nPos = this.nPos + 1
  4027.                                 exit
  4028.                         endif
  4029.                         if c == '\'
  4030.                                 this.nPos = this.nPos + 1
  4031.                                 if (this.nPos>this.nLen)
  4032.                                         this.setError('\\ at the end of input')
  4033.                                         return ''
  4034.                                 endif
  4035.                                 c = this.getChar()
  4036.                                 if c==''
  4037.                                         return ''
  4038.                                 endif
  4039.                                 do case
  4040.                                 case c=='"'
  4041.                                         c='"'
  4042.                                 case c=='\'
  4043.                                         c='\'
  4044.                                 case c=='/'
  4045.                                         c='/'
  4046.                                 case c=='b'
  4047.                                         c=chr(8)
  4048.                                 case c=='t'
  4049.                                         c=chr(9)
  4050.                                 case c=='n'
  4051.                                         c=chr(10)
  4052.                                 case c=='f'
  4053.                                         c=chr(12)
  4054.                                 case c=='r'
  4055.                                         c=chr(13)
  4056.                                 otherwise
  4057.                                         ******* FALTAN LOS UNICODE
  4058.                                         this.setError('Invalid escape sequence in string literal')
  4059.                                         return ''
  4060.                                 endcase
  4061.                         endif
  4062.                         cRetVal = cRetVal + c
  4063.                         this.nPos = this.nPos + 1
  4064.                 enddo
  4065.         return cRetVal
  4066.                                        
  4067.  
  4068.         **** Pendiente numeros con E
  4069.         function parseNumber()
  4070.         local nStartPos,c, isInt, cNumero
  4071.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  4072.                         this.setError('Expected number literal')
  4073.                         return 0
  4074.                 endif
  4075.                 nStartPos = this.nPos
  4076.                 c = this.getChar()
  4077.                 if c == '-'
  4078.                         c = this.nextChar()
  4079.                 endif
  4080.                 if c == '0'
  4081.                         c = this.nextChar()
  4082.                 else
  4083.                         if isdigit(c)
  4084.                                 c = this.nextChar()
  4085.                                 do while isdigit(c)
  4086.                                         c = this.nextChar()
  4087.                                 enddo
  4088.                         else
  4089.                                 this.setError('Expected digit when parsing number')
  4090.                                 return 0
  4091.                         endif
  4092.                 endif
  4093.                
  4094.                 isInt = .t.
  4095.                 if c=='.'
  4096.                         c = this.nextChar()
  4097.                         if isdigit(c)
  4098.                                 c = this.nextChar()
  4099.                                 isInt = .f.
  4100.                                 do while isDigit(c)
  4101.                                         c = this.nextChar()
  4102.                                 enddo
  4103.                         else
  4104.                                 this.setError('Expected digit following dot comma')
  4105.                                 return 0
  4106.                         endif
  4107.                 endif
  4108.                
  4109.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  4110.         return val(cNumero)
  4111.  
  4112.  
  4113.  
  4114.         function getToken()
  4115.         local char1
  4116.                 do while .t.
  4117.                         if this.nPos > this.nLen
  4118.                                 return null
  4119.                         endif
  4120.                         char1 = substr(this.cJson, this.nPos, 1)
  4121.                         if char1==' '
  4122.                                 this.nPos = this.nPos + 1
  4123.                                 loop
  4124.                         endif
  4125.                         return char1
  4126.                 enddo
  4127.         return
  4128.        
  4129.                
  4130.                
  4131.         function getChar()
  4132.                 if this.nPos > this.nLen
  4133.                         this.setError('Unexpected end of JSON stream')
  4134.                         return ''
  4135.                 endif
  4136.         return substr(this.cJson, this.nPos, 1)
  4137.        
  4138.         function nextChar()
  4139.                 this.nPos = this.nPos + 1
  4140.                 if this.nPos > this.nLen
  4141.                         return ''
  4142.                 endif
  4143.         return substr(this.cJson, this.nPos, 1)
  4144.        
  4145.         function setError(cMsg)
  4146.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  4147.         return
  4148.  
  4149.  
  4150.         function fixUnicode(cStr)
  4151.                 cStr = StrTran(cStr,'\u00e1','á')
  4152.                 cStr = StrTran(cStr,'\u00e9','é')
  4153.                 cStr = StrTran(cStr,'\u00ed','í')
  4154.                 cStr = StrTran(cStr,'\u00f3','ó')
  4155.                 cStr = StrTran(cStr,'\u00fa','ú')
  4156.                 cStr = StrTran(cStr,'\u00c1','Á')
  4157.                 cStr = StrTran(cStr,'\u00c9','É')
  4158.                 cStr = StrTran(cStr,'\u00cd','Í')
  4159.                 cStr = StrTran(cStr,'\u00d3','Ó')
  4160.                 cStr = StrTran(cStr,'\u00da','Ú')
  4161.                 cStr = StrTran(cStr,'\u00f1','ń')
  4162.                 cStr = StrTran(cStr,'\u00d1','Ń')
  4163.         return cStr
  4164.  
  4165.  
  4166.  
  4167. enddefine
  4168.  
  4169.  
  4170.  
  4171.  
  4172.  
  4173. *
  4174. * class used to return an array
  4175. *
  4176. define class myArray as custom
  4177.         nSize = 0
  4178.         dimension array[1]
  4179.  
  4180.         function add(xExpr)
  4181.                 this.nSize = this.nSize + 1
  4182.                 dimension this.array[this.nSize]
  4183.                 this.array[this.nSize] = xExpr
  4184.         return
  4185.  
  4186.         function get(n)
  4187.         return this.array[n]
  4188.  
  4189. enddefine
  4190.  
  4191.  
  4192.  
  4193. *
  4194. * class used to simulate an object
  4195. * all properties are prefixed with 'prop' to permit property names like: error, init
  4196. * that already exists like vfp methods
  4197. *
  4198. define class myObj as custom
  4199. Hidden ;
  4200.         ClassLibrary,Comment, ;
  4201.         BaseClass,ControlCount, ;
  4202.         Controls,Objects,Object,;
  4203.         Height,HelpContextID,Left,Name, ;
  4204.         Parent,ParentClass,Picture, ;
  4205.         Tag,Top,WhatsThisHelpID,Width
  4206.                
  4207.         function set(cPropName, xValue)
  4208.                 cPropName = '_'+cPropName
  4209.                 if type('this.'+cPropName)=='U'
  4210.                         this.addProperty(cPropName,xValue)
  4211.                 else
  4212.                         local cmd
  4213.                         cmd = 'this.'+cPropName+'=xValue'
  4214.                         &cmd
  4215.                 endif
  4216.         return
  4217.        
  4218.         procedure get (cPropName)
  4219.                 cPropName = '_'+cPropName
  4220.                 If type('this.'+cPropName)=='U'
  4221.                         return ''
  4222.                 Else
  4223.                         local cmd
  4224.                         cmd = 'return this.'+cPropName
  4225.                         &cmd
  4226.                 endif
  4227.         return ''
  4228. enddefine
  4229.  
  4230.  
  4231.  
  4232.  
  4233.  
  4234. function testJsonClass
  4235.         clear
  4236.         set decimal to 10
  4237.         oJson = newObject('json')
  4238.        
  4239.        
  4240.         ? 'Test Basic Types'
  4241.         ? '----------------'
  4242.         ? oJson.decode('null')
  4243.         ? oJson.decode('true')
  4244.         ? oJson.decode('false')
  4245.         ?
  4246.         ? oJson.decode('791123')
  4247.         ? oJson.decode('791123.45')
  4248.         ? oJson.decode('791123.45.')
  4249.         ? oJson.decode('"nacho gtz"')
  4250.         if not empty(oJson.cError)
  4251.                 ? oJson.cError
  4252.                 return
  4253.         endif
  4254.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  4255.         if not empty(oJson.cError)
  4256.                 ? oJson.cError
  4257.                 return
  4258.         endif
  4259.        
  4260.         ? 'Test Array'
  4261.         ? '----------'
  4262.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  4263.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  4264.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  4265.         nombres = arr.get(1)
  4266.         edades  = arr.get(2)
  4267.         ? nombres.get(1), edades.get(1)
  4268.         ? nombres.get(2), edades.get(2)
  4269.         ? nombres.get(3), edades.get(3)
  4270.         ?
  4271.         ? 'Test Object'
  4272.         ? '-----------'
  4273.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  4274.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  4275.         ? obj._Nombre, obj._Edad, obj._IsGood
  4276.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  4277.         ? obj.get('jsonrpc'), obj._jsonrpc
  4278.         ? obj.get('id'), obj._id
  4279.         ? obj.get('method'), obj._method
  4280.         ? obj._Params.array[1], obj._Params.get(1)
  4281.  
  4282.         ?
  4283.         ? 'Test nested object'
  4284.         ? '------------------'
  4285.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  4286.         obj = oJson.decode(cJson)
  4287.         if not empty(oJson.cError)
  4288.                 ? oJson.cError
  4289.                 return
  4290.         endif
  4291.         ? cJson
  4292.         ? 'method -->',obj._method
  4293.         ? 'usrkey -->',obj._params._data._usrkey
  4294.         ? 'sendto -->',obj._params._data._sendto
  4295.         ? 'name  --->',obj._params._data._name
  4296.         ? 'expires ->',obj._params._data._expires
  4297.  
  4298.         ?
  4299.         ? 'Test empty object'
  4300.         ? '-----------------'
  4301.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  4302.         obj = oJson.decode(cJson)
  4303.         if not empty(oJson.cError)     
  4304.                 ? oJson.cError
  4305.                 return
  4306.         endif
  4307.         ? cJson
  4308.         ? 'result -->',obj._result, obj.get('result')
  4309.         oError = obj.get('error')
  4310.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  4311.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  4312.         ? 'id  ----->',obj._id, obj.get('id')
  4313.         ?  type("oError._code")
  4314.  
  4315.         ?
  4316.         ? 'Probar decode-enconde-decode-encode'
  4317.         ? '------------------------------------'
  4318.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  4319.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  4320.         ? cJson
  4321.         oSmtp = json_decode(cJson)
  4322.         cJson =  json_encode(oSmtp)
  4323.         ? cJson
  4324.         oSmtp = json_decode(cJson)
  4325.         cJson =  json_encode(oSmtp)
  4326.         ? cJson
  4327.  
  4328.         * Probar falla
  4329.         ?
  4330.         ? 'Probar una falla en el json'
  4331.         ? '---------------------------'
  4332.         cJson = ' {"server":"", "user":"", "password":"" ,'
  4333.         oSmtp = json_decode(cJson)
  4334.         if not empty(json_getErrorMsg())
  4335.                 ? json_getErrorMsg()
  4336.         endif
  4337.  
  4338.         ?
  4339.         ? 'Pruebas Finalizadas'
  4340. retur
  4341. * +52(653)534-8800
  4342. * Monterrey México
  4343. * -----------------------------------
  4344. *
  4345. * Libreria para el manejo de JSON en VFP
  4346. *
  4347. * Gracias a Google por el codigo de Json de Dart
  4348. * Thanks Google for the code in Json Dart
  4349. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  4350. *
  4351. *
  4352. * Codificar y Decodificar JSON
  4353. *
  4354. * Puedes usar las funciones:
  4355. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  4356. *               json_decode(cJson)  te regresa el objeto representado en cJson
  4357. *
  4358. * Tambien puedes usar directamente la clase:
  4359. *
  4360. *       oJson = newobject('json','json.prg')
  4361. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  4362. *       ? oJson.encode(oCliente)
  4363. *       ? oCliente.get('nombre')
  4364. *       ? oCliente.get('apellido')
  4365. *
  4366. *
  4367. * VFPJSON  Encode and Decode JSON for VFP
  4368. * Examples:
  4369. *       oJson = newobject('json','json.prg')
  4370. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  4371. *       ? oJson.encode(oCustomer)
  4372. *       ? oCustomer.get('name')
  4373. *       ? oCustomer.get('lastname')
  4374. *
  4375. *
  4376. lRunTest = .f.
  4377. if lRunTest
  4378.         testJsonClass()
  4379. endif
  4380. return
  4381.  
  4382.  
  4383. function json_encode(xExpr)
  4384.         if vartype(_json)<>'O'
  4385.                 public _json
  4386.                 _json = newobject('json')
  4387.         endif
  4388. return _json.encode(@xExpr)
  4389.  
  4390.  
  4391. function json_decode(cJson)
  4392. local retval
  4393.         if vartype(_json)<>'O'
  4394.                 public _json
  4395.                 _json = newobject('json')
  4396.         endif
  4397.         retval = _json.decode(cJson)
  4398.         if not empty(_json.cError)
  4399.                 return null
  4400.         endif
  4401. return retval
  4402.  
  4403. function json_getErrorMsg()
  4404. return _json.cError
  4405.        
  4406.  
  4407.  
  4408. *
  4409. * json class
  4410. *
  4411. *
  4412. define class json as custom
  4413.  
  4414.  
  4415.         nPos=0
  4416.         nLen=0
  4417.         cJson=''
  4418.         cError=''
  4419.  
  4420.  
  4421.         *
  4422.         * Genera el codigo cJson para parametro que se manda
  4423.         *
  4424.         function encode(xExpr)
  4425.         local cTipo
  4426.                 * Cuando se manda una arreglo,
  4427.                 if type('ALen(xExpr)')=='N'
  4428.                         cTipo = 'A'
  4429.                 Else
  4430.                         cTipo = VarType(xExpr)
  4431.                 Endif
  4432.                
  4433.                 Do Case
  4434.                 Case cTipo=='D'
  4435.                         return '"'+dtos(xExpr)+'"'
  4436.                 Case cTipo=='N'
  4437.                         return Transform(xExpr)
  4438.                 Case cTipo=='L'
  4439.                         return iif(xExpr,'true','false')
  4440.                 Case cTipo=='X'
  4441.                         return 'null'
  4442.                 Case cTipo=='C'
  4443.                         xExpr = allt(xExpr)
  4444.                         xExpr = StrTran(xExpr, '\', '\\' )
  4445.                         xExpr = StrTran(xExpr, '/', '\/' )
  4446.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  4447.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  4448.                         xExpr = StrTran(xExpr, '"', '\"' )
  4449.                         return '"'+xExpr+'"'
  4450.  
  4451.                 case cTipo=='O'
  4452.                         local cProp, cJsonValue, cRetVal, aProp[1]
  4453.                         =AMembers(aProp,xExpr)
  4454.                         cRetVal = ''
  4455.                         for each cProp in aProp
  4456.                                 *? cProp
  4457.                                 *? cRetVal
  4458.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  4459.                                         * algunas propiedades pueden no estar definidas
  4460.                                         * como: activecontrol, parent, etc
  4461.                                         loop
  4462.                                 endif
  4463.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  4464.                                         *
  4465.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  4466.                                         *
  4467.                                         Local i,nTotElem
  4468.                                         cJsonValue = ''
  4469.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  4470.                                         For i=1 to nTotElem
  4471.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  4472.                                         Next
  4473.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  4474.                                 else
  4475.                                         *
  4476.                                         * es otro tipo de dato normal C, N, L
  4477.                                         *
  4478.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  4479.                                 endif
  4480.                                 if left(cProp,1)=='_'
  4481.                                         cProp = substr(cProp,2)
  4482.                                 endif
  4483.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  4484.                         next
  4485.                         return '{' + substr(cRetVal,2) + '}'
  4486.  
  4487.                 case cTipo=='A'
  4488.                         local valor, cRetVal
  4489.                         cRetVal = ''   
  4490.                         for each valor in xExpr
  4491.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  4492.                         next
  4493.                         return  '[' + substr(cRetVal,2) + ']'
  4494.                        
  4495.                 endcase
  4496.  
  4497.         return ''
  4498.  
  4499.  
  4500.  
  4501.  
  4502.  
  4503.         *
  4504.         * regresa un elemento representado por la cadena json que se manda
  4505.         *
  4506.        
  4507.         function decode(cJson)
  4508.         local retValue
  4509.                 cJson = StrTran(cJson,chr(9),'')
  4510.                 cJson = StrTran(cJson,chr(10),'')
  4511.                 cJson = StrTran(cJson,chr(13),'')
  4512.                 cJson = this.fixUnicode(cJson)
  4513.                 this.nPos  = 1
  4514.                 this.cJson = cJson
  4515.                 this.nLen  = len(cJson)
  4516.                 this.cError = ''
  4517.                 retValue = this.parsevalue()
  4518.                 if not empty(this.cError)
  4519.                         return null
  4520.                 endif
  4521.                 if this.getToken()<>null
  4522.                         this.setError('Junk at the end of JSON input')
  4523.                         return null
  4524.                 endif
  4525.         return retValue
  4526.                
  4527.        
  4528.         function parseValue()
  4529.         local token
  4530.                 token = this.getToken()
  4531.                 if token==null
  4532.                         this.setError('Nothing to parse')
  4533.                         return null
  4534.                 endif
  4535.                 do case
  4536.                 case token=='"'
  4537.                         return this.parseString()
  4538.                 case isdigit(token) or token=='-'
  4539.                         return this.parseNumber()
  4540.                 case token=='n'
  4541.                         return this.expectedKeyword('null',null)
  4542.                 case token=='f'
  4543.                         return this.expectedKeyword('false',.f.)
  4544.                 case token=='t'
  4545.                         return this.expectedKeyword('true',.t.)
  4546.                 case token=='{'
  4547.                         return this.parseObject()
  4548.                 case token=='['
  4549.                         return this.parseArray()
  4550.                 otherwise
  4551.                         this.setError('Unexpected token')
  4552.                 endcase
  4553.         return
  4554.                
  4555.        
  4556.         function expectedKeyword(cWord,eValue)
  4557.                 for i=1 to len(cWord)
  4558.                         cChar = this.getChar()
  4559.                         if cChar <> substr(cWord,i,1)
  4560.                                 this.setError("Expected keyword '" + cWord + "'")
  4561.                                 return ''
  4562.                         endif
  4563.                         this.nPos = this.nPos + 1
  4564.                 next
  4565.         return eValue
  4566.        
  4567.  
  4568.         function parseObject()
  4569.         local retval, cPropName, xValue
  4570.                 retval = createObject('myObj')
  4571.                 this.nPos = this.nPos + 1 && Eat {
  4572.                 if this.getToken()<>'}'
  4573.                         do while .t.
  4574.                                 cPropName = this.parseString()
  4575.                                 if not empty(this.cError)
  4576.                                         return null
  4577.                                 endif
  4578.                                 if this.getToken()<>':'
  4579.                                         this.setError("Expected ':' when parsing object")
  4580.                                         return null
  4581.                                 endif
  4582.                                 this.nPos = this.nPos + 1
  4583.                                 xValue = this.parseValue()
  4584.                                 if not empty(this.cError)
  4585.                                         return null
  4586.                                 endif                          
  4587.                                 ** Debug ? cPropName, type('xValue')
  4588.                                 retval.set(cPropName, xValue)
  4589.                                 if this.getToken()<>','
  4590.                                         exit
  4591.                                 endif
  4592.                                 this.nPos = this.nPos + 1
  4593.                         enddo
  4594.                 endif
  4595.                 if this.getToken()<>'}'
  4596.                         this.setError("Expected '}' at the end of object")
  4597.                         return null
  4598.                 endif
  4599.                 this.nPos = this.nPos + 1
  4600.         return retval
  4601.  
  4602.  
  4603.         function parseArray()
  4604.         local retVal, xValue
  4605.                 retval = createObject('MyArray')
  4606.                 this.nPos = this.nPos + 1       && Eat [
  4607.                 if this.getToken() <> ']'
  4608.                         do while .t.
  4609.                                 xValue = this.parseValue()
  4610.                                 if not empty(this.cError)
  4611.                                         return null
  4612.                                 endif
  4613.                                 retval.add( xValue )
  4614.                                 if this.getToken()<>','
  4615.                                         exit
  4616.                                 endif
  4617.                                 this.nPos = this.nPos + 1
  4618.                         enddo
  4619.                         if this.getToken() <> ']'
  4620.                                 this.setError('Expected ] at the end of array')
  4621.                                 return null
  4622.                         endif
  4623.                 endif
  4624.                 this.nPos = this.nPos + 1
  4625.         return retval
  4626.        
  4627.  
  4628.         function parseString()
  4629.         local cRetVal, c
  4630.                 if this.getToken()<>'"'
  4631.                         this.setError('Expected "')
  4632.                         return ''
  4633.                 endif
  4634.                 this.nPos = this.nPos + 1       && Eat "
  4635.                 cRetVal = ''
  4636.                 do while .t.
  4637.                         c = this.getChar()
  4638.                         if c==''
  4639.                                 return ''
  4640.                         endif
  4641.                         if c == '"'
  4642.                                 this.nPos = this.nPos + 1
  4643.                                 exit
  4644.                         endif
  4645.                         if c == '\'
  4646.                                 this.nPos = this.nPos + 1
  4647.                                 if (this.nPos>this.nLen)
  4648.                                         this.setError('\\ at the end of input')
  4649.                                         return ''
  4650.                                 endif
  4651.                                 c = this.getChar()
  4652.                                 if c==''
  4653.                                         return ''
  4654.                                 endif
  4655.                                 do case
  4656.                                 case c=='"'
  4657.                                         c='"'
  4658.                                 case c=='\'
  4659.                                         c='\'
  4660.                                 case c=='/'
  4661.                                         c='/'
  4662.                                 case c=='b'
  4663.                                         c=chr(8)
  4664.                                 case c=='t'
  4665.                                         c=chr(9)
  4666.                                 case c=='n'
  4667.                                         c=chr(10)
  4668.                                 case c=='f'
  4669.                                         c=chr(12)
  4670.                                 case c=='r'
  4671.                                         c=chr(13)
  4672.                                 otherwise
  4673.                                         ******* FALTAN LOS UNICODE
  4674.                                         this.setError('Invalid escape sequence in string literal')
  4675.                                         return ''
  4676.                                 endcase
  4677.                         endif
  4678.                         cRetVal = cRetVal + c
  4679.                         this.nPos = this.nPos + 1
  4680.                 enddo
  4681.         return cRetVal
  4682.                                        
  4683.  
  4684.         **** Pendiente numeros con E
  4685.         function parseNumber()
  4686.         local nStartPos,c, isInt, cNumero
  4687.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  4688.                         this.setError('Expected number literal')
  4689.                         return 0
  4690.                 endif
  4691.                 nStartPos = this.nPos
  4692.                 c = this.getChar()
  4693.                 if c == '-'
  4694.                         c = this.nextChar()
  4695.                 endif
  4696.                 if c == '0'
  4697.                         c = this.nextChar()
  4698.                 else
  4699.                         if isdigit(c)
  4700.                                 c = this.nextChar()
  4701.                                 do while isdigit(c)
  4702.                                         c = this.nextChar()
  4703.                                 enddo
  4704.                         else
  4705.                                 this.setError('Expected digit when parsing number')
  4706.                                 return 0
  4707.                         endif
  4708.                 endif
  4709.                
  4710.                 isInt = .t.
  4711.                 if c=='.'
  4712.                         c = this.nextChar()
  4713.                         if isdigit(c)
  4714.                                 c = this.nextChar()
  4715.                                 isInt = .f.
  4716.                                 do while isDigit(c)
  4717.                                         c = this.nextChar()
  4718.                                 enddo
  4719.                         else
  4720.                                 this.setError('Expected digit following dot comma')
  4721.                                 return 0
  4722.                         endif
  4723.                 endif
  4724.                
  4725.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  4726.         return val(cNumero)
  4727.  
  4728.  
  4729.  
  4730.         function getToken()
  4731.         local char1
  4732.                 do while .t.
  4733.                         if this.nPos > this.nLen
  4734.                                 return null
  4735.                         endif
  4736.                         char1 = substr(this.cJson, this.nPos, 1)
  4737.                         if char1==' '
  4738.                                 this.nPos = this.nPos + 1
  4739.                                 loop
  4740.                         endif
  4741.                         return char1
  4742.                 enddo
  4743.         return
  4744.        
  4745.                
  4746.                
  4747.         function getChar()
  4748.                 if this.nPos > this.nLen
  4749.                         this.setError('Unexpected end of JSON stream')
  4750.                         return ''
  4751.                 endif
  4752.         return substr(this.cJson, this.nPos, 1)
  4753.        
  4754.         function nextChar()
  4755.                 this.nPos = this.nPos + 1
  4756.                 if this.nPos > this.nLen
  4757.                         return ''
  4758.                 endif
  4759.         return substr(this.cJson, this.nPos, 1)
  4760.        
  4761.         function setError(cMsg)
  4762.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  4763.         return
  4764.  
  4765.  
  4766.         function fixUnicode(cStr)
  4767.                 cStr = StrTran(cStr,'\u00e1','á')
  4768.                 cStr = StrTran(cStr,'\u00e9','é')
  4769.                 cStr = StrTran(cStr,'\u00ed','í')
  4770.                 cStr = StrTran(cStr,'\u00f3','ó')
  4771.                 cStr = StrTran(cStr,'\u00fa','ú')
  4772.                 cStr = StrTran(cStr,'\u00c1','Á')
  4773.                 cStr = StrTran(cStr,'\u00c9','É')
  4774.                 cStr = StrTran(cStr,'\u00cd','Í')
  4775.                 cStr = StrTran(cStr,'\u00d3','Ó')
  4776.                 cStr = StrTran(cStr,'\u00da','Ú')
  4777.                 cStr = StrTran(cStr,'\u00f1','ń')
  4778.                 cStr = StrTran(cStr,'\u00d1','Ń')
  4779.         return cStr
  4780.  
  4781.  
  4782.  
  4783. enddefine
  4784.  
  4785.  
  4786.  
  4787.  
  4788.  
  4789. *
  4790. * class used to return an array
  4791. *
  4792. define class myArray as custom
  4793.         nSize = 0
  4794.         dimension array[1]
  4795.  
  4796.         function add(xExpr)
  4797.                 this.nSize = this.nSize + 1
  4798.                 dimension this.array[this.nSize]
  4799.                 this.array[this.nSize] = xExpr
  4800.         return
  4801.  
  4802.         function get(n)
  4803.         return this.array[n]
  4804.  
  4805. enddefine
  4806.  
  4807.  
  4808.  
  4809. *
  4810. * class used to simulate an object
  4811. * all properties are prefixed with 'prop' to permit property names like: error, init
  4812. * that already exists like vfp methods
  4813. *
  4814. define class myObj as custom
  4815. Hidden ;
  4816.         ClassLibrary,Comment, ;
  4817.         BaseClass,ControlCount, ;
  4818.         Controls,Objects,Object,;
  4819.         Height,HelpContextID,Left,Name, ;
  4820.         Parent,ParentClass,Picture, ;
  4821.         Tag,Top,WhatsThisHelpID,Width
  4822.                
  4823.         function set(cPropName, xValue)
  4824.                 cPropName = '_'+cPropName
  4825.                 if type('this.'+cPropName)=='U'
  4826.                         this.addProperty(cPropName,xValue)
  4827.                 else
  4828.                         local cmd
  4829.                         cmd = 'this.'+cPropName+'=xValue'
  4830.                         &cmd
  4831.                 endif
  4832.         return
  4833.        
  4834.         procedure get (cPropName)
  4835.                 cPropName = '_'+cPropName
  4836.                 If type('this.'+cPropName)=='U'
  4837.                         return ''
  4838.                 Else
  4839.                         local cmd
  4840.                         cmd = 'return this.'+cPropName
  4841.                         &cmd
  4842.                 endif
  4843.         return ''
  4844. enddefine
  4845.  
  4846.  
  4847.  
  4848.  
  4849.  
  4850. function testJsonClass
  4851.         clear
  4852.         set decimal to 10
  4853.         oJson = newObject('json')
  4854.        
  4855.        
  4856.         ? 'Test Basic Types'
  4857.         ? '----------------'
  4858.         ? oJson.decode('null')
  4859.         ? oJson.decode('true')
  4860.         ? oJson.decode('false')
  4861.         ?
  4862.         ? oJson.decode('791123')
  4863.         ? oJson.decode('791123.45')
  4864.         ? oJson.decode('791123.45.')
  4865.         ? oJson.decode('"nacho gtz"')
  4866.         if not empty(oJson.cError)
  4867.                 ? oJson.cError
  4868.                 return
  4869.         endif
  4870.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  4871.         if not empty(oJson.cError)
  4872.                 ? oJson.cError
  4873.                 return
  4874.         endif
  4875.        
  4876.         ? 'Test Array'
  4877.         ? '----------'
  4878.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  4879.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  4880.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  4881.         nombres = arr.get(1)
  4882.         edades  = arr.get(2)
  4883.         ? nombres.get(1), edades.get(1)
  4884.         ? nombres.get(2), edades.get(2)
  4885.         ? nombres.get(3), edades.get(3)
  4886.         ?
  4887.         ? 'Test Object'
  4888.         ? '-----------'
  4889.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  4890.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  4891.         ? obj._Nombre, obj._Edad, obj._IsGood
  4892.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  4893.         ? obj.get('jsonrpc'), obj._jsonrpc
  4894.         ? obj.get('id'), obj._id
  4895.         ? obj.get('method'), obj._method
  4896.         ? obj._Params.array[1], obj._Params.get(1)
  4897.  
  4898.         ?
  4899.         ? 'Test nested object'
  4900.         ? '------------------'
  4901.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  4902.         obj = oJson.decode(cJson)
  4903.         if not empty(oJson.cError)
  4904.                 ? oJson.cError
  4905.                 return
  4906.         endif
  4907.         ? cJson
  4908.         ? 'method -->',obj._method
  4909.         ? 'usrkey -->',obj._params._data._usrkey
  4910.         ? 'sendto -->',obj._params._data._sendto
  4911.         ? 'name  --->',obj._params._data._name
  4912.         ? 'expires ->',obj._params._data._expires
  4913.  
  4914.         ?
  4915.         ? 'Test empty object'
  4916.         ? '-----------------'
  4917.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  4918.         obj = oJson.decode(cJson)
  4919.         if not empty(oJson.cError)     
  4920.                 ? oJson.cError
  4921.                 return
  4922.         endif
  4923.         ? cJson
  4924.         ? 'result -->',obj._result, obj.get('result')
  4925.         oError = obj.get('error')
  4926.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  4927.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  4928.         ? 'id  ----->',obj._id, obj.get('id')
  4929.         ?  type("oError._code")
  4930.  
  4931.         ?
  4932.         ? 'Probar decode-enconde-decode-encode'
  4933.         ? '------------------------------------'
  4934.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  4935.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  4936.         ? cJson
  4937.         oSmtp = json_decode(cJson)
  4938.         cJson =  json_encode(oSmtp)
  4939.         ? cJson
  4940.         oSmtp = json_decode(cJson)
  4941.         cJson =  json_encode(oSmtp)
  4942.         ? cJson
  4943.  
  4944.         * Probar falla
  4945.         ?
  4946.         ? 'Probar una falla en el json'
  4947.         ? '---------------------------'
  4948.         cJson = ' {"server":"", "user":"", "password":"" ,'
  4949.         oSmtp = json_decode(cJson)
  4950.         if not empty(json_getErrorMsg())
  4951.                 ? json_getErrorMsg()
  4952.         endif
  4953.  
  4954.         ?
  4955.         ? 'Pruebas Finalizadas'
  4956. retur
  4957. * Monterrey México
  4958. * -----------------------------------
  4959. *
  4960. * Libreria para el manejo de JSON en VFP
  4961. *
  4962. * Gracias a Google por el codigo de Json de Dart
  4963. * Thanks Google for the code in Json Dart
  4964. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  4965. *
  4966. *
  4967. * Codificar y Decodificar JSON
  4968. *
  4969. * Puedes usar las funciones:
  4970. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  4971. *               json_decode(cJson)  te regresa el objeto representado en cJson
  4972. *
  4973. * Tambien puedes usar directamente la clase:
  4974. *
  4975. *       oJson = newobject('json','json.prg')
  4976. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  4977. *       ? oJson.encode(oCliente)
  4978. *       ? oCliente.get('nombre')
  4979. *       ? oCliente.get('apellido')
  4980. *
  4981. *
  4982. * VFPJSON  Encode and Decode JSON for VFP
  4983. * Examples:
  4984. *       oJson = newobject('json','json.prg')
  4985. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  4986. *       ? oJson.encode(oCustomer)
  4987. *       ? oCustomer.get('name')
  4988. *       ? oCustomer.get('lastname')
  4989. *
  4990. *
  4991. lRunTest = .f.
  4992. if lRunTest
  4993.         testJsonClass()
  4994. endif
  4995. return
  4996.  
  4997.  
  4998. function json_encode(xExpr)
  4999.         if vartype(_json)<>'O'
  5000.                 public _json
  5001.                 _json = newobject('json')
  5002.         endif
  5003. return _json.encode(@xExpr)
  5004.  
  5005.  
  5006. function json_decode(cJson)
  5007. local retval
  5008.         if vartype(_json)<>'O'
  5009.                 public _json
  5010.                 _json = newobject('json')
  5011.         endif
  5012.         retval = _json.decode(cJson)
  5013.         if not empty(_json.cError)
  5014.                 return null
  5015.         endif
  5016. return retval
  5017.  
  5018. function json_getErrorMsg()
  5019. return _json.cError
  5020.        
  5021.  
  5022.  
  5023. *
  5024. * json class
  5025. *
  5026. *
  5027. define class json as custom
  5028.  
  5029.  
  5030.         nPos=0
  5031.         nLen=0
  5032.         cJson=''
  5033.         cError=''
  5034.  
  5035.  
  5036.         *
  5037.         * Genera el codigo cJson para parametro que se manda
  5038.         *
  5039.         function encode(xExpr)
  5040.         local cTipo
  5041.                 * Cuando se manda una arreglo,
  5042.                 if type('ALen(xExpr)')=='N'
  5043.                         cTipo = 'A'
  5044.                 Else
  5045.                         cTipo = VarType(xExpr)
  5046.                 Endif
  5047.                
  5048.                 Do Case
  5049.                 Case cTipo=='D'
  5050.                         return '"'+dtos(xExpr)+'"'
  5051.                 Case cTipo=='N'
  5052.                         return Transform(xExpr)
  5053.                 Case cTipo=='L'
  5054.                         return iif(xExpr,'true','false')
  5055.                 Case cTipo=='X'
  5056.                         return 'null'
  5057.                 Case cTipo=='C'
  5058.                         xExpr = allt(xExpr)
  5059.                         xExpr = StrTran(xExpr, '\', '\\' )
  5060.                         xExpr = StrTran(xExpr, '/', '\/' )
  5061.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  5062.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  5063.                         xExpr = StrTran(xExpr, '"', '\"' )
  5064.                         return '"'+xExpr+'"'
  5065.  
  5066.                 case cTipo=='O'
  5067.                         local cProp, cJsonValue, cRetVal, aProp[1]
  5068.                         =AMembers(aProp,xExpr)
  5069.                         cRetVal = ''
  5070.                         for each cProp in aProp
  5071.                                 *? cProp
  5072.                                 *? cRetVal
  5073.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  5074.                                         * algunas propiedades pueden no estar definidas
  5075.                                         * como: activecontrol, parent, etc
  5076.                                         loop
  5077.                                 endif
  5078.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  5079.                                         *
  5080.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  5081.                                         *
  5082.                                         Local i,nTotElem
  5083.                                         cJsonValue = ''
  5084.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  5085.                                         For i=1 to nTotElem
  5086.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  5087.                                         Next
  5088.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  5089.                                 else
  5090.                                         *
  5091.                                         * es otro tipo de dato normal C, N, L
  5092.                                         *
  5093.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  5094.                                 endif
  5095.                                 if left(cProp,1)=='_'
  5096.                                         cProp = substr(cProp,2)
  5097.                                 endif
  5098.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  5099.                         next
  5100.                         return '{' + substr(cRetVal,2) + '}'
  5101.  
  5102.                 case cTipo=='A'
  5103.                         local valor, cRetVal
  5104.                         cRetVal = ''   
  5105.                         for each valor in xExpr
  5106.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  5107.                         next
  5108.                         return  '[' + substr(cRetVal,2) + ']'
  5109.                        
  5110.                 endcase
  5111.  
  5112.         return ''
  5113.  
  5114.  
  5115.  
  5116.  
  5117.  
  5118.         *
  5119.         * regresa un elemento representado por la cadena json que se manda
  5120.         *
  5121.        
  5122.         function decode(cJson)
  5123.         local retValue
  5124.                 cJson = StrTran(cJson,chr(9),'')
  5125.                 cJson = StrTran(cJson,chr(10),'')
  5126.                 cJson = StrTran(cJson,chr(13),'')
  5127.                 cJson = this.fixUnicode(cJson)
  5128.                 this.nPos  = 1
  5129.                 this.cJson = cJson
  5130.                 this.nLen  = len(cJson)
  5131.                 this.cError = ''
  5132.                 retValue = this.parsevalue()
  5133.                 if not empty(this.cError)
  5134.                         return null
  5135.                 endif
  5136.                 if this.getToken()<>null
  5137.                         this.setError('Junk at the end of JSON input')
  5138.                         return null
  5139.                 endif
  5140.         return retValue
  5141.                
  5142.        
  5143.         function parseValue()
  5144.         local token
  5145.                 token = this.getToken()
  5146.                 if token==null
  5147.                         this.setError('Nothing to parse')
  5148.                         return null
  5149.                 endif
  5150.                 do case
  5151.                 case token=='"'
  5152.                         return this.parseString()
  5153.                 case isdigit(token) or token=='-'
  5154.                         return this.parseNumber()
  5155.                 case token=='n'
  5156.                         return this.expectedKeyword('null',null)
  5157.                 case token=='f'
  5158.                         return this.expectedKeyword('false',.f.)
  5159.                 case token=='t'
  5160.                         return this.expectedKeyword('true',.t.)
  5161.                 case token=='{'
  5162.                         return this.parseObject()
  5163.                 case token=='['
  5164.                         return this.parseArray()
  5165.                 otherwise
  5166.                         this.setError('Unexpected token')
  5167.                 endcase
  5168.         return
  5169.                
  5170.        
  5171.         function expectedKeyword(cWord,eValue)
  5172.                 for i=1 to len(cWord)
  5173.                         cChar = this.getChar()
  5174.                         if cChar <> substr(cWord,i,1)
  5175.                                 this.setError("Expected keyword '" + cWord + "'")
  5176.                                 return ''
  5177.                         endif
  5178.                         this.nPos = this.nPos + 1
  5179.                 next
  5180.         return eValue
  5181.        
  5182.  
  5183.         function parseObject()
  5184.         local retval, cPropName, xValue
  5185.                 retval = createObject('myObj')
  5186.                 this.nPos = this.nPos + 1 && Eat {
  5187.                 if this.getToken()<>'}'
  5188.                         do while .t.
  5189.                                 cPropName = this.parseString()
  5190.                                 if not empty(this.cError)
  5191.                                         return null
  5192.                                 endif
  5193.                                 if this.getToken()<>':'
  5194.                                         this.setError("Expected ':' when parsing object")
  5195.                                         return null
  5196.                                 endif
  5197.                                 this.nPos = this.nPos + 1
  5198.                                 xValue = this.parseValue()
  5199.                                 if not empty(this.cError)
  5200.                                         return null
  5201.                                 endif                          
  5202.                                 ** Debug ? cPropName, type('xValue')
  5203.                                 retval.set(cPropName, xValue)
  5204.                                 if this.getToken()<>','
  5205.                                         exit
  5206.                                 endif
  5207.                                 this.nPos = this.nPos + 1
  5208.                         enddo
  5209.                 endif
  5210.                 if this.getToken()<>'}'
  5211.                         this.setError("Expected '}' at the end of object")
  5212.                         return null
  5213.                 endif
  5214.                 this.nPos = this.nPos + 1
  5215.         return retval
  5216.  
  5217.  
  5218.         function parseArray()
  5219.         local retVal, xValue
  5220.                 retval = createObject('MyArray')
  5221.                 this.nPos = this.nPos + 1       && Eat [
  5222.                 if this.getToken() <> ']'
  5223.                         do while .t.
  5224.                                 xValue = this.parseValue()
  5225.                                 if not empty(this.cError)
  5226.                                         return null
  5227.                                 endif
  5228.                                 retval.add( xValue )
  5229.                                 if this.getToken()<>','
  5230.                                         exit
  5231.                                 endif
  5232.                                 this.nPos = this.nPos + 1
  5233.                         enddo
  5234.                         if this.getToken() <> ']'
  5235.                                 this.setError('Expected ] at the end of array')
  5236.                                 return null
  5237.                         endif
  5238.                 endif
  5239.                 this.nPos = this.nPos + 1
  5240.         return retval
  5241.        
  5242.  
  5243.         function parseString()
  5244.         local cRetVal, c
  5245.                 if this.getToken()<>'"'
  5246.                         this.setError('Expected "')
  5247.                         return ''
  5248.                 endif
  5249.                 this.nPos = this.nPos + 1       && Eat "
  5250.                 cRetVal = ''
  5251.                 do while .t.
  5252.                         c = this.getChar()
  5253.                         if c==''
  5254.                                 return ''
  5255.                         endif
  5256.                         if c == '"'
  5257.                                 this.nPos = this.nPos + 1
  5258.                                 exit
  5259.                         endif
  5260.                         if c == '\'
  5261.                                 this.nPos = this.nPos + 1
  5262.                                 if (this.nPos>this.nLen)
  5263.                                         this.setError('\\ at the end of input')
  5264.                                         return ''
  5265.                                 endif
  5266.                                 c = this.getChar()
  5267.                                 if c==''
  5268.                                         return ''
  5269.                                 endif
  5270.                                 do case
  5271.                                 case c=='"'
  5272.                                         c='"'
  5273.                                 case c=='\'
  5274.                                         c='\'
  5275.                                 case c=='/'
  5276.                                         c='/'
  5277.                                 case c=='b'
  5278.                                         c=chr(8)
  5279.                                 case c=='t'
  5280.                                         c=chr(9)
  5281.                                 case c=='n'
  5282.                                         c=chr(10)
  5283.                                 case c=='f'
  5284.                                         c=chr(12)
  5285.                                 case c=='r'
  5286.                                         c=chr(13)
  5287.                                 otherwise
  5288.                                         ******* FALTAN LOS UNICODE
  5289.                                         this.setError('Invalid escape sequence in string literal')
  5290.                                         return ''
  5291.                                 endcase
  5292.                         endif
  5293.                         cRetVal = cRetVal + c
  5294.                         this.nPos = this.nPos + 1
  5295.                 enddo
  5296.         return cRetVal
  5297.                                        
  5298.  
  5299.         **** Pendiente numeros con E
  5300.         function parseNumber()
  5301.         local nStartPos,c, isInt, cNumero
  5302.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  5303.                         this.setError('Expected number literal')
  5304.                         return 0
  5305.                 endif
  5306.                 nStartPos = this.nPos
  5307.                 c = this.getChar()
  5308.                 if c == '-'
  5309.                         c = this.nextChar()
  5310.                 endif
  5311.                 if c == '0'
  5312.                         c = this.nextChar()
  5313.                 else
  5314.                         if isdigit(c)
  5315.                                 c = this.nextChar()
  5316.                                 do while isdigit(c)
  5317.                                         c = this.nextChar()
  5318.                                 enddo
  5319.                         else
  5320.                                 this.setError('Expected digit when parsing number')
  5321.                                 return 0
  5322.                         endif
  5323.                 endif
  5324.                
  5325.                 isInt = .t.
  5326.                 if c=='.'
  5327.                         c = this.nextChar()
  5328.                         if isdigit(c)
  5329.                                 c = this.nextChar()
  5330.                                 isInt = .f.
  5331.                                 do while isDigit(c)
  5332.                                         c = this.nextChar()
  5333.                                 enddo
  5334.                         else
  5335.                                 this.setError('Expected digit following dot comma')
  5336.                                 return 0
  5337.                         endif
  5338.                 endif
  5339.                
  5340.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  5341.         return val(cNumero)
  5342.  
  5343.  
  5344.  
  5345.         function getToken()
  5346.         local char1
  5347.                 do while .t.
  5348.                         if this.nPos > this.nLen
  5349.                                 return null
  5350.                         endif
  5351.                         char1 = substr(this.cJson, this.nPos, 1)
  5352.                         if char1==' '
  5353.                                 this.nPos = this.nPos + 1
  5354.                                 loop
  5355.                         endif
  5356.                         return char1
  5357.                 enddo
  5358.         return
  5359.        
  5360.                
  5361.                
  5362.         function getChar()
  5363.                 if this.nPos > this.nLen
  5364.                         this.setError('Unexpected end of JSON stream')
  5365.                         return ''
  5366.                 endif
  5367.         return substr(this.cJson, this.nPos, 1)
  5368.        
  5369.         function nextChar()
  5370.                 this.nPos = this.nPos + 1
  5371.                 if this.nPos > this.nLen
  5372.                         return ''
  5373.                 endif
  5374.         return substr(this.cJson, this.nPos, 1)
  5375.        
  5376.         function setError(cMsg)
  5377.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  5378.         return
  5379.  
  5380.  
  5381.         function fixUnicode(cStr)
  5382.                 cStr = StrTran(cStr,'\u00e1','á')
  5383.                 cStr = StrTran(cStr,'\u00e9','é')
  5384.                 cStr = StrTran(cStr,'\u00ed','í')
  5385.                 cStr = StrTran(cStr,'\u00f3','ó')
  5386.                 cStr = StrTran(cStr,'\u00fa','ú')
  5387.                 cStr = StrTran(cStr,'\u00c1','Á')
  5388.                 cStr = StrTran(cStr,'\u00c9','É')
  5389.                 cStr = StrTran(cStr,'\u00cd','Í')
  5390.                 cStr = StrTran(cStr,'\u00d3','Ó')
  5391.                 cStr = StrTran(cStr,'\u00da','Ú')
  5392.                 cStr = StrTran(cStr,'\u00f1','ń')
  5393.                 cStr = StrTran(cStr,'\u00d1','Ń')
  5394.         return cStr
  5395.  
  5396.  
  5397.  
  5398. enddefine
  5399.  
  5400.  
  5401.  
  5402.  
  5403.  
  5404. *
  5405. * class used to return an array
  5406. *
  5407. define class myArray as custom
  5408.         nSize = 0
  5409.         dimension array[1]
  5410.  
  5411.         function add(xExpr)
  5412.                 this.nSize = this.nSize + 1
  5413.                 dimension this.array[this.nSize]
  5414.                 this.array[this.nSize] = xExpr
  5415.         return
  5416.  
  5417.         function get(n)
  5418.         return this.array[n]
  5419.  
  5420. enddefine
  5421.  
  5422.  
  5423.  
  5424. *
  5425. * class used to simulate an object
  5426. * all properties are prefixed with 'prop' to permit property names like: error, init
  5427. * that already exists like vfp methods
  5428. *
  5429. define class myObj as custom
  5430. Hidden ;
  5431.         ClassLibrary,Comment, ;
  5432.         BaseClass,ControlCount, ;
  5433.         Controls,Objects,Object,;
  5434.         Height,HelpContextID,Left,Name, ;
  5435.         Parent,ParentClass,Picture, ;
  5436.         Tag,Top,WhatsThisHelpID,Width
  5437.                
  5438.         function set(cPropName, xValue)
  5439.                 cPropName = '_'+cPropName
  5440.                 if type('this.'+cPropName)=='U'
  5441.                         this.addProperty(cPropName,xValue)
  5442.                 else
  5443.                         local cmd
  5444.                         cmd = 'this.'+cPropName+'=xValue'
  5445.                         &cmd
  5446.                 endif
  5447.         return
  5448.        
  5449.         procedure get (cPropName)
  5450.                 cPropName = '_'+cPropName
  5451.                 If type('this.'+cPropName)=='U'
  5452.                         return ''
  5453.                 Else
  5454.                         local cmd
  5455.                         cmd = 'return this.'+cPropName
  5456.                         &cmd
  5457.                 endif
  5458.         return ''
  5459. enddefine
  5460.  
  5461.  
  5462.  
  5463.  
  5464.  
  5465. function testJsonClass
  5466.         clear
  5467.         set decimal to 10
  5468.         oJson = newObject('json')
  5469.        
  5470.        
  5471.         ? 'Test Basic Types'
  5472.         ? '----------------'
  5473.         ? oJson.decode('null')
  5474.         ? oJson.decode('true')
  5475.         ? oJson.decode('false')
  5476.         ?
  5477.         ? oJson.decode('791123')
  5478.         ? oJson.decode('791123.45')
  5479.         ? oJson.decode('791123.45.')
  5480.         ? oJson.decode('"nacho gtz"')
  5481.         if not empty(oJson.cError)
  5482.                 ? oJson.cError
  5483.                 return
  5484.         endif
  5485.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  5486.         if not empty(oJson.cError)
  5487.                 ? oJson.cError
  5488.                 return
  5489.         endif
  5490.        
  5491.         ? 'Test Array'
  5492.         ? '----------'
  5493.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  5494.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  5495.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  5496.         nombres = arr.get(1)
  5497.         edades  = arr.get(2)
  5498.         ? nombres.get(1), edades.get(1)
  5499.         ? nombres.get(2), edades.get(2)
  5500.         ? nombres.get(3), edades.get(3)
  5501.         ?
  5502.         ? 'Test Object'
  5503.         ? '-----------'
  5504.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  5505.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  5506.         ? obj._Nombre, obj._Edad, obj._IsGood
  5507.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  5508.         ? obj.get('jsonrpc'), obj._jsonrpc
  5509.         ? obj.get('id'), obj._id
  5510.         ? obj.get('method'), obj._method
  5511.         ? obj._Params.array[1], obj._Params.get(1)
  5512.  
  5513.         ?
  5514.         ? 'Test nested object'
  5515.         ? '------------------'
  5516.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  5517.         obj = oJson.decode(cJson)
  5518.         if not empty(oJson.cError)
  5519.                 ? oJson.cError
  5520.                 return
  5521.         endif
  5522.         ? cJson
  5523.         ? 'method -->',obj._method
  5524.         ? 'usrkey -->',obj._params._data._usrkey
  5525.         ? 'sendto -->',obj._params._data._sendto
  5526.         ? 'name  --->',obj._params._data._name
  5527.         ? 'expires ->',obj._params._data._expires
  5528.  
  5529.         ?
  5530.         ? 'Test empty object'
  5531.         ? '-----------------'
  5532.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  5533.         obj = oJson.decode(cJson)
  5534.         if not empty(oJson.cError)     
  5535.                 ? oJson.cError
  5536.                 return
  5537.         endif
  5538.         ? cJson
  5539.         ? 'result -->',obj._result, obj.get('result')
  5540.         oError = obj.get('error')
  5541.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  5542.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  5543.         ? 'id  ----->',obj._id, obj.get('id')
  5544.         ?  type("oError._code")
  5545.  
  5546.         ?
  5547.         ? 'Probar decode-enconde-decode-encode'
  5548.         ? '------------------------------------'
  5549.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  5550.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  5551.         ? cJson
  5552.         oSmtp = json_decode(cJson)
  5553.         cJson =  json_encode(oSmtp)
  5554.         ? cJson
  5555.         oSmtp = json_decode(cJson)
  5556.         cJson =  json_encode(oSmtp)
  5557.         ? cJson
  5558.  
  5559.         * Probar falla
  5560.         ?
  5561.         ? 'Probar una falla en el json'
  5562.         ? '---------------------------'
  5563.         cJson = ' {"server":"", "user":"", "password":"" ,'
  5564.         oSmtp = json_decode(cJson)
  5565.         if not empty(json_getErrorMsg())
  5566.                 ? json_getErrorMsg()
  5567.         endif
  5568.  
  5569.         ?
  5570.         ? 'Pruebas Finalizadas'
  5571. retur
  5572. * -----------------------------------
  5573. *
  5574. * Libreria para el manejo de JSON en VFP
  5575. *
  5576. * Gracias a Google por el codigo de Json de Dart
  5577. * Thanks Google for the code in Json Dart
  5578. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  5579. *
  5580. *
  5581. * Codificar y Decodificar JSON
  5582. *
  5583. * Puedes usar las funciones:
  5584. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  5585. *               json_decode(cJson)  te regresa el objeto representado en cJson
  5586. *
  5587. * Tambien puedes usar directamente la clase:
  5588. *
  5589. *       oJson = newobject('json','json.prg')
  5590. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  5591. *       ? oJson.encode(oCliente)
  5592. *       ? oCliente.get('nombre')
  5593. *       ? oCliente.get('apellido')
  5594. *
  5595. *
  5596. * VFPJSON  Encode and Decode JSON for VFP
  5597. * Examples:
  5598. *       oJson = newobject('json','json.prg')
  5599. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  5600. *       ? oJson.encode(oCustomer)
  5601. *       ? oCustomer.get('name')
  5602. *       ? oCustomer.get('lastname')
  5603. *
  5604. *
  5605. lRunTest = .f.
  5606. if lRunTest
  5607.         testJsonClass()
  5608. endif
  5609. return
  5610.  
  5611.  
  5612. function json_encode(xExpr)
  5613.         if vartype(_json)<>'O'
  5614.                 public _json
  5615.                 _json = newobject('json')
  5616.         endif
  5617. return _json.encode(@xExpr)
  5618.  
  5619.  
  5620. function json_decode(cJson)
  5621. local retval
  5622.         if vartype(_json)<>'O'
  5623.                 public _json
  5624.                 _json = newobject('json')
  5625.         endif
  5626.         retval = _json.decode(cJson)
  5627.         if not empty(_json.cError)
  5628.                 return null
  5629.         endif
  5630. return retval
  5631.  
  5632. function json_getErrorMsg()
  5633. return _json.cError
  5634.        
  5635.  
  5636.  
  5637. *
  5638. * json class
  5639. *
  5640. *
  5641. define class json as custom
  5642.  
  5643.  
  5644.         nPos=0
  5645.         nLen=0
  5646.         cJson=''
  5647.         cError=''
  5648.  
  5649.  
  5650.         *
  5651.         * Genera el codigo cJson para parametro que se manda
  5652.         *
  5653.         function encode(xExpr)
  5654.         local cTipo
  5655.                 * Cuando se manda una arreglo,
  5656.                 if type('ALen(xExpr)')=='N'
  5657.                         cTipo = 'A'
  5658.                 Else
  5659.                         cTipo = VarType(xExpr)
  5660.                 Endif
  5661.                
  5662.                 Do Case
  5663.                 Case cTipo=='D'
  5664.                         return '"'+dtos(xExpr)+'"'
  5665.                 Case cTipo=='N'
  5666.                         return Transform(xExpr)
  5667.                 Case cTipo=='L'
  5668.                         return iif(xExpr,'true','false')
  5669.                 Case cTipo=='X'
  5670.                         return 'null'
  5671.                 Case cTipo=='C'
  5672.                         xExpr = allt(xExpr)
  5673.                         xExpr = StrTran(xExpr, '\', '\\' )
  5674.                         xExpr = StrTran(xExpr, '/', '\/' )
  5675.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  5676.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  5677.                         xExpr = StrTran(xExpr, '"', '\"' )
  5678.                         return '"'+xExpr+'"'
  5679.  
  5680.                 case cTipo=='O'
  5681.                         local cProp, cJsonValue, cRetVal, aProp[1]
  5682.                         =AMembers(aProp,xExpr)
  5683.                         cRetVal = ''
  5684.                         for each cProp in aProp
  5685.                                 *? cProp
  5686.                                 *? cRetVal
  5687.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  5688.                                         * algunas propiedades pueden no estar definidas
  5689.                                         * como: activecontrol, parent, etc
  5690.                                         loop
  5691.                                 endif
  5692.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  5693.                                         *
  5694.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  5695.                                         *
  5696.                                         Local i,nTotElem
  5697.                                         cJsonValue = ''
  5698.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  5699.                                         For i=1 to nTotElem
  5700.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  5701.                                         Next
  5702.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  5703.                                 else
  5704.                                         *
  5705.                                         * es otro tipo de dato normal C, N, L
  5706.                                         *
  5707.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  5708.                                 endif
  5709.                                 if left(cProp,1)=='_'
  5710.                                         cProp = substr(cProp,2)
  5711.                                 endif
  5712.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  5713.                         next
  5714.                         return '{' + substr(cRetVal,2) + '}'
  5715.  
  5716.                 case cTipo=='A'
  5717.                         local valor, cRetVal
  5718.                         cRetVal = ''   
  5719.                         for each valor in xExpr
  5720.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  5721.                         next
  5722.                         return  '[' + substr(cRetVal,2) + ']'
  5723.                        
  5724.                 endcase
  5725.  
  5726.         return ''
  5727.  
  5728.  
  5729.  
  5730.  
  5731.  
  5732.         *
  5733.         * regresa un elemento representado por la cadena json que se manda
  5734.         *
  5735.        
  5736.         function decode(cJson)
  5737.         local retValue
  5738.                 cJson = StrTran(cJson,chr(9),'')
  5739.                 cJson = StrTran(cJson,chr(10),'')
  5740.                 cJson = StrTran(cJson,chr(13),'')
  5741.                 cJson = this.fixUnicode(cJson)
  5742.                 this.nPos  = 1
  5743.                 this.cJson = cJson
  5744.                 this.nLen  = len(cJson)
  5745.                 this.cError = ''
  5746.                 retValue = this.parsevalue()
  5747.                 if not empty(this.cError)
  5748.                         return null
  5749.                 endif
  5750.                 if this.getToken()<>null
  5751.                         this.setError('Junk at the end of JSON input')
  5752.                         return null
  5753.                 endif
  5754.         return retValue
  5755.                
  5756.        
  5757.         function parseValue()
  5758.         local token
  5759.                 token = this.getToken()
  5760.                 if token==null
  5761.                         this.setError('Nothing to parse')
  5762.                         return null
  5763.                 endif
  5764.                 do case
  5765.                 case token=='"'
  5766.                         return this.parseString()
  5767.                 case isdigit(token) or token=='-'
  5768.                         return this.parseNumber()
  5769.                 case token=='n'
  5770.                         return this.expectedKeyword('null',null)
  5771.                 case token=='f'
  5772.                         return this.expectedKeyword('false',.f.)
  5773.                 case token=='t'
  5774.                         return this.expectedKeyword('true',.t.)
  5775.                 case token=='{'
  5776.                         return this.parseObject()
  5777.                 case token=='['
  5778.                         return this.parseArray()
  5779.                 otherwise
  5780.                         this.setError('Unexpected token')
  5781.                 endcase
  5782.         return
  5783.                
  5784.        
  5785.         function expectedKeyword(cWord,eValue)
  5786.                 for i=1 to len(cWord)
  5787.                         cChar = this.getChar()
  5788.                         if cChar <> substr(cWord,i,1)
  5789.                                 this.setError("Expected keyword '" + cWord + "'")
  5790.                                 return ''
  5791.                         endif
  5792.                         this.nPos = this.nPos + 1
  5793.                 next
  5794.         return eValue
  5795.        
  5796.  
  5797.         function parseObject()
  5798.         local retval, cPropName, xValue
  5799.                 retval = createObject('myObj')
  5800.                 this.nPos = this.nPos + 1 && Eat {
  5801.                 if this.getToken()<>'}'
  5802.                         do while .t.
  5803.                                 cPropName = this.parseString()
  5804.                                 if not empty(this.cError)
  5805.                                         return null
  5806.                                 endif
  5807.                                 if this.getToken()<>':'
  5808.                                         this.setError("Expected ':' when parsing object")
  5809.                                         return null
  5810.                                 endif
  5811.                                 this.nPos = this.nPos + 1
  5812.                                 xValue = this.parseValue()
  5813.                                 if not empty(this.cError)
  5814.                                         return null
  5815.                                 endif                          
  5816.                                 ** Debug ? cPropName, type('xValue')
  5817.                                 retval.set(cPropName, xValue)
  5818.                                 if this.getToken()<>','
  5819.                                         exit
  5820.                                 endif
  5821.                                 this.nPos = this.nPos + 1
  5822.                         enddo
  5823.                 endif
  5824.                 if this.getToken()<>'}'
  5825.                         this.setError("Expected '}' at the end of object")
  5826.                         return null
  5827.                 endif
  5828.                 this.nPos = this.nPos + 1
  5829.         return retval
  5830.  
  5831.  
  5832.         function parseArray()
  5833.         local retVal, xValue
  5834.                 retval = createObject('MyArray')
  5835.                 this.nPos = this.nPos + 1       && Eat [
  5836.                 if this.getToken() <> ']'
  5837.                         do while .t.
  5838.                                 xValue = this.parseValue()
  5839.                                 if not empty(this.cError)
  5840.                                         return null
  5841.                                 endif
  5842.                                 retval.add( xValue )
  5843.                                 if this.getToken()<>','
  5844.                                         exit
  5845.                                 endif
  5846.                                 this.nPos = this.nPos + 1
  5847.                         enddo
  5848.                         if this.getToken() <> ']'
  5849.                                 this.setError('Expected ] at the end of array')
  5850.                                 return null
  5851.                         endif
  5852.                 endif
  5853.                 this.nPos = this.nPos + 1
  5854.         return retval
  5855.        
  5856.  
  5857.         function parseString()
  5858.         local cRetVal, c
  5859.                 if this.getToken()<>'"'
  5860.                         this.setError('Expected "')
  5861.                         return ''
  5862.                 endif
  5863.                 this.nPos = this.nPos + 1       && Eat "
  5864.                 cRetVal = ''
  5865.                 do while .t.
  5866.                         c = this.getChar()
  5867.                         if c==''
  5868.                                 return ''
  5869.                         endif
  5870.                         if c == '"'
  5871.                                 this.nPos = this.nPos + 1
  5872.                                 exit
  5873.                         endif
  5874.                         if c == '\'
  5875.                                 this.nPos = this.nPos + 1
  5876.                                 if (this.nPos>this.nLen)
  5877.                                         this.setError('\\ at the end of input')
  5878.                                         return ''
  5879.                                 endif
  5880.                                 c = this.getChar()
  5881.                                 if c==''
  5882.                                         return ''
  5883.                                 endif
  5884.                                 do case
  5885.                                 case c=='"'
  5886.                                         c='"'
  5887.                                 case c=='\'
  5888.                                         c='\'
  5889.                                 case c=='/'
  5890.                                         c='/'
  5891.                                 case c=='b'
  5892.                                         c=chr(8)
  5893.                                 case c=='t'
  5894.                                         c=chr(9)
  5895.                                 case c=='n'
  5896.                                         c=chr(10)
  5897.                                 case c=='f'
  5898.                                         c=chr(12)
  5899.                                 case c=='r'
  5900.                                         c=chr(13)
  5901.                                 otherwise
  5902.                                         ******* FALTAN LOS UNICODE
  5903.                                         this.setError('Invalid escape sequence in string literal')
  5904.                                         return ''
  5905.                                 endcase
  5906.                         endif
  5907.                         cRetVal = cRetVal + c
  5908.                         this.nPos = this.nPos + 1
  5909.                 enddo
  5910.         return cRetVal
  5911.                                        
  5912.  
  5913.         **** Pendiente numeros con E
  5914.         function parseNumber()
  5915.         local nStartPos,c, isInt, cNumero
  5916.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  5917.                         this.setError('Expected number literal')
  5918.                         return 0
  5919.                 endif
  5920.                 nStartPos = this.nPos
  5921.                 c = this.getChar()
  5922.                 if c == '-'
  5923.                         c = this.nextChar()
  5924.                 endif
  5925.                 if c == '0'
  5926.                         c = this.nextChar()
  5927.                 else
  5928.                         if isdigit(c)
  5929.                                 c = this.nextChar()
  5930.                                 do while isdigit(c)
  5931.                                         c = this.nextChar()
  5932.                                 enddo
  5933.                         else
  5934.                                 this.setError('Expected digit when parsing number')
  5935.                                 return 0
  5936.                         endif
  5937.                 endif
  5938.                
  5939.                 isInt = .t.
  5940.                 if c=='.'
  5941.                         c = this.nextChar()
  5942.                         if isdigit(c)
  5943.                                 c = this.nextChar()
  5944.                                 isInt = .f.
  5945.                                 do while isDigit(c)
  5946.                                         c = this.nextChar()
  5947.                                 enddo
  5948.                         else
  5949.                                 this.setError('Expected digit following dot comma')
  5950.                                 return 0
  5951.                         endif
  5952.                 endif
  5953.                
  5954.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  5955.         return val(cNumero)
  5956.  
  5957.  
  5958.  
  5959.         function getToken()
  5960.         local char1
  5961.                 do while .t.
  5962.                         if this.nPos > this.nLen
  5963.                                 return null
  5964.                         endif
  5965.                         char1 = substr(this.cJson, this.nPos, 1)
  5966.                         if char1==' '
  5967.                                 this.nPos = this.nPos + 1
  5968.                                 loop
  5969.                         endif
  5970.                         return char1
  5971.                 enddo
  5972.         return
  5973.        
  5974.                
  5975.                
  5976.         function getChar()
  5977.                 if this.nPos > this.nLen
  5978.                         this.setError('Unexpected end of JSON stream')
  5979.                         return ''
  5980.                 endif
  5981.         return substr(this.cJson, this.nPos, 1)
  5982.        
  5983.         function nextChar()
  5984.                 this.nPos = this.nPos + 1
  5985.                 if this.nPos > this.nLen
  5986.                         return ''
  5987.                 endif
  5988.         return substr(this.cJson, this.nPos, 1)
  5989.        
  5990.         function setError(cMsg)
  5991.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  5992.         return
  5993.  
  5994.  
  5995.         function fixUnicode(cStr)
  5996.                 cStr = StrTran(cStr,'\u00e1','á')
  5997.                 cStr = StrTran(cStr,'\u00e9','é')
  5998.                 cStr = StrTran(cStr,'\u00ed','í')
  5999.                 cStr = StrTran(cStr,'\u00f3','ó')
  6000.                 cStr = StrTran(cStr,'\u00fa','ú')
  6001.                 cStr = StrTran(cStr,'\u00c1','Á')
  6002.                 cStr = StrTran(cStr,'\u00c9','É')
  6003.                 cStr = StrTran(cStr,'\u00cd','Í')
  6004.                 cStr = StrTran(cStr,'\u00d3','Ó')
  6005.                 cStr = StrTran(cStr,'\u00da','Ú')
  6006.                 cStr = StrTran(cStr,'\u00f1','ń')
  6007.                 cStr = StrTran(cStr,'\u00d1','Ń')
  6008.         return cStr
  6009.  
  6010.  
  6011.  
  6012. enddefine
  6013.  
  6014.  
  6015.  
  6016.  
  6017.  
  6018. *
  6019. * class used to return an array
  6020. *
  6021. define class myArray as custom
  6022.         nSize = 0
  6023.         dimension array[1]
  6024.  
  6025.         function add(xExpr)
  6026.                 this.nSize = this.nSize + 1
  6027.                 dimension this.array[this.nSize]
  6028.                 this.array[this.nSize] = xExpr
  6029.         return
  6030.  
  6031.         function get(n)
  6032.         return this.array[n]
  6033.  
  6034. enddefine
  6035.  
  6036.  
  6037.  
  6038. *
  6039. * class used to simulate an object
  6040. * all properties are prefixed with 'prop' to permit property names like: error, init
  6041. * that already exists like vfp methods
  6042. *
  6043. define class myObj as custom
  6044. Hidden ;
  6045.         ClassLibrary,Comment, ;
  6046.         BaseClass,ControlCount, ;
  6047.         Controls,Objects,Object,;
  6048.         Height,HelpContextID,Left,Name, ;
  6049.         Parent,ParentClass,Picture, ;
  6050.         Tag,Top,WhatsThisHelpID,Width
  6051.                
  6052.         function set(cPropName, xValue)
  6053.                 cPropName = '_'+cPropName
  6054.                 if type('this.'+cPropName)=='U'
  6055.                         this.addProperty(cPropName,xValue)
  6056.                 else
  6057.                         local cmd
  6058.                         cmd = 'this.'+cPropName+'=xValue'
  6059.                         &cmd
  6060.                 endif
  6061.         return
  6062.        
  6063.         procedure get (cPropName)
  6064.                 cPropName = '_'+cPropName
  6065.                 If type('this.'+cPropName)=='U'
  6066.                         return ''
  6067.                 Else
  6068.                         local cmd
  6069.                         cmd = 'return this.'+cPropName
  6070.                         &cmd
  6071.                 endif
  6072.         return ''
  6073. enddefine
  6074.  
  6075.  
  6076.  
  6077.  
  6078.  
  6079. function testJsonClass
  6080.         clear
  6081.         set decimal to 10
  6082.         oJson = newObject('json')
  6083.        
  6084.        
  6085.         ? 'Test Basic Types'
  6086.         ? '----------------'
  6087.         ? oJson.decode('null')
  6088.         ? oJson.decode('true')
  6089.         ? oJson.decode('false')
  6090.         ?
  6091.         ? oJson.decode('791123')
  6092.         ? oJson.decode('791123.45')
  6093.         ? oJson.decode('791123.45.')
  6094.         ? oJson.decode('"nacho gtz"')
  6095.         if not empty(oJson.cError)
  6096.                 ? oJson.cError
  6097.                 return
  6098.         endif
  6099.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  6100.         if not empty(oJson.cError)
  6101.                 ? oJson.cError
  6102.                 return
  6103.         endif
  6104.        
  6105.         ? 'Test Array'
  6106.         ? '----------'
  6107.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  6108.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  6109.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  6110.         nombres = arr.get(1)
  6111.         edades  = arr.get(2)
  6112.         ? nombres.get(1), edades.get(1)
  6113.         ? nombres.get(2), edades.get(2)
  6114.         ? nombres.get(3), edades.get(3)
  6115.         ?
  6116.         ? 'Test Object'
  6117.         ? '-----------'
  6118.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  6119.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  6120.         ? obj._Nombre, obj._Edad, obj._IsGood
  6121.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  6122.         ? obj.get('jsonrpc'), obj._jsonrpc
  6123.         ? obj.get('id'), obj._id
  6124.         ? obj.get('method'), obj._method
  6125.         ? obj._Params.array[1], obj._Params.get(1)
  6126.  
  6127.         ?
  6128.         ? 'Test nested object'
  6129.         ? '------------------'
  6130.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  6131.         obj = oJson.decode(cJson)
  6132.         if not empty(oJson.cError)
  6133.                 ? oJson.cError
  6134.                 return
  6135.         endif
  6136.         ? cJson
  6137.         ? 'method -->',obj._method
  6138.         ? 'usrkey -->',obj._params._data._usrkey
  6139.         ? 'sendto -->',obj._params._data._sendto
  6140.         ? 'name  --->',obj._params._data._name
  6141.         ? 'expires ->',obj._params._data._expires
  6142.  
  6143.         ?
  6144.         ? 'Test empty object'
  6145.         ? '-----------------'
  6146.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  6147.         obj = oJson.decode(cJson)
  6148.         if not empty(oJson.cError)     
  6149.                 ? oJson.cError
  6150.                 return
  6151.         endif
  6152.         ? cJson
  6153.         ? 'result -->',obj._result, obj.get('result')
  6154.         oError = obj.get('error')
  6155.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  6156.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  6157.         ? 'id  ----->',obj._id, obj.get('id')
  6158.         ?  type("oError._code")
  6159.  
  6160.         ?
  6161.         ? 'Probar decode-enconde-decode-encode'
  6162.         ? '------------------------------------'
  6163.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  6164.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  6165.         ? cJson
  6166.         oSmtp = json_decode(cJson)
  6167.         cJson =  json_encode(oSmtp)
  6168.         ? cJson
  6169.         oSmtp = json_decode(cJson)
  6170.         cJson =  json_encode(oSmtp)
  6171.         ? cJson
  6172.  
  6173.         * Probar falla
  6174.         ?
  6175.         ? 'Probar una falla en el json'
  6176.         ? '---------------------------'
  6177.         cJson = ' {"server":"", "user":"", "password":"" ,'
  6178.         oSmtp = json_decode(cJson)
  6179.         if not empty(json_getErrorMsg())
  6180.                 ? json_getErrorMsg()
  6181.         endif
  6182.  
  6183.         ?
  6184.         ? 'Pruebas Finalizadas'
  6185. retur
  6186. *
  6187. * Libreria para el manejo de JSON en VFP
  6188. *
  6189. * Gracias a Google por el codigo de Json de Dart
  6190. * Thanks Google for the code in Json Dart
  6191. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  6192. *
  6193. *
  6194. * Codificar y Decodificar JSON
  6195. *
  6196. * Puedes usar las funciones:
  6197. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  6198. *               json_decode(cJson)  te regresa el objeto representado en cJson
  6199. *
  6200. * Tambien puedes usar directamente la clase:
  6201. *
  6202. *       oJson = newobject('json','json.prg')
  6203. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  6204. *       ? oJson.encode(oCliente)
  6205. *       ? oCliente.get('nombre')
  6206. *       ? oCliente.get('apellido')
  6207. *
  6208. *
  6209. * VFPJSON  Encode and Decode JSON for VFP
  6210. * Examples:
  6211. *       oJson = newobject('json','json.prg')
  6212. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  6213. *       ? oJson.encode(oCustomer)
  6214. *       ? oCustomer.get('name')
  6215. *       ? oCustomer.get('lastname')
  6216. *
  6217. *
  6218. lRunTest = .f.
  6219. if lRunTest
  6220.         testJsonClass()
  6221. endif
  6222. return
  6223.  
  6224.  
  6225. function json_encode(xExpr)
  6226.         if vartype(_json)<>'O'
  6227.                 public _json
  6228.                 _json = newobject('json')
  6229.         endif
  6230. return _json.encode(@xExpr)
  6231.  
  6232.  
  6233. function json_decode(cJson)
  6234. local retval
  6235.         if vartype(_json)<>'O'
  6236.                 public _json
  6237.                 _json = newobject('json')
  6238.         endif
  6239.         retval = _json.decode(cJson)
  6240.         if not empty(_json.cError)
  6241.                 return null
  6242.         endif
  6243. return retval
  6244.  
  6245. function json_getErrorMsg()
  6246. return _json.cError
  6247.        
  6248.  
  6249.  
  6250. *
  6251. * json class
  6252. *
  6253. *
  6254. define class json as custom
  6255.  
  6256.  
  6257.         nPos=0
  6258.         nLen=0
  6259.         cJson=''
  6260.         cError=''
  6261.  
  6262.  
  6263.         *
  6264.         * Genera el codigo cJson para parametro que se manda
  6265.         *
  6266.         function encode(xExpr)
  6267.         local cTipo
  6268.                 * Cuando se manda una arreglo,
  6269.                 if type('ALen(xExpr)')=='N'
  6270.                         cTipo = 'A'
  6271.                 Else
  6272.                         cTipo = VarType(xExpr)
  6273.                 Endif
  6274.                
  6275.                 Do Case
  6276.                 Case cTipo=='D'
  6277.                         return '"'+dtos(xExpr)+'"'
  6278.                 Case cTipo=='N'
  6279.                         return Transform(xExpr)
  6280.                 Case cTipo=='L'
  6281.                         return iif(xExpr,'true','false')
  6282.                 Case cTipo=='X'
  6283.                         return 'null'
  6284.                 Case cTipo=='C'
  6285.                         xExpr = allt(xExpr)
  6286.                         xExpr = StrTran(xExpr, '\', '\\' )
  6287.                         xExpr = StrTran(xExpr, '/', '\/' )
  6288.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  6289.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  6290.                         xExpr = StrTran(xExpr, '"', '\"' )
  6291.                         return '"'+xExpr+'"'
  6292.  
  6293.                 case cTipo=='O'
  6294.                         local cProp, cJsonValue, cRetVal, aProp[1]
  6295.                         =AMembers(aProp,xExpr)
  6296.                         cRetVal = ''
  6297.                         for each cProp in aProp
  6298.                                 *? cProp
  6299.                                 *? cRetVal
  6300.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  6301.                                         * algunas propiedades pueden no estar definidas
  6302.                                         * como: activecontrol, parent, etc
  6303.                                         loop
  6304.                                 endif
  6305.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  6306.                                         *
  6307.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  6308.                                         *
  6309.                                         Local i,nTotElem
  6310.                                         cJsonValue = ''
  6311.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  6312.                                         For i=1 to nTotElem
  6313.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  6314.                                         Next
  6315.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  6316.                                 else
  6317.                                         *
  6318.                                         * es otro tipo de dato normal C, N, L
  6319.                                         *
  6320.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  6321.                                 endif
  6322.                                 if left(cProp,1)=='_'
  6323.                                         cProp = substr(cProp,2)
  6324.                                 endif
  6325.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  6326.                         next
  6327.                         return '{' + substr(cRetVal,2) + '}'
  6328.  
  6329.                 case cTipo=='A'
  6330.                         local valor, cRetVal
  6331.                         cRetVal = ''   
  6332.                         for each valor in xExpr
  6333.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  6334.                         next
  6335.                         return  '[' + substr(cRetVal,2) + ']'
  6336.                        
  6337.                 endcase
  6338.  
  6339.         return ''
  6340.  
  6341.  
  6342.  
  6343.  
  6344.  
  6345.         *
  6346.         * regresa un elemento representado por la cadena json que se manda
  6347.         *
  6348.        
  6349.         function decode(cJson)
  6350.         local retValue
  6351.                 cJson = StrTran(cJson,chr(9),'')
  6352.                 cJson = StrTran(cJson,chr(10),'')
  6353.                 cJson = StrTran(cJson,chr(13),'')
  6354.                 cJson = this.fixUnicode(cJson)
  6355.                 this.nPos  = 1
  6356.                 this.cJson = cJson
  6357.                 this.nLen  = len(cJson)
  6358.                 this.cError = ''
  6359.                 retValue = this.parsevalue()
  6360.                 if not empty(this.cError)
  6361.                         return null
  6362.                 endif
  6363.                 if this.getToken()<>null
  6364.                         this.setError('Junk at the end of JSON input')
  6365.                         return null
  6366.                 endif
  6367.         return retValue
  6368.                
  6369.        
  6370.         function parseValue()
  6371.         local token
  6372.                 token = this.getToken()
  6373.                 if token==null
  6374.                         this.setError('Nothing to parse')
  6375.                         return null
  6376.                 endif
  6377.                 do case
  6378.                 case token=='"'
  6379.                         return this.parseString()
  6380.                 case isdigit(token) or token=='-'
  6381.                         return this.parseNumber()
  6382.                 case token=='n'
  6383.                         return this.expectedKeyword('null',null)
  6384.                 case token=='f'
  6385.                         return this.expectedKeyword('false',.f.)
  6386.                 case token=='t'
  6387.                         return this.expectedKeyword('true',.t.)
  6388.                 case token=='{'
  6389.                         return this.parseObject()
  6390.                 case token=='['
  6391.                         return this.parseArray()
  6392.                 otherwise
  6393.                         this.setError('Unexpected token')
  6394.                 endcase
  6395.         return
  6396.                
  6397.        
  6398.         function expectedKeyword(cWord,eValue)
  6399.                 for i=1 to len(cWord)
  6400.                         cChar = this.getChar()
  6401.                         if cChar <> substr(cWord,i,1)
  6402.                                 this.setError("Expected keyword '" + cWord + "'")
  6403.                                 return ''
  6404.                         endif
  6405.                         this.nPos = this.nPos + 1
  6406.                 next
  6407.         return eValue
  6408.        
  6409.  
  6410.         function parseObject()
  6411.         local retval, cPropName, xValue
  6412.                 retval = createObject('myObj')
  6413.                 this.nPos = this.nPos + 1 && Eat {
  6414.                 if this.getToken()<>'}'
  6415.                         do while .t.
  6416.                                 cPropName = this.parseString()
  6417.                                 if not empty(this.cError)
  6418.                                         return null
  6419.                                 endif
  6420.                                 if this.getToken()<>':'
  6421.                                         this.setError("Expected ':' when parsing object")
  6422.                                         return null
  6423.                                 endif
  6424.                                 this.nPos = this.nPos + 1
  6425.                                 xValue = this.parseValue()
  6426.                                 if not empty(this.cError)
  6427.                                         return null
  6428.                                 endif                          
  6429.                                 ** Debug ? cPropName, type('xValue')
  6430.                                 retval.set(cPropName, xValue)
  6431.                                 if this.getToken()<>','
  6432.                                         exit
  6433.                                 endif
  6434.                                 this.nPos = this.nPos + 1
  6435.                         enddo
  6436.                 endif
  6437.                 if this.getToken()<>'}'
  6438.                         this.setError("Expected '}' at the end of object")
  6439.                         return null
  6440.                 endif
  6441.                 this.nPos = this.nPos + 1
  6442.         return retval
  6443.  
  6444.  
  6445.         function parseArray()
  6446.         local retVal, xValue
  6447.                 retval = createObject('MyArray')
  6448.                 this.nPos = this.nPos + 1       && Eat [
  6449.                 if this.getToken() <> ']'
  6450.                         do while .t.
  6451.                                 xValue = this.parseValue()
  6452.                                 if not empty(this.cError)
  6453.                                         return null
  6454.                                 endif
  6455.                                 retval.add( xValue )
  6456.                                 if this.getToken()<>','
  6457.                                         exit
  6458.                                 endif
  6459.                                 this.nPos = this.nPos + 1
  6460.                         enddo
  6461.                         if this.getToken() <> ']'
  6462.                                 this.setError('Expected ] at the end of array')
  6463.                                 return null
  6464.                         endif
  6465.                 endif
  6466.                 this.nPos = this.nPos + 1
  6467.         return retval
  6468.        
  6469.  
  6470.         function parseString()
  6471.         local cRetVal, c
  6472.                 if this.getToken()<>'"'
  6473.                         this.setError('Expected "')
  6474.                         return ''
  6475.                 endif
  6476.                 this.nPos = this.nPos + 1       && Eat "
  6477.                 cRetVal = ''
  6478.                 do while .t.
  6479.                         c = this.getChar()
  6480.                         if c==''
  6481.                                 return ''
  6482.                         endif
  6483.                         if c == '"'
  6484.                                 this.nPos = this.nPos + 1
  6485.                                 exit
  6486.                         endif
  6487.                         if c == '\'
  6488.                                 this.nPos = this.nPos + 1
  6489.                                 if (this.nPos>this.nLen)
  6490.                                         this.setError('\\ at the end of input')
  6491.                                         return ''
  6492.                                 endif
  6493.                                 c = this.getChar()
  6494.                                 if c==''
  6495.                                         return ''
  6496.                                 endif
  6497.                                 do case
  6498.                                 case c=='"'
  6499.                                         c='"'
  6500.                                 case c=='\'
  6501.                                         c='\'
  6502.                                 case c=='/'
  6503.                                         c='/'
  6504.                                 case c=='b'
  6505.                                         c=chr(8)
  6506.                                 case c=='t'
  6507.                                         c=chr(9)
  6508.                                 case c=='n'
  6509.                                         c=chr(10)
  6510.                                 case c=='f'
  6511.                                         c=chr(12)
  6512.                                 case c=='r'
  6513.                                         c=chr(13)
  6514.                                 otherwise
  6515.                                         ******* FALTAN LOS UNICODE
  6516.                                         this.setError('Invalid escape sequence in string literal')
  6517.                                         return ''
  6518.                                 endcase
  6519.                         endif
  6520.                         cRetVal = cRetVal + c
  6521.                         this.nPos = this.nPos + 1
  6522.                 enddo
  6523.         return cRetVal
  6524.                                        
  6525.  
  6526.         **** Pendiente numeros con E
  6527.         function parseNumber()
  6528.         local nStartPos,c, isInt, cNumero
  6529.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  6530.                         this.setError('Expected number literal')
  6531.                         return 0
  6532.                 endif
  6533.                 nStartPos = this.nPos
  6534.                 c = this.getChar()
  6535.                 if c == '-'
  6536.                         c = this.nextChar()
  6537.                 endif
  6538.                 if c == '0'
  6539.                         c = this.nextChar()
  6540.                 else
  6541.                         if isdigit(c)
  6542.                                 c = this.nextChar()
  6543.                                 do while isdigit(c)
  6544.                                         c = this.nextChar()
  6545.                                 enddo
  6546.                         else
  6547.                                 this.setError('Expected digit when parsing number')
  6548.                                 return 0
  6549.                         endif
  6550.                 endif
  6551.                
  6552.                 isInt = .t.
  6553.                 if c=='.'
  6554.                         c = this.nextChar()
  6555.                         if isdigit(c)
  6556.                                 c = this.nextChar()
  6557.                                 isInt = .f.
  6558.                                 do while isDigit(c)
  6559.                                         c = this.nextChar()
  6560.                                 enddo
  6561.                         else
  6562.                                 this.setError('Expected digit following dot comma')
  6563.                                 return 0
  6564.                         endif
  6565.                 endif
  6566.                
  6567.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  6568.         return val(cNumero)
  6569.  
  6570.  
  6571.  
  6572.         function getToken()
  6573.         local char1
  6574.                 do while .t.
  6575.                         if this.nPos > this.nLen
  6576.                                 return null
  6577.                         endif
  6578.                         char1 = substr(this.cJson, this.nPos, 1)
  6579.                         if char1==' '
  6580.                                 this.nPos = this.nPos + 1
  6581.                                 loop
  6582.                         endif
  6583.                         return char1
  6584.                 enddo
  6585.         return
  6586.        
  6587.                
  6588.                
  6589.         function getChar()
  6590.                 if this.nPos > this.nLen
  6591.                         this.setError('Unexpected end of JSON stream')
  6592.                         return ''
  6593.                 endif
  6594.         return substr(this.cJson, this.nPos, 1)
  6595.        
  6596.         function nextChar()
  6597.                 this.nPos = this.nPos + 1
  6598.                 if this.nPos > this.nLen
  6599.                         return ''
  6600.                 endif
  6601.         return substr(this.cJson, this.nPos, 1)
  6602.        
  6603.         function setError(cMsg)
  6604.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  6605.         return
  6606.  
  6607.  
  6608.         function fixUnicode(cStr)
  6609.                 cStr = StrTran(cStr,'\u00e1','á')
  6610.                 cStr = StrTran(cStr,'\u00e9','é')
  6611.                 cStr = StrTran(cStr,'\u00ed','í')
  6612.                 cStr = StrTran(cStr,'\u00f3','ó')
  6613.                 cStr = StrTran(cStr,'\u00fa','ú')
  6614.                 cStr = StrTran(cStr,'\u00c1','Á')
  6615.                 cStr = StrTran(cStr,'\u00c9','É')
  6616.                 cStr = StrTran(cStr,'\u00cd','Í')
  6617.                 cStr = StrTran(cStr,'\u00d3','Ó')
  6618.                 cStr = StrTran(cStr,'\u00da','Ú')
  6619.                 cStr = StrTran(cStr,'\u00f1','ń')
  6620.                 cStr = StrTran(cStr,'\u00d1','Ń')
  6621.         return cStr
  6622.  
  6623.  
  6624.  
  6625. enddefine
  6626.  
  6627.  
  6628.  
  6629.  
  6630.  
  6631. *
  6632. * class used to return an array
  6633. *
  6634. define class myArray as custom
  6635.         nSize = 0
  6636.         dimension array[1]
  6637.  
  6638.         function add(xExpr)
  6639.                 this.nSize = this.nSize + 1
  6640.                 dimension this.array[this.nSize]
  6641.                 this.array[this.nSize] = xExpr
  6642.         return
  6643.  
  6644.         function get(n)
  6645.         return this.array[n]
  6646.  
  6647. enddefine
  6648.  
  6649.  
  6650.  
  6651. *
  6652. * class used to simulate an object
  6653. * all properties are prefixed with 'prop' to permit property names like: error, init
  6654. * that already exists like vfp methods
  6655. *
  6656. define class myObj as custom
  6657. Hidden ;
  6658.         ClassLibrary,Comment, ;
  6659.         BaseClass,ControlCount, ;
  6660.         Controls,Objects,Object,;
  6661.         Height,HelpContextID,Left,Name, ;
  6662.         Parent,ParentClass,Picture, ;
  6663.         Tag,Top,WhatsThisHelpID,Width
  6664.                
  6665.         function set(cPropName, xValue)
  6666.                 cPropName = '_'+cPropName
  6667.                 if type('this.'+cPropName)=='U'
  6668.                         this.addProperty(cPropName,xValue)
  6669.                 else
  6670.                         local cmd
  6671.                         cmd = 'this.'+cPropName+'=xValue'
  6672.                         &cmd
  6673.                 endif
  6674.         return
  6675.        
  6676.         procedure get (cPropName)
  6677.                 cPropName = '_'+cPropName
  6678.                 If type('this.'+cPropName)=='U'
  6679.                         return ''
  6680.                 Else
  6681.                         local cmd
  6682.                         cmd = 'return this.'+cPropName
  6683.                         &cmd
  6684.                 endif
  6685.         return ''
  6686. enddefine
  6687.  
  6688.  
  6689.  
  6690.  
  6691.  
  6692. function testJsonClass
  6693.         clear
  6694.         set decimal to 10
  6695.         oJson = newObject('json')
  6696.        
  6697.        
  6698.         ? 'Test Basic Types'
  6699.         ? '----------------'
  6700.         ? oJson.decode('null')
  6701.         ? oJson.decode('true')
  6702.         ? oJson.decode('false')
  6703.         ?
  6704.         ? oJson.decode('791123')
  6705.         ? oJson.decode('791123.45')
  6706.         ? oJson.decode('791123.45.')
  6707.         ? oJson.decode('"nacho gtz"')
  6708.         if not empty(oJson.cError)
  6709.                 ? oJson.cError
  6710.                 return
  6711.         endif
  6712.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  6713.         if not empty(oJson.cError)
  6714.                 ? oJson.cError
  6715.                 return
  6716.         endif
  6717.        
  6718.         ? 'Test Array'
  6719.         ? '----------'
  6720.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  6721.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  6722.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  6723.         nombres = arr.get(1)
  6724.         edades  = arr.get(2)
  6725.         ? nombres.get(1), edades.get(1)
  6726.         ? nombres.get(2), edades.get(2)
  6727.         ? nombres.get(3), edades.get(3)
  6728.         ?
  6729.         ? 'Test Object'
  6730.         ? '-----------'
  6731.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  6732.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  6733.         ? obj._Nombre, obj._Edad, obj._IsGood
  6734.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  6735.         ? obj.get('jsonrpc'), obj._jsonrpc
  6736.         ? obj.get('id'), obj._id
  6737.         ? obj.get('method'), obj._method
  6738.         ? obj._Params.array[1], obj._Params.get(1)
  6739.  
  6740.         ?
  6741.         ? 'Test nested object'
  6742.         ? '------------------'
  6743.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  6744.         obj = oJson.decode(cJson)
  6745.         if not empty(oJson.cError)
  6746.                 ? oJson.cError
  6747.                 return
  6748.         endif
  6749.         ? cJson
  6750.         ? 'method -->',obj._method
  6751.         ? 'usrkey -->',obj._params._data._usrkey
  6752.         ? 'sendto -->',obj._params._data._sendto
  6753.         ? 'name  --->',obj._params._data._name
  6754.         ? 'expires ->',obj._params._data._expires
  6755.  
  6756.         ?
  6757.         ? 'Test empty object'
  6758.         ? '-----------------'
  6759.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  6760.         obj = oJson.decode(cJson)
  6761.         if not empty(oJson.cError)     
  6762.                 ? oJson.cError
  6763.                 return
  6764.         endif
  6765.         ? cJson
  6766.         ? 'result -->',obj._result, obj.get('result')
  6767.         oError = obj.get('error')
  6768.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  6769.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  6770.         ? 'id  ----->',obj._id, obj.get('id')
  6771.         ?  type("oError._code")
  6772.  
  6773.         ?
  6774.         ? 'Probar decode-enconde-decode-encode'
  6775.         ? '------------------------------------'
  6776.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  6777.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  6778.         ? cJson
  6779.         oSmtp = json_decode(cJson)
  6780.         cJson =  json_encode(oSmtp)
  6781.         ? cJson
  6782.         oSmtp = json_decode(cJson)
  6783.         cJson =  json_encode(oSmtp)
  6784.         ? cJson
  6785.  
  6786.         * Probar falla
  6787.         ?
  6788.         ? 'Probar una falla en el json'
  6789.         ? '---------------------------'
  6790.         cJson = ' {"server":"", "user":"", "password":"" ,'
  6791.         oSmtp = json_decode(cJson)
  6792.         if not empty(json_getErrorMsg())
  6793.                 ? json_getErrorMsg()
  6794.         endif
  6795.  
  6796.         ?
  6797.         ? 'Pruebas Finalizadas'
  6798. retur
  6799. * Libreria para el manejo de JSON en VFP
  6800. *
  6801. * Gracias a Google por el codigo de Json de Dart
  6802. * Thanks Google for the code in Json Dart
  6803. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  6804. *
  6805. *
  6806. * Codificar y Decodificar JSON
  6807. *
  6808. * Puedes usar las funciones:
  6809. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  6810. *               json_decode(cJson)  te regresa el objeto representado en cJson
  6811. *
  6812. * Tambien puedes usar directamente la clase:
  6813. *
  6814. *       oJson = newobject('json','json.prg')
  6815. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  6816. *       ? oJson.encode(oCliente)
  6817. *       ? oCliente.get('nombre')
  6818. *       ? oCliente.get('apellido')
  6819. *
  6820. *
  6821. * VFPJSON  Encode and Decode JSON for VFP
  6822. * Examples:
  6823. *       oJson = newobject('json','json.prg')
  6824. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  6825. *       ? oJson.encode(oCustomer)
  6826. *       ? oCustomer.get('name')
  6827. *       ? oCustomer.get('lastname')
  6828. *
  6829. *
  6830. lRunTest = .f.
  6831. if lRunTest
  6832.         testJsonClass()
  6833. endif
  6834. return
  6835.  
  6836.  
  6837. function json_encode(xExpr)
  6838.         if vartype(_json)<>'O'
  6839.                 public _json
  6840.                 _json = newobject('json')
  6841.         endif
  6842. return _json.encode(@xExpr)
  6843.  
  6844.  
  6845. function json_decode(cJson)
  6846. local retval
  6847.         if vartype(_json)<>'O'
  6848.                 public _json
  6849.                 _json = newobject('json')
  6850.         endif
  6851.         retval = _json.decode(cJson)
  6852.         if not empty(_json.cError)
  6853.                 return null
  6854.         endif
  6855. return retval
  6856.  
  6857. function json_getErrorMsg()
  6858. return _json.cError
  6859.        
  6860.  
  6861.  
  6862. *
  6863. * json class
  6864. *
  6865. *
  6866. define class json as custom
  6867.  
  6868.  
  6869.         nPos=0
  6870.         nLen=0
  6871.         cJson=''
  6872.         cError=''
  6873.  
  6874.  
  6875.         *
  6876.         * Genera el codigo cJson para parametro que se manda
  6877.         *
  6878.         function encode(xExpr)
  6879.         local cTipo
  6880.                 * Cuando se manda una arreglo,
  6881.                 if type('ALen(xExpr)')=='N'
  6882.                         cTipo = 'A'
  6883.                 Else
  6884.                         cTipo = VarType(xExpr)
  6885.                 Endif
  6886.                
  6887.                 Do Case
  6888.                 Case cTipo=='D'
  6889.                         return '"'+dtos(xExpr)+'"'
  6890.                 Case cTipo=='N'
  6891.                         return Transform(xExpr)
  6892.                 Case cTipo=='L'
  6893.                         return iif(xExpr,'true','false')
  6894.                 Case cTipo=='X'
  6895.                         return 'null'
  6896.                 Case cTipo=='C'
  6897.                         xExpr = allt(xExpr)
  6898.                         xExpr = StrTran(xExpr, '\', '\\' )
  6899.                         xExpr = StrTran(xExpr, '/', '\/' )
  6900.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  6901.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  6902.                         xExpr = StrTran(xExpr, '"', '\"' )
  6903.                         return '"'+xExpr+'"'
  6904.  
  6905.                 case cTipo=='O'
  6906.                         local cProp, cJsonValue, cRetVal, aProp[1]
  6907.                         =AMembers(aProp,xExpr)
  6908.                         cRetVal = ''
  6909.                         for each cProp in aProp
  6910.                                 *? cProp
  6911.                                 *? cRetVal
  6912.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  6913.                                         * algunas propiedades pueden no estar definidas
  6914.                                         * como: activecontrol, parent, etc
  6915.                                         loop
  6916.                                 endif
  6917.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  6918.                                         *
  6919.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  6920.                                         *
  6921.                                         Local i,nTotElem
  6922.                                         cJsonValue = ''
  6923.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  6924.                                         For i=1 to nTotElem
  6925.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  6926.                                         Next
  6927.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  6928.                                 else
  6929.                                         *
  6930.                                         * es otro tipo de dato normal C, N, L
  6931.                                         *
  6932.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  6933.                                 endif
  6934.                                 if left(cProp,1)=='_'
  6935.                                         cProp = substr(cProp,2)
  6936.                                 endif
  6937.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  6938.                         next
  6939.                         return '{' + substr(cRetVal,2) + '}'
  6940.  
  6941.                 case cTipo=='A'
  6942.                         local valor, cRetVal
  6943.                         cRetVal = ''   
  6944.                         for each valor in xExpr
  6945.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  6946.                         next
  6947.                         return  '[' + substr(cRetVal,2) + ']'
  6948.                        
  6949.                 endcase
  6950.  
  6951.         return ''
  6952.  
  6953.  
  6954.  
  6955.  
  6956.  
  6957.         *
  6958.         * regresa un elemento representado por la cadena json que se manda
  6959.         *
  6960.        
  6961.         function decode(cJson)
  6962.         local retValue
  6963.                 cJson = StrTran(cJson,chr(9),'')
  6964.                 cJson = StrTran(cJson,chr(10),'')
  6965.                 cJson = StrTran(cJson,chr(13),'')
  6966.                 cJson = this.fixUnicode(cJson)
  6967.                 this.nPos  = 1
  6968.                 this.cJson = cJson
  6969.                 this.nLen  = len(cJson)
  6970.                 this.cError = ''
  6971.                 retValue = this.parsevalue()
  6972.                 if not empty(this.cError)
  6973.                         return null
  6974.                 endif
  6975.                 if this.getToken()<>null
  6976.                         this.setError('Junk at the end of JSON input')
  6977.                         return null
  6978.                 endif
  6979.         return retValue
  6980.                
  6981.        
  6982.         function parseValue()
  6983.         local token
  6984.                 token = this.getToken()
  6985.                 if token==null
  6986.                         this.setError('Nothing to parse')
  6987.                         return null
  6988.                 endif
  6989.                 do case
  6990.                 case token=='"'
  6991.                         return this.parseString()
  6992.                 case isdigit(token) or token=='-'
  6993.                         return this.parseNumber()
  6994.                 case token=='n'
  6995.                         return this.expectedKeyword('null',null)
  6996.                 case token=='f'
  6997.                         return this.expectedKeyword('false',.f.)
  6998.                 case token=='t'
  6999.                         return this.expectedKeyword('true',.t.)
  7000.                 case token=='{'
  7001.                         return this.parseObject()
  7002.                 case token=='['
  7003.                         return this.parseArray()
  7004.                 otherwise
  7005.                         this.setError('Unexpected token')
  7006.                 endcase
  7007.         return
  7008.                
  7009.        
  7010.         function expectedKeyword(cWord,eValue)
  7011.                 for i=1 to len(cWord)
  7012.                         cChar = this.getChar()
  7013.                         if cChar <> substr(cWord,i,1)
  7014.                                 this.setError("Expected keyword '" + cWord + "'")
  7015.                                 return ''
  7016.                         endif
  7017.                         this.nPos = this.nPos + 1
  7018.                 next
  7019.         return eValue
  7020.        
  7021.  
  7022.         function parseObject()
  7023.         local retval, cPropName, xValue
  7024.                 retval = createObject('myObj')
  7025.                 this.nPos = this.nPos + 1 && Eat {
  7026.                 if this.getToken()<>'}'
  7027.                         do while .t.
  7028.                                 cPropName = this.parseString()
  7029.                                 if not empty(this.cError)
  7030.                                         return null
  7031.                                 endif
  7032.                                 if this.getToken()<>':'
  7033.                                         this.setError("Expected ':' when parsing object")
  7034.                                         return null
  7035.                                 endif
  7036.                                 this.nPos = this.nPos + 1
  7037.                                 xValue = this.parseValue()
  7038.                                 if not empty(this.cError)
  7039.                                         return null
  7040.                                 endif                          
  7041.                                 ** Debug ? cPropName, type('xValue')
  7042.                                 retval.set(cPropName, xValue)
  7043.                                 if this.getToken()<>','
  7044.                                         exit
  7045.                                 endif
  7046.                                 this.nPos = this.nPos + 1
  7047.                         enddo
  7048.                 endif
  7049.                 if this.getToken()<>'}'
  7050.                         this.setError("Expected '}' at the end of object")
  7051.                         return null
  7052.                 endif
  7053.                 this.nPos = this.nPos + 1
  7054.         return retval
  7055.  
  7056.  
  7057.         function parseArray()
  7058.         local retVal, xValue
  7059.                 retval = createObject('MyArray')
  7060.                 this.nPos = this.nPos + 1       && Eat [
  7061.                 if this.getToken() <> ']'
  7062.                         do while .t.
  7063.                                 xValue = this.parseValue()
  7064.                                 if not empty(this.cError)
  7065.                                         return null
  7066.                                 endif
  7067.                                 retval.add( xValue )
  7068.                                 if this.getToken()<>','
  7069.                                         exit
  7070.                                 endif
  7071.                                 this.nPos = this.nPos + 1
  7072.                         enddo
  7073.                         if this.getToken() <> ']'
  7074.                                 this.setError('Expected ] at the end of array')
  7075.                                 return null
  7076.                         endif
  7077.                 endif
  7078.                 this.nPos = this.nPos + 1
  7079.         return retval
  7080.        
  7081.  
  7082.         function parseString()
  7083.         local cRetVal, c
  7084.                 if this.getToken()<>'"'
  7085.                         this.setError('Expected "')
  7086.                         return ''
  7087.                 endif
  7088.                 this.nPos = this.nPos + 1       && Eat "
  7089.                 cRetVal = ''
  7090.                 do while .t.
  7091.                         c = this.getChar()
  7092.                         if c==''
  7093.                                 return ''
  7094.                         endif
  7095.                         if c == '"'
  7096.                                 this.nPos = this.nPos + 1
  7097.                                 exit
  7098.                         endif
  7099.                         if c == '\'
  7100.                                 this.nPos = this.nPos + 1
  7101.                                 if (this.nPos>this.nLen)
  7102.                                         this.setError('\\ at the end of input')
  7103.                                         return ''
  7104.                                 endif
  7105.                                 c = this.getChar()
  7106.                                 if c==''
  7107.                                         return ''
  7108.                                 endif
  7109.                                 do case
  7110.                                 case c=='"'
  7111.                                         c='"'
  7112.                                 case c=='\'
  7113.                                         c='\'
  7114.                                 case c=='/'
  7115.                                         c='/'
  7116.                                 case c=='b'
  7117.                                         c=chr(8)
  7118.                                 case c=='t'
  7119.                                         c=chr(9)
  7120.                                 case c=='n'
  7121.                                         c=chr(10)
  7122.                                 case c=='f'
  7123.                                         c=chr(12)
  7124.                                 case c=='r'
  7125.                                         c=chr(13)
  7126.                                 otherwise
  7127.                                         ******* FALTAN LOS UNICODE
  7128.                                         this.setError('Invalid escape sequence in string literal')
  7129.                                         return ''
  7130.                                 endcase
  7131.                         endif
  7132.                         cRetVal = cRetVal + c
  7133.                         this.nPos = this.nPos + 1
  7134.                 enddo
  7135.         return cRetVal
  7136.                                        
  7137.  
  7138.         **** Pendiente numeros con E
  7139.         function parseNumber()
  7140.         local nStartPos,c, isInt, cNumero
  7141.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  7142.                         this.setError('Expected number literal')
  7143.                         return 0
  7144.                 endif
  7145.                 nStartPos = this.nPos
  7146.                 c = this.getChar()
  7147.                 if c == '-'
  7148.                         c = this.nextChar()
  7149.                 endif
  7150.                 if c == '0'
  7151.                         c = this.nextChar()
  7152.                 else
  7153.                         if isdigit(c)
  7154.                                 c = this.nextChar()
  7155.                                 do while isdigit(c)
  7156.                                         c = this.nextChar()
  7157.                                 enddo
  7158.                         else
  7159.                                 this.setError('Expected digit when parsing number')
  7160.                                 return 0
  7161.                         endif
  7162.                 endif
  7163.                
  7164.                 isInt = .t.
  7165.                 if c=='.'
  7166.                         c = this.nextChar()
  7167.                         if isdigit(c)
  7168.                                 c = this.nextChar()
  7169.                                 isInt = .f.
  7170.                                 do while isDigit(c)
  7171.                                         c = this.nextChar()
  7172.                                 enddo
  7173.                         else
  7174.                                 this.setError('Expected digit following dot comma')
  7175.                                 return 0
  7176.                         endif
  7177.                 endif
  7178.                
  7179.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  7180.         return val(cNumero)
  7181.  
  7182.  
  7183.  
  7184.         function getToken()
  7185.         local char1
  7186.                 do while .t.
  7187.                         if this.nPos > this.nLen
  7188.                                 return null
  7189.                         endif
  7190.                         char1 = substr(this.cJson, this.nPos, 1)
  7191.                         if char1==' '
  7192.                                 this.nPos = this.nPos + 1
  7193.                                 loop
  7194.                         endif
  7195.                         return char1
  7196.                 enddo
  7197.         return
  7198.        
  7199.                
  7200.                
  7201.         function getChar()
  7202.                 if this.nPos > this.nLen
  7203.                         this.setError('Unexpected end of JSON stream')
  7204.                         return ''
  7205.                 endif
  7206.         return substr(this.cJson, this.nPos, 1)
  7207.        
  7208.         function nextChar()
  7209.                 this.nPos = this.nPos + 1
  7210.                 if this.nPos > this.nLen
  7211.                         return ''
  7212.                 endif
  7213.         return substr(this.cJson, this.nPos, 1)
  7214.        
  7215.         function setError(cMsg)
  7216.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  7217.         return
  7218.  
  7219.  
  7220.         function fixUnicode(cStr)
  7221.                 cStr = StrTran(cStr,'\u00e1','á')
  7222.                 cStr = StrTran(cStr,'\u00e9','é')
  7223.                 cStr = StrTran(cStr,'\u00ed','í')
  7224.                 cStr = StrTran(cStr,'\u00f3','ó')
  7225.                 cStr = StrTran(cStr,'\u00fa','ú')
  7226.                 cStr = StrTran(cStr,'\u00c1','Á')
  7227.                 cStr = StrTran(cStr,'\u00c9','É')
  7228.                 cStr = StrTran(cStr,'\u00cd','Í')
  7229.                 cStr = StrTran(cStr,'\u00d3','Ó')
  7230.                 cStr = StrTran(cStr,'\u00da','Ú')
  7231.                 cStr = StrTran(cStr,'\u00f1','ń')
  7232.                 cStr = StrTran(cStr,'\u00d1','Ń')
  7233.         return cStr
  7234.  
  7235.  
  7236.  
  7237. enddefine
  7238.  
  7239.  
  7240.  
  7241.  
  7242.  
  7243. *
  7244. * class used to return an array
  7245. *
  7246. define class myArray as custom
  7247.         nSize = 0
  7248.         dimension array[1]
  7249.  
  7250.         function add(xExpr)
  7251.                 this.nSize = this.nSize + 1
  7252.                 dimension this.array[this.nSize]
  7253.                 this.array[this.nSize] = xExpr
  7254.         return
  7255.  
  7256.         function get(n)
  7257.         return this.array[n]
  7258.  
  7259. enddefine
  7260.  
  7261.  
  7262.  
  7263. *
  7264. * class used to simulate an object
  7265. * all properties are prefixed with 'prop' to permit property names like: error, init
  7266. * that already exists like vfp methods
  7267. *
  7268. define class myObj as custom
  7269. Hidden ;
  7270.         ClassLibrary,Comment, ;
  7271.         BaseClass,ControlCount, ;
  7272.         Controls,Objects,Object,;
  7273.         Height,HelpContextID,Left,Name, ;
  7274.         Parent,ParentClass,Picture, ;
  7275.         Tag,Top,WhatsThisHelpID,Width
  7276.                
  7277.         function set(cPropName, xValue)
  7278.                 cPropName = '_'+cPropName
  7279.                 if type('this.'+cPropName)=='U'
  7280.                         this.addProperty(cPropName,xValue)
  7281.                 else
  7282.                         local cmd
  7283.                         cmd = 'this.'+cPropName+'=xValue'
  7284.                         &cmd
  7285.                 endif
  7286.         return
  7287.        
  7288.         procedure get (cPropName)
  7289.                 cPropName = '_'+cPropName
  7290.                 If type('this.'+cPropName)=='U'
  7291.                         return ''
  7292.                 Else
  7293.                         local cmd
  7294.                         cmd = 'return this.'+cPropName
  7295.                         &cmd
  7296.                 endif
  7297.         return ''
  7298. enddefine
  7299.  
  7300.  
  7301.  
  7302.  
  7303.  
  7304. function testJsonClass
  7305.         clear
  7306.         set decimal to 10
  7307.         oJson = newObject('json')
  7308.        
  7309.        
  7310.         ? 'Test Basic Types'
  7311.         ? '----------------'
  7312.         ? oJson.decode('null')
  7313.         ? oJson.decode('true')
  7314.         ? oJson.decode('false')
  7315.         ?
  7316.         ? oJson.decode('791123')
  7317.         ? oJson.decode('791123.45')
  7318.         ? oJson.decode('791123.45.')
  7319.         ? oJson.decode('"nacho gtz"')
  7320.         if not empty(oJson.cError)
  7321.                 ? oJson.cError
  7322.                 return
  7323.         endif
  7324.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  7325.         if not empty(oJson.cError)
  7326.                 ? oJson.cError
  7327.                 return
  7328.         endif
  7329.        
  7330.         ? 'Test Array'
  7331.         ? '----------'
  7332.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  7333.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  7334.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  7335.         nombres = arr.get(1)
  7336.         edades  = arr.get(2)
  7337.         ? nombres.get(1), edades.get(1)
  7338.         ? nombres.get(2), edades.get(2)
  7339.         ? nombres.get(3), edades.get(3)
  7340.         ?
  7341.         ? 'Test Object'
  7342.         ? '-----------'
  7343.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  7344.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  7345.         ? obj._Nombre, obj._Edad, obj._IsGood
  7346.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  7347.         ? obj.get('jsonrpc'), obj._jsonrpc
  7348.         ? obj.get('id'), obj._id
  7349.         ? obj.get('method'), obj._method
  7350.         ? obj._Params.array[1], obj._Params.get(1)
  7351.  
  7352.         ?
  7353.         ? 'Test nested object'
  7354.         ? '------------------'
  7355.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  7356.         obj = oJson.decode(cJson)
  7357.         if not empty(oJson.cError)
  7358.                 ? oJson.cError
  7359.                 return
  7360.         endif
  7361.         ? cJson
  7362.         ? 'method -->',obj._method
  7363.         ? 'usrkey -->',obj._params._data._usrkey
  7364.         ? 'sendto -->',obj._params._data._sendto
  7365.         ? 'name  --->',obj._params._data._name
  7366.         ? 'expires ->',obj._params._data._expires
  7367.  
  7368.         ?
  7369.         ? 'Test empty object'
  7370.         ? '-----------------'
  7371.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  7372.         obj = oJson.decode(cJson)
  7373.         if not empty(oJson.cError)     
  7374.                 ? oJson.cError
  7375.                 return
  7376.         endif
  7377.         ? cJson
  7378.         ? 'result -->',obj._result, obj.get('result')
  7379.         oError = obj.get('error')
  7380.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  7381.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  7382.         ? 'id  ----->',obj._id, obj.get('id')
  7383.         ?  type("oError._code")
  7384.  
  7385.         ?
  7386.         ? 'Probar decode-enconde-decode-encode'
  7387.         ? '------------------------------------'
  7388.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  7389.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  7390.         ? cJson
  7391.         oSmtp = json_decode(cJson)
  7392.         cJson =  json_encode(oSmtp)
  7393.         ? cJson
  7394.         oSmtp = json_decode(cJson)
  7395.         cJson =  json_encode(oSmtp)
  7396.         ? cJson
  7397.  
  7398.         * Probar falla
  7399.         ?
  7400.         ? 'Probar una falla en el json'
  7401.         ? '---------------------------'
  7402.         cJson = ' {"server":"", "user":"", "password":"" ,'
  7403.         oSmtp = json_decode(cJson)
  7404.         if not empty(json_getErrorMsg())
  7405.                 ? json_getErrorMsg()
  7406.         endif
  7407.  
  7408.         ?
  7409.         ? 'Pruebas Finalizadas'
  7410. retur
  7411. *
  7412. * Gracias a Google por el codigo de Json de Dart
  7413. * Thanks Google for the code in Json Dart
  7414. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  7415. *
  7416. *
  7417. * Codificar y Decodificar JSON
  7418. *
  7419. * Puedes usar las funciones:
  7420. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  7421. *               json_decode(cJson)  te regresa el objeto representado en cJson
  7422. *
  7423. * Tambien puedes usar directamente la clase:
  7424. *
  7425. *       oJson = newobject('json','json.prg')
  7426. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  7427. *       ? oJson.encode(oCliente)
  7428. *       ? oCliente.get('nombre')
  7429. *       ? oCliente.get('apellido')
  7430. *
  7431. *
  7432. * VFPJSON  Encode and Decode JSON for VFP
  7433. * Examples:
  7434. *       oJson = newobject('json','json.prg')
  7435. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  7436. *       ? oJson.encode(oCustomer)
  7437. *       ? oCustomer.get('name')
  7438. *       ? oCustomer.get('lastname')
  7439. *
  7440. *
  7441. lRunTest = .f.
  7442. if lRunTest
  7443.         testJsonClass()
  7444. endif
  7445. return
  7446.  
  7447.  
  7448. function json_encode(xExpr)
  7449.         if vartype(_json)<>'O'
  7450.                 public _json
  7451.                 _json = newobject('json')
  7452.         endif
  7453. return _json.encode(@xExpr)
  7454.  
  7455.  
  7456. function json_decode(cJson)
  7457. local retval
  7458.         if vartype(_json)<>'O'
  7459.                 public _json
  7460.                 _json = newobject('json')
  7461.         endif
  7462.         retval = _json.decode(cJson)
  7463.         if not empty(_json.cError)
  7464.                 return null
  7465.         endif
  7466. return retval
  7467.  
  7468. function json_getErrorMsg()
  7469. return _json.cError
  7470.        
  7471.  
  7472.  
  7473. *
  7474. * json class
  7475. *
  7476. *
  7477. define class json as custom
  7478.  
  7479.  
  7480.         nPos=0
  7481.         nLen=0
  7482.         cJson=''
  7483.         cError=''
  7484.  
  7485.  
  7486.         *
  7487.         * Genera el codigo cJson para parametro que se manda
  7488.         *
  7489.         function encode(xExpr)
  7490.         local cTipo
  7491.                 * Cuando se manda una arreglo,
  7492.                 if type('ALen(xExpr)')=='N'
  7493.                         cTipo = 'A'
  7494.                 Else
  7495.                         cTipo = VarType(xExpr)
  7496.                 Endif
  7497.                
  7498.                 Do Case
  7499.                 Case cTipo=='D'
  7500.                         return '"'+dtos(xExpr)+'"'
  7501.                 Case cTipo=='N'
  7502.                         return Transform(xExpr)
  7503.                 Case cTipo=='L'
  7504.                         return iif(xExpr,'true','false')
  7505.                 Case cTipo=='X'
  7506.                         return 'null'
  7507.                 Case cTipo=='C'
  7508.                         xExpr = allt(xExpr)
  7509.                         xExpr = StrTran(xExpr, '\', '\\' )
  7510.                         xExpr = StrTran(xExpr, '/', '\/' )
  7511.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  7512.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  7513.                         xExpr = StrTran(xExpr, '"', '\"' )
  7514.                         return '"'+xExpr+'"'
  7515.  
  7516.                 case cTipo=='O'
  7517.                         local cProp, cJsonValue, cRetVal, aProp[1]
  7518.                         =AMembers(aProp,xExpr)
  7519.                         cRetVal = ''
  7520.                         for each cProp in aProp
  7521.                                 *? cProp
  7522.                                 *? cRetVal
  7523.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  7524.                                         * algunas propiedades pueden no estar definidas
  7525.                                         * como: activecontrol, parent, etc
  7526.                                         loop
  7527.                                 endif
  7528.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  7529.                                         *
  7530.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  7531.                                         *
  7532.                                         Local i,nTotElem
  7533.                                         cJsonValue = ''
  7534.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  7535.                                         For i=1 to nTotElem
  7536.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  7537.                                         Next
  7538.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  7539.                                 else
  7540.                                         *
  7541.                                         * es otro tipo de dato normal C, N, L
  7542.                                         *
  7543.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  7544.                                 endif
  7545.                                 if left(cProp,1)=='_'
  7546.                                         cProp = substr(cProp,2)
  7547.                                 endif
  7548.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  7549.                         next
  7550.                         return '{' + substr(cRetVal,2) + '}'
  7551.  
  7552.                 case cTipo=='A'
  7553.                         local valor, cRetVal
  7554.                         cRetVal = ''   
  7555.                         for each valor in xExpr
  7556.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  7557.                         next
  7558.                         return  '[' + substr(cRetVal,2) + ']'
  7559.                        
  7560.                 endcase
  7561.  
  7562.         return ''
  7563.  
  7564.  
  7565.  
  7566.  
  7567.  
  7568.         *
  7569.         * regresa un elemento representado por la cadena json que se manda
  7570.         *
  7571.        
  7572.         function decode(cJson)
  7573.         local retValue
  7574.                 cJson = StrTran(cJson,chr(9),'')
  7575.                 cJson = StrTran(cJson,chr(10),'')
  7576.                 cJson = StrTran(cJson,chr(13),'')
  7577.                 cJson = this.fixUnicode(cJson)
  7578.                 this.nPos  = 1
  7579.                 this.cJson = cJson
  7580.                 this.nLen  = len(cJson)
  7581.                 this.cError = ''
  7582.                 retValue = this.parsevalue()
  7583.                 if not empty(this.cError)
  7584.                         return null
  7585.                 endif
  7586.                 if this.getToken()<>null
  7587.                         this.setError('Junk at the end of JSON input')
  7588.                         return null
  7589.                 endif
  7590.         return retValue
  7591.                
  7592.        
  7593.         function parseValue()
  7594.         local token
  7595.                 token = this.getToken()
  7596.                 if token==null
  7597.                         this.setError('Nothing to parse')
  7598.                         return null
  7599.                 endif
  7600.                 do case
  7601.                 case token=='"'
  7602.                         return this.parseString()
  7603.                 case isdigit(token) or token=='-'
  7604.                         return this.parseNumber()
  7605.                 case token=='n'
  7606.                         return this.expectedKeyword('null',null)
  7607.                 case token=='f'
  7608.                         return this.expectedKeyword('false',.f.)
  7609.                 case token=='t'
  7610.                         return this.expectedKeyword('true',.t.)
  7611.                 case token=='{'
  7612.                         return this.parseObject()
  7613.                 case token=='['
  7614.                         return this.parseArray()
  7615.                 otherwise
  7616.                         this.setError('Unexpected token')
  7617.                 endcase
  7618.         return
  7619.                
  7620.        
  7621.         function expectedKeyword(cWord,eValue)
  7622.                 for i=1 to len(cWord)
  7623.                         cChar = this.getChar()
  7624.                         if cChar <> substr(cWord,i,1)
  7625.                                 this.setError("Expected keyword '" + cWord + "'")
  7626.                                 return ''
  7627.                         endif
  7628.                         this.nPos = this.nPos + 1
  7629.                 next
  7630.         return eValue
  7631.        
  7632.  
  7633.         function parseObject()
  7634.         local retval, cPropName, xValue
  7635.                 retval = createObject('myObj')
  7636.                 this.nPos = this.nPos + 1 && Eat {
  7637.                 if this.getToken()<>'}'
  7638.                         do while .t.
  7639.                                 cPropName = this.parseString()
  7640.                                 if not empty(this.cError)
  7641.                                         return null
  7642.                                 endif
  7643.                                 if this.getToken()<>':'
  7644.                                         this.setError("Expected ':' when parsing object")
  7645.                                         return null
  7646.                                 endif
  7647.                                 this.nPos = this.nPos + 1
  7648.                                 xValue = this.parseValue()
  7649.                                 if not empty(this.cError)
  7650.                                         return null
  7651.                                 endif                          
  7652.                                 ** Debug ? cPropName, type('xValue')
  7653.                                 retval.set(cPropName, xValue)
  7654.                                 if this.getToken()<>','
  7655.                                         exit
  7656.                                 endif
  7657.                                 this.nPos = this.nPos + 1
  7658.                         enddo
  7659.                 endif
  7660.                 if this.getToken()<>'}'
  7661.                         this.setError("Expected '}' at the end of object")
  7662.                         return null
  7663.                 endif
  7664.                 this.nPos = this.nPos + 1
  7665.         return retval
  7666.  
  7667.  
  7668.         function parseArray()
  7669.         local retVal, xValue
  7670.                 retval = createObject('MyArray')
  7671.                 this.nPos = this.nPos + 1       && Eat [
  7672.                 if this.getToken() <> ']'
  7673.                         do while .t.
  7674.                                 xValue = this.parseValue()
  7675.                                 if not empty(this.cError)
  7676.                                         return null
  7677.                                 endif
  7678.                                 retval.add( xValue )
  7679.                                 if this.getToken()<>','
  7680.                                         exit
  7681.                                 endif
  7682.                                 this.nPos = this.nPos + 1
  7683.                         enddo
  7684.                         if this.getToken() <> ']'
  7685.                                 this.setError('Expected ] at the end of array')
  7686.                                 return null
  7687.                         endif
  7688.                 endif
  7689.                 this.nPos = this.nPos + 1
  7690.         return retval
  7691.        
  7692.  
  7693.         function parseString()
  7694.         local cRetVal, c
  7695.                 if this.getToken()<>'"'
  7696.                         this.setError('Expected "')
  7697.                         return ''
  7698.                 endif
  7699.                 this.nPos = this.nPos + 1       && Eat "
  7700.                 cRetVal = ''
  7701.                 do while .t.
  7702.                         c = this.getChar()
  7703.                         if c==''
  7704.                                 return ''
  7705.                         endif
  7706.                         if c == '"'
  7707.                                 this.nPos = this.nPos + 1
  7708.                                 exit
  7709.                         endif
  7710.                         if c == '\'
  7711.                                 this.nPos = this.nPos + 1
  7712.                                 if (this.nPos>this.nLen)
  7713.                                         this.setError('\\ at the end of input')
  7714.                                         return ''
  7715.                                 endif
  7716.                                 c = this.getChar()
  7717.                                 if c==''
  7718.                                         return ''
  7719.                                 endif
  7720.                                 do case
  7721.                                 case c=='"'
  7722.                                         c='"'
  7723.                                 case c=='\'
  7724.                                         c='\'
  7725.                                 case c=='/'
  7726.                                         c='/'
  7727.                                 case c=='b'
  7728.                                         c=chr(8)
  7729.                                 case c=='t'
  7730.                                         c=chr(9)
  7731.                                 case c=='n'
  7732.                                         c=chr(10)
  7733.                                 case c=='f'
  7734.                                         c=chr(12)
  7735.                                 case c=='r'
  7736.                                         c=chr(13)
  7737.                                 otherwise
  7738.                                         ******* FALTAN LOS UNICODE
  7739.                                         this.setError('Invalid escape sequence in string literal')
  7740.                                         return ''
  7741.                                 endcase
  7742.                         endif
  7743.                         cRetVal = cRetVal + c
  7744.                         this.nPos = this.nPos + 1
  7745.                 enddo
  7746.         return cRetVal
  7747.                                        
  7748.  
  7749.         **** Pendiente numeros con E
  7750.         function parseNumber()
  7751.         local nStartPos,c, isInt, cNumero
  7752.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  7753.                         this.setError('Expected number literal')
  7754.                         return 0
  7755.                 endif
  7756.                 nStartPos = this.nPos
  7757.                 c = this.getChar()
  7758.                 if c == '-'
  7759.                         c = this.nextChar()
  7760.                 endif
  7761.                 if c == '0'
  7762.                         c = this.nextChar()
  7763.                 else
  7764.                         if isdigit(c)
  7765.                                 c = this.nextChar()
  7766.                                 do while isdigit(c)
  7767.                                         c = this.nextChar()
  7768.                                 enddo
  7769.                         else
  7770.                                 this.setError('Expected digit when parsing number')
  7771.                                 return 0
  7772.                         endif
  7773.                 endif
  7774.                
  7775.                 isInt = .t.
  7776.                 if c=='.'
  7777.                         c = this.nextChar()
  7778.                         if isdigit(c)
  7779.                                 c = this.nextChar()
  7780.                                 isInt = .f.
  7781.                                 do while isDigit(c)
  7782.                                         c = this.nextChar()
  7783.                                 enddo
  7784.                         else
  7785.                                 this.setError('Expected digit following dot comma')
  7786.                                 return 0
  7787.                         endif
  7788.                 endif
  7789.                
  7790.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  7791.         return val(cNumero)
  7792.  
  7793.  
  7794.  
  7795.         function getToken()
  7796.         local char1
  7797.                 do while .t.
  7798.                         if this.nPos > this.nLen
  7799.                                 return null
  7800.                         endif
  7801.                         char1 = substr(this.cJson, this.nPos, 1)
  7802.                         if char1==' '
  7803.                                 this.nPos = this.nPos + 1
  7804.                                 loop
  7805.                         endif
  7806.                         return char1
  7807.                 enddo
  7808.         return
  7809.        
  7810.                
  7811.                
  7812.         function getChar()
  7813.                 if this.nPos > this.nLen
  7814.                         this.setError('Unexpected end of JSON stream')
  7815.                         return ''
  7816.                 endif
  7817.         return substr(this.cJson, this.nPos, 1)
  7818.        
  7819.         function nextChar()
  7820.                 this.nPos = this.nPos + 1
  7821.                 if this.nPos > this.nLen
  7822.                         return ''
  7823.                 endif
  7824.         return substr(this.cJson, this.nPos, 1)
  7825.        
  7826.         function setError(cMsg)
  7827.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  7828.         return
  7829.  
  7830.  
  7831.         function fixUnicode(cStr)
  7832.                 cStr = StrTran(cStr,'\u00e1','á')
  7833.                 cStr = StrTran(cStr,'\u00e9','é')
  7834.                 cStr = StrTran(cStr,'\u00ed','í')
  7835.                 cStr = StrTran(cStr,'\u00f3','ó')
  7836.                 cStr = StrTran(cStr,'\u00fa','ú')
  7837.                 cStr = StrTran(cStr,'\u00c1','Á')
  7838.                 cStr = StrTran(cStr,'\u00c9','É')
  7839.                 cStr = StrTran(cStr,'\u00cd','Í')
  7840.                 cStr = StrTran(cStr,'\u00d3','Ó')
  7841.                 cStr = StrTran(cStr,'\u00da','Ú')
  7842.                 cStr = StrTran(cStr,'\u00f1','ń')
  7843.                 cStr = StrTran(cStr,'\u00d1','Ń')
  7844.         return cStr
  7845.  
  7846.  
  7847.  
  7848. enddefine
  7849.  
  7850.  
  7851.  
  7852.  
  7853.  
  7854. *
  7855. * class used to return an array
  7856. *
  7857. define class myArray as custom
  7858.         nSize = 0
  7859.         dimension array[1]
  7860.  
  7861.         function add(xExpr)
  7862.                 this.nSize = this.nSize + 1
  7863.                 dimension this.array[this.nSize]
  7864.                 this.array[this.nSize] = xExpr
  7865.         return
  7866.  
  7867.         function get(n)
  7868.         return this.array[n]
  7869.  
  7870. enddefine
  7871.  
  7872.  
  7873.  
  7874. *
  7875. * class used to simulate an object
  7876. * all properties are prefixed with 'prop' to permit property names like: error, init
  7877. * that already exists like vfp methods
  7878. *
  7879. define class myObj as custom
  7880. Hidden ;
  7881.         ClassLibrary,Comment, ;
  7882.         BaseClass,ControlCount, ;
  7883.         Controls,Objects,Object,;
  7884.         Height,HelpContextID,Left,Name, ;
  7885.         Parent,ParentClass,Picture, ;
  7886.         Tag,Top,WhatsThisHelpID,Width
  7887.                
  7888.         function set(cPropName, xValue)
  7889.                 cPropName = '_'+cPropName
  7890.                 if type('this.'+cPropName)=='U'
  7891.                         this.addProperty(cPropName,xValue)
  7892.                 else
  7893.                         local cmd
  7894.                         cmd = 'this.'+cPropName+'=xValue'
  7895.                         &cmd
  7896.                 endif
  7897.         return
  7898.        
  7899.         procedure get (cPropName)
  7900.                 cPropName = '_'+cPropName
  7901.                 If type('this.'+cPropName)=='U'
  7902.                         return ''
  7903.                 Else
  7904.                         local cmd
  7905.                         cmd = 'return this.'+cPropName
  7906.                         &cmd
  7907.                 endif
  7908.         return ''
  7909. enddefine
  7910.  
  7911.  
  7912.  
  7913.  
  7914.  
  7915. function testJsonClass
  7916.         clear
  7917.         set decimal to 10
  7918.         oJson = newObject('json')
  7919.        
  7920.        
  7921.         ? 'Test Basic Types'
  7922.         ? '----------------'
  7923.         ? oJson.decode('null')
  7924.         ? oJson.decode('true')
  7925.         ? oJson.decode('false')
  7926.         ?
  7927.         ? oJson.decode('791123')
  7928.         ? oJson.decode('791123.45')
  7929.         ? oJson.decode('791123.45.')
  7930.         ? oJson.decode('"nacho gtz"')
  7931.         if not empty(oJson.cError)
  7932.                 ? oJson.cError
  7933.                 return
  7934.         endif
  7935.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  7936.         if not empty(oJson.cError)
  7937.                 ? oJson.cError
  7938.                 return
  7939.         endif
  7940.        
  7941.         ? 'Test Array'
  7942.         ? '----------'
  7943.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  7944.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  7945.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  7946.         nombres = arr.get(1)
  7947.         edades  = arr.get(2)
  7948.         ? nombres.get(1), edades.get(1)
  7949.         ? nombres.get(2), edades.get(2)
  7950.         ? nombres.get(3), edades.get(3)
  7951.         ?
  7952.         ? 'Test Object'
  7953.         ? '-----------'
  7954.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  7955.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  7956.         ? obj._Nombre, obj._Edad, obj._IsGood
  7957.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  7958.         ? obj.get('jsonrpc'), obj._jsonrpc
  7959.         ? obj.get('id'), obj._id
  7960.         ? obj.get('method'), obj._method
  7961.         ? obj._Params.array[1], obj._Params.get(1)
  7962.  
  7963.         ?
  7964.         ? 'Test nested object'
  7965.         ? '------------------'
  7966.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  7967.         obj = oJson.decode(cJson)
  7968.         if not empty(oJson.cError)
  7969.                 ? oJson.cError
  7970.                 return
  7971.         endif
  7972.         ? cJson
  7973.         ? 'method -->',obj._method
  7974.         ? 'usrkey -->',obj._params._data._usrkey
  7975.         ? 'sendto -->',obj._params._data._sendto
  7976.         ? 'name  --->',obj._params._data._name
  7977.         ? 'expires ->',obj._params._data._expires
  7978.  
  7979.         ?
  7980.         ? 'Test empty object'
  7981.         ? '-----------------'
  7982.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  7983.         obj = oJson.decode(cJson)
  7984.         if not empty(oJson.cError)     
  7985.                 ? oJson.cError
  7986.                 return
  7987.         endif
  7988.         ? cJson
  7989.         ? 'result -->',obj._result, obj.get('result')
  7990.         oError = obj.get('error')
  7991.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  7992.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  7993.         ? 'id  ----->',obj._id, obj.get('id')
  7994.         ?  type("oError._code")
  7995.  
  7996.         ?
  7997.         ? 'Probar decode-enconde-decode-encode'
  7998.         ? '------------------------------------'
  7999.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  8000.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  8001.         ? cJson
  8002.         oSmtp = json_decode(cJson)
  8003.         cJson =  json_encode(oSmtp)
  8004.         ? cJson
  8005.         oSmtp = json_decode(cJson)
  8006.         cJson =  json_encode(oSmtp)
  8007.         ? cJson
  8008.  
  8009.         * Probar falla
  8010.         ?
  8011.         ? 'Probar una falla en el json'
  8012.         ? '---------------------------'
  8013.         cJson = ' {"server":"", "user":"", "password":"" ,'
  8014.         oSmtp = json_decode(cJson)
  8015.         if not empty(json_getErrorMsg())
  8016.                 ? json_getErrorMsg()
  8017.         endif
  8018.  
  8019.         ?
  8020.         ? 'Pruebas Finalizadas'
  8021. retur
  8022. * Gracias a Google por el codigo de Json de Dart
  8023. * Thanks Google for the code in Json Dart
  8024. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  8025. *
  8026. *
  8027. * Codificar y Decodificar JSON
  8028. *
  8029. * Puedes usar las funciones:
  8030. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  8031. *               json_decode(cJson)  te regresa el objeto representado en cJson
  8032. *
  8033. * Tambien puedes usar directamente la clase:
  8034. *
  8035. *       oJson = newobject('json','json.prg')
  8036. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  8037. *       ? oJson.encode(oCliente)
  8038. *       ? oCliente.get('nombre')
  8039. *       ? oCliente.get('apellido')
  8040. *
  8041. *
  8042. * VFPJSON  Encode and Decode JSON for VFP
  8043. * Examples:
  8044. *       oJson = newobject('json','json.prg')
  8045. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  8046. *       ? oJson.encode(oCustomer)
  8047. *       ? oCustomer.get('name')
  8048. *       ? oCustomer.get('lastname')
  8049. *
  8050. *
  8051. lRunTest = .f.
  8052. if lRunTest
  8053.         testJsonClass()
  8054. endif
  8055. return
  8056.  
  8057.  
  8058. function json_encode(xExpr)
  8059.         if vartype(_json)<>'O'
  8060.                 public _json
  8061.                 _json = newobject('json')
  8062.         endif
  8063. return _json.encode(@xExpr)
  8064.  
  8065.  
  8066. function json_decode(cJson)
  8067. local retval
  8068.         if vartype(_json)<>'O'
  8069.                 public _json
  8070.                 _json = newobject('json')
  8071.         endif
  8072.         retval = _json.decode(cJson)
  8073.         if not empty(_json.cError)
  8074.                 return null
  8075.         endif
  8076. return retval
  8077.  
  8078. function json_getErrorMsg()
  8079. return _json.cError
  8080.        
  8081.  
  8082.  
  8083. *
  8084. * json class
  8085. *
  8086. *
  8087. define class json as custom
  8088.  
  8089.  
  8090.         nPos=0
  8091.         nLen=0
  8092.         cJson=''
  8093.         cError=''
  8094.  
  8095.  
  8096.         *
  8097.         * Genera el codigo cJson para parametro que se manda
  8098.         *
  8099.         function encode(xExpr)
  8100.         local cTipo
  8101.                 * Cuando se manda una arreglo,
  8102.                 if type('ALen(xExpr)')=='N'
  8103.                         cTipo = 'A'
  8104.                 Else
  8105.                         cTipo = VarType(xExpr)
  8106.                 Endif
  8107.                
  8108.                 Do Case
  8109.                 Case cTipo=='D'
  8110.                         return '"'+dtos(xExpr)+'"'
  8111.                 Case cTipo=='N'
  8112.                         return Transform(xExpr)
  8113.                 Case cTipo=='L'
  8114.                         return iif(xExpr,'true','false')
  8115.                 Case cTipo=='X'
  8116.                         return 'null'
  8117.                 Case cTipo=='C'
  8118.                         xExpr = allt(xExpr)
  8119.                         xExpr = StrTran(xExpr, '\', '\\' )
  8120.                         xExpr = StrTran(xExpr, '/', '\/' )
  8121.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  8122.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  8123.                         xExpr = StrTran(xExpr, '"', '\"' )
  8124.                         return '"'+xExpr+'"'
  8125.  
  8126.                 case cTipo=='O'
  8127.                         local cProp, cJsonValue, cRetVal, aProp[1]
  8128.                         =AMembers(aProp,xExpr)
  8129.                         cRetVal = ''
  8130.                         for each cProp in aProp
  8131.                                 *? cProp
  8132.                                 *? cRetVal
  8133.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  8134.                                         * algunas propiedades pueden no estar definidas
  8135.                                         * como: activecontrol, parent, etc
  8136.                                         loop
  8137.                                 endif
  8138.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  8139.                                         *
  8140.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  8141.                                         *
  8142.                                         Local i,nTotElem
  8143.                                         cJsonValue = ''
  8144.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  8145.                                         For i=1 to nTotElem
  8146.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  8147.                                         Next
  8148.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  8149.                                 else
  8150.                                         *
  8151.                                         * es otro tipo de dato normal C, N, L
  8152.                                         *
  8153.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  8154.                                 endif
  8155.                                 if left(cProp,1)=='_'
  8156.                                         cProp = substr(cProp,2)
  8157.                                 endif
  8158.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  8159.                         next
  8160.                         return '{' + substr(cRetVal,2) + '}'
  8161.  
  8162.                 case cTipo=='A'
  8163.                         local valor, cRetVal
  8164.                         cRetVal = ''   
  8165.                         for each valor in xExpr
  8166.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  8167.                         next
  8168.                         return  '[' + substr(cRetVal,2) + ']'
  8169.                        
  8170.                 endcase
  8171.  
  8172.         return ''
  8173.  
  8174.  
  8175.  
  8176.  
  8177.  
  8178.         *
  8179.         * regresa un elemento representado por la cadena json que se manda
  8180.         *
  8181.        
  8182.         function decode(cJson)
  8183.         local retValue
  8184.                 cJson = StrTran(cJson,chr(9),'')
  8185.                 cJson = StrTran(cJson,chr(10),'')
  8186.                 cJson = StrTran(cJson,chr(13),'')
  8187.                 cJson = this.fixUnicode(cJson)
  8188.                 this.nPos  = 1
  8189.                 this.cJson = cJson
  8190.                 this.nLen  = len(cJson)
  8191.                 this.cError = ''
  8192.                 retValue = this.parsevalue()
  8193.                 if not empty(this.cError)
  8194.                         return null
  8195.                 endif
  8196.                 if this.getToken()<>null
  8197.                         this.setError('Junk at the end of JSON input')
  8198.                         return null
  8199.                 endif
  8200.         return retValue
  8201.                
  8202.        
  8203.         function parseValue()
  8204.         local token
  8205.                 token = this.getToken()
  8206.                 if token==null
  8207.                         this.setError('Nothing to parse')
  8208.                         return null
  8209.                 endif
  8210.                 do case
  8211.                 case token=='"'
  8212.                         return this.parseString()
  8213.                 case isdigit(token) or token=='-'
  8214.                         return this.parseNumber()
  8215.                 case token=='n'
  8216.                         return this.expectedKeyword('null',null)
  8217.                 case token=='f'
  8218.                         return this.expectedKeyword('false',.f.)
  8219.                 case token=='t'
  8220.                         return this.expectedKeyword('true',.t.)
  8221.                 case token=='{'
  8222.                         return this.parseObject()
  8223.                 case token=='['
  8224.                         return this.parseArray()
  8225.                 otherwise
  8226.                         this.setError('Unexpected token')
  8227.                 endcase
  8228.         return
  8229.                
  8230.        
  8231.         function expectedKeyword(cWord,eValue)
  8232.                 for i=1 to len(cWord)
  8233.                         cChar = this.getChar()
  8234.                         if cChar <> substr(cWord,i,1)
  8235.                                 this.setError("Expected keyword '" + cWord + "'")
  8236.                                 return ''
  8237.                         endif
  8238.                         this.nPos = this.nPos + 1
  8239.                 next
  8240.         return eValue
  8241.        
  8242.  
  8243.         function parseObject()
  8244.         local retval, cPropName, xValue
  8245.                 retval = createObject('myObj')
  8246.                 this.nPos = this.nPos + 1 && Eat {
  8247.                 if this.getToken()<>'}'
  8248.                         do while .t.
  8249.                                 cPropName = this.parseString()
  8250.                                 if not empty(this.cError)
  8251.                                         return null
  8252.                                 endif
  8253.                                 if this.getToken()<>':'
  8254.                                         this.setError("Expected ':' when parsing object")
  8255.                                         return null
  8256.                                 endif
  8257.                                 this.nPos = this.nPos + 1
  8258.                                 xValue = this.parseValue()
  8259.                                 if not empty(this.cError)
  8260.                                         return null
  8261.                                 endif                          
  8262.                                 ** Debug ? cPropName, type('xValue')
  8263.                                 retval.set(cPropName, xValue)
  8264.                                 if this.getToken()<>','
  8265.                                         exit
  8266.                                 endif
  8267.                                 this.nPos = this.nPos + 1
  8268.                         enddo
  8269.                 endif
  8270.                 if this.getToken()<>'}'
  8271.                         this.setError("Expected '}' at the end of object")
  8272.                         return null
  8273.                 endif
  8274.                 this.nPos = this.nPos + 1
  8275.         return retval
  8276.  
  8277.  
  8278.         function parseArray()
  8279.         local retVal, xValue
  8280.                 retval = createObject('MyArray')
  8281.                 this.nPos = this.nPos + 1       && Eat [
  8282.                 if this.getToken() <> ']'
  8283.                         do while .t.
  8284.                                 xValue = this.parseValue()
  8285.                                 if not empty(this.cError)
  8286.                                         return null
  8287.                                 endif
  8288.                                 retval.add( xValue )
  8289.                                 if this.getToken()<>','
  8290.                                         exit
  8291.                                 endif
  8292.                                 this.nPos = this.nPos + 1
  8293.                         enddo
  8294.                         if this.getToken() <> ']'
  8295.                                 this.setError('Expected ] at the end of array')
  8296.                                 return null
  8297.                         endif
  8298.                 endif
  8299.                 this.nPos = this.nPos + 1
  8300.         return retval
  8301.        
  8302.  
  8303.         function parseString()
  8304.         local cRetVal, c
  8305.                 if this.getToken()<>'"'
  8306.                         this.setError('Expected "')
  8307.                         return ''
  8308.                 endif
  8309.                 this.nPos = this.nPos + 1       && Eat "
  8310.                 cRetVal = ''
  8311.                 do while .t.
  8312.                         c = this.getChar()
  8313.                         if c==''
  8314.                                 return ''
  8315.                         endif
  8316.                         if c == '"'
  8317.                                 this.nPos = this.nPos + 1
  8318.                                 exit
  8319.                         endif
  8320.                         if c == '\'
  8321.                                 this.nPos = this.nPos + 1
  8322.                                 if (this.nPos>this.nLen)
  8323.                                         this.setError('\\ at the end of input')
  8324.                                         return ''
  8325.                                 endif
  8326.                                 c = this.getChar()
  8327.                                 if c==''
  8328.                                         return ''
  8329.                                 endif
  8330.                                 do case
  8331.                                 case c=='"'
  8332.                                         c='"'
  8333.                                 case c=='\'
  8334.                                         c='\'
  8335.                                 case c=='/'
  8336.                                         c='/'
  8337.                                 case c=='b'
  8338.                                         c=chr(8)
  8339.                                 case c=='t'
  8340.                                         c=chr(9)
  8341.                                 case c=='n'
  8342.                                         c=chr(10)
  8343.                                 case c=='f'
  8344.                                         c=chr(12)
  8345.                                 case c=='r'
  8346.                                         c=chr(13)
  8347.                                 otherwise
  8348.                                         ******* FALTAN LOS UNICODE
  8349.                                         this.setError('Invalid escape sequence in string literal')
  8350.                                         return ''
  8351.                                 endcase
  8352.                         endif
  8353.                         cRetVal = cRetVal + c
  8354.                         this.nPos = this.nPos + 1
  8355.                 enddo
  8356.         return cRetVal
  8357.                                        
  8358.  
  8359.         **** Pendiente numeros con E
  8360.         function parseNumber()
  8361.         local nStartPos,c, isInt, cNumero
  8362.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  8363.                         this.setError('Expected number literal')
  8364.                         return 0
  8365.                 endif
  8366.                 nStartPos = this.nPos
  8367.                 c = this.getChar()
  8368.                 if c == '-'
  8369.                         c = this.nextChar()
  8370.                 endif
  8371.                 if c == '0'
  8372.                         c = this.nextChar()
  8373.                 else
  8374.                         if isdigit(c)
  8375.                                 c = this.nextChar()
  8376.                                 do while isdigit(c)
  8377.                                         c = this.nextChar()
  8378.                                 enddo
  8379.                         else
  8380.                                 this.setError('Expected digit when parsing number')
  8381.                                 return 0
  8382.                         endif
  8383.                 endif
  8384.                
  8385.                 isInt = .t.
  8386.                 if c=='.'
  8387.                         c = this.nextChar()
  8388.                         if isdigit(c)
  8389.                                 c = this.nextChar()
  8390.                                 isInt = .f.
  8391.                                 do while isDigit(c)
  8392.                                         c = this.nextChar()
  8393.                                 enddo
  8394.                         else
  8395.                                 this.setError('Expected digit following dot comma')
  8396.                                 return 0
  8397.                         endif
  8398.                 endif
  8399.                
  8400.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  8401.         return val(cNumero)
  8402.  
  8403.  
  8404.  
  8405.         function getToken()
  8406.         local char1
  8407.                 do while .t.
  8408.                         if this.nPos > this.nLen
  8409.                                 return null
  8410.                         endif
  8411.                         char1 = substr(this.cJson, this.nPos, 1)
  8412.                         if char1==' '
  8413.                                 this.nPos = this.nPos + 1
  8414.                                 loop
  8415.                         endif
  8416.                         return char1
  8417.                 enddo
  8418.         return
  8419.        
  8420.                
  8421.                
  8422.         function getChar()
  8423.                 if this.nPos > this.nLen
  8424.                         this.setError('Unexpected end of JSON stream')
  8425.                         return ''
  8426.                 endif
  8427.         return substr(this.cJson, this.nPos, 1)
  8428.        
  8429.         function nextChar()
  8430.                 this.nPos = this.nPos + 1
  8431.                 if this.nPos > this.nLen
  8432.                         return ''
  8433.                 endif
  8434.         return substr(this.cJson, this.nPos, 1)
  8435.        
  8436.         function setError(cMsg)
  8437.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  8438.         return
  8439.  
  8440.  
  8441.         function fixUnicode(cStr)
  8442.                 cStr = StrTran(cStr,'\u00e1','á')
  8443.                 cStr = StrTran(cStr,'\u00e9','é')
  8444.                 cStr = StrTran(cStr,'\u00ed','í')
  8445.                 cStr = StrTran(cStr,'\u00f3','ó')
  8446.                 cStr = StrTran(cStr,'\u00fa','ú')
  8447.                 cStr = StrTran(cStr,'\u00c1','Á')
  8448.                 cStr = StrTran(cStr,'\u00c9','É')
  8449.                 cStr = StrTran(cStr,'\u00cd','Í')
  8450.                 cStr = StrTran(cStr,'\u00d3','Ó')
  8451.                 cStr = StrTran(cStr,'\u00da','Ú')
  8452.                 cStr = StrTran(cStr,'\u00f1','ń')
  8453.                 cStr = StrTran(cStr,'\u00d1','Ń')
  8454.         return cStr
  8455.  
  8456.  
  8457.  
  8458. enddefine
  8459.  
  8460.  
  8461.  
  8462.  
  8463.  
  8464. *
  8465. * class used to return an array
  8466. *
  8467. define class myArray as custom
  8468.         nSize = 0
  8469.         dimension array[1]
  8470.  
  8471.         function add(xExpr)
  8472.                 this.nSize = this.nSize + 1
  8473.                 dimension this.array[this.nSize]
  8474.                 this.array[this.nSize] = xExpr
  8475.         return
  8476.  
  8477.         function get(n)
  8478.         return this.array[n]
  8479.  
  8480. enddefine
  8481.  
  8482.  
  8483.  
  8484. *
  8485. * class used to simulate an object
  8486. * all properties are prefixed with 'prop' to permit property names like: error, init
  8487. * that already exists like vfp methods
  8488. *
  8489. define class myObj as custom
  8490. Hidden ;
  8491.         ClassLibrary,Comment, ;
  8492.         BaseClass,ControlCount, ;
  8493.         Controls,Objects,Object,;
  8494.         Height,HelpContextID,Left,Name, ;
  8495.         Parent,ParentClass,Picture, ;
  8496.         Tag,Top,WhatsThisHelpID,Width
  8497.                
  8498.         function set(cPropName, xValue)
  8499.                 cPropName = '_'+cPropName
  8500.                 if type('this.'+cPropName)=='U'
  8501.                         this.addProperty(cPropName,xValue)
  8502.                 else
  8503.                         local cmd
  8504.                         cmd = 'this.'+cPropName+'=xValue'
  8505.                         &cmd
  8506.                 endif
  8507.         return
  8508.        
  8509.         procedure get (cPropName)
  8510.                 cPropName = '_'+cPropName
  8511.                 If type('this.'+cPropName)=='U'
  8512.                         return ''
  8513.                 Else
  8514.                         local cmd
  8515.                         cmd = 'return this.'+cPropName
  8516.                         &cmd
  8517.                 endif
  8518.         return ''
  8519. enddefine
  8520.  
  8521.  
  8522.  
  8523.  
  8524.  
  8525. function testJsonClass
  8526.         clear
  8527.         set decimal to 10
  8528.         oJson = newObject('json')
  8529.        
  8530.        
  8531.         ? 'Test Basic Types'
  8532.         ? '----------------'
  8533.         ? oJson.decode('null')
  8534.         ? oJson.decode('true')
  8535.         ? oJson.decode('false')
  8536.         ?
  8537.         ? oJson.decode('791123')
  8538.         ? oJson.decode('791123.45')
  8539.         ? oJson.decode('791123.45.')
  8540.         ? oJson.decode('"nacho gtz"')
  8541.         if not empty(oJson.cError)
  8542.                 ? oJson.cError
  8543.                 return
  8544.         endif
  8545.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  8546.         if not empty(oJson.cError)
  8547.                 ? oJson.cError
  8548.                 return
  8549.         endif
  8550.        
  8551.         ? 'Test Array'
  8552.         ? '----------'
  8553.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  8554.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  8555.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  8556.         nombres = arr.get(1)
  8557.         edades  = arr.get(2)
  8558.         ? nombres.get(1), edades.get(1)
  8559.         ? nombres.get(2), edades.get(2)
  8560.         ? nombres.get(3), edades.get(3)
  8561.         ?
  8562.         ? 'Test Object'
  8563.         ? '-----------'
  8564.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  8565.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  8566.         ? obj._Nombre, obj._Edad, obj._IsGood
  8567.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  8568.         ? obj.get('jsonrpc'), obj._jsonrpc
  8569.         ? obj.get('id'), obj._id
  8570.         ? obj.get('method'), obj._method
  8571.         ? obj._Params.array[1], obj._Params.get(1)
  8572.  
  8573.         ?
  8574.         ? 'Test nested object'
  8575.         ? '------------------'
  8576.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  8577.         obj = oJson.decode(cJson)
  8578.         if not empty(oJson.cError)
  8579.                 ? oJson.cError
  8580.                 return
  8581.         endif
  8582.         ? cJson
  8583.         ? 'method -->',obj._method
  8584.         ? 'usrkey -->',obj._params._data._usrkey
  8585.         ? 'sendto -->',obj._params._data._sendto
  8586.         ? 'name  --->',obj._params._data._name
  8587.         ? 'expires ->',obj._params._data._expires
  8588.  
  8589.         ?
  8590.         ? 'Test empty object'
  8591.         ? '-----------------'
  8592.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  8593.         obj = oJson.decode(cJson)
  8594.         if not empty(oJson.cError)     
  8595.                 ? oJson.cError
  8596.                 return
  8597.         endif
  8598.         ? cJson
  8599.         ? 'result -->',obj._result, obj.get('result')
  8600.         oError = obj.get('error')
  8601.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  8602.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  8603.         ? 'id  ----->',obj._id, obj.get('id')
  8604.         ?  type("oError._code")
  8605.  
  8606.         ?
  8607.         ? 'Probar decode-enconde-decode-encode'
  8608.         ? '------------------------------------'
  8609.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  8610.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  8611.         ? cJson
  8612.         oSmtp = json_decode(cJson)
  8613.         cJson =  json_encode(oSmtp)
  8614.         ? cJson
  8615.         oSmtp = json_decode(cJson)
  8616.         cJson =  json_encode(oSmtp)
  8617.         ? cJson
  8618.  
  8619.         * Probar falla
  8620.         ?
  8621.         ? 'Probar una falla en el json'
  8622.         ? '---------------------------'
  8623.         cJson = ' {"server":"", "user":"", "password":"" ,'
  8624.         oSmtp = json_decode(cJson)
  8625.         if not empty(json_getErrorMsg())
  8626.                 ? json_getErrorMsg()
  8627.         endif
  8628.  
  8629.         ?
  8630.         ? 'Pruebas Finalizadas'
  8631. retur
  8632. * Thanks Google for the code in Json Dart
  8633. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  8634. *
  8635. *
  8636. * Codificar y Decodificar JSON
  8637. *
  8638. * Puedes usar las funciones:
  8639. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  8640. *               json_decode(cJson)  te regresa el objeto representado en cJson
  8641. *
  8642. * Tambien puedes usar directamente la clase:
  8643. *
  8644. *       oJson = newobject('json','json.prg')
  8645. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  8646. *       ? oJson.encode(oCliente)
  8647. *       ? oCliente.get('nombre')
  8648. *       ? oCliente.get('apellido')
  8649. *
  8650. *
  8651. * VFPJSON  Encode and Decode JSON for VFP
  8652. * Examples:
  8653. *       oJson = newobject('json','json.prg')
  8654. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  8655. *       ? oJson.encode(oCustomer)
  8656. *       ? oCustomer.get('name')
  8657. *       ? oCustomer.get('lastname')
  8658. *
  8659. *
  8660. lRunTest = .f.
  8661. if lRunTest
  8662.         testJsonClass()
  8663. endif
  8664. return
  8665.  
  8666.  
  8667. function json_encode(xExpr)
  8668.         if vartype(_json)<>'O'
  8669.                 public _json
  8670.                 _json = newobject('json')
  8671.         endif
  8672. return _json.encode(@xExpr)
  8673.  
  8674.  
  8675. function json_decode(cJson)
  8676. local retval
  8677.         if vartype(_json)<>'O'
  8678.                 public _json
  8679.                 _json = newobject('json')
  8680.         endif
  8681.         retval = _json.decode(cJson)
  8682.         if not empty(_json.cError)
  8683.                 return null
  8684.         endif
  8685. return retval
  8686.  
  8687. function json_getErrorMsg()
  8688. return _json.cError
  8689.        
  8690.  
  8691.  
  8692. *
  8693. * json class
  8694. *
  8695. *
  8696. define class json as custom
  8697.  
  8698.  
  8699.         nPos=0
  8700.         nLen=0
  8701.         cJson=''
  8702.         cError=''
  8703.  
  8704.  
  8705.         *
  8706.         * Genera el codigo cJson para parametro que se manda
  8707.         *
  8708.         function encode(xExpr)
  8709.         local cTipo
  8710.                 * Cuando se manda una arreglo,
  8711.                 if type('ALen(xExpr)')=='N'
  8712.                         cTipo = 'A'
  8713.                 Else
  8714.                         cTipo = VarType(xExpr)
  8715.                 Endif
  8716.                
  8717.                 Do Case
  8718.                 Case cTipo=='D'
  8719.                         return '"'+dtos(xExpr)+'"'
  8720.                 Case cTipo=='N'
  8721.                         return Transform(xExpr)
  8722.                 Case cTipo=='L'
  8723.                         return iif(xExpr,'true','false')
  8724.                 Case cTipo=='X'
  8725.                         return 'null'
  8726.                 Case cTipo=='C'
  8727.                         xExpr = allt(xExpr)
  8728.                         xExpr = StrTran(xExpr, '\', '\\' )
  8729.                         xExpr = StrTran(xExpr, '/', '\/' )
  8730.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  8731.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  8732.                         xExpr = StrTran(xExpr, '"', '\"' )
  8733.                         return '"'+xExpr+'"'
  8734.  
  8735.                 case cTipo=='O'
  8736.                         local cProp, cJsonValue, cRetVal, aProp[1]
  8737.                         =AMembers(aProp,xExpr)
  8738.                         cRetVal = ''
  8739.                         for each cProp in aProp
  8740.                                 *? cProp
  8741.                                 *? cRetVal
  8742.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  8743.                                         * algunas propiedades pueden no estar definidas
  8744.                                         * como: activecontrol, parent, etc
  8745.                                         loop
  8746.                                 endif
  8747.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  8748.                                         *
  8749.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  8750.                                         *
  8751.                                         Local i,nTotElem
  8752.                                         cJsonValue = ''
  8753.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  8754.                                         For i=1 to nTotElem
  8755.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  8756.                                         Next
  8757.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  8758.                                 else
  8759.                                         *
  8760.                                         * es otro tipo de dato normal C, N, L
  8761.                                         *
  8762.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  8763.                                 endif
  8764.                                 if left(cProp,1)=='_'
  8765.                                         cProp = substr(cProp,2)
  8766.                                 endif
  8767.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  8768.                         next
  8769.                         return '{' + substr(cRetVal,2) + '}'
  8770.  
  8771.                 case cTipo=='A'
  8772.                         local valor, cRetVal
  8773.                         cRetVal = ''   
  8774.                         for each valor in xExpr
  8775.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  8776.                         next
  8777.                         return  '[' + substr(cRetVal,2) + ']'
  8778.                        
  8779.                 endcase
  8780.  
  8781.         return ''
  8782.  
  8783.  
  8784.  
  8785.  
  8786.  
  8787.         *
  8788.         * regresa un elemento representado por la cadena json que se manda
  8789.         *
  8790.        
  8791.         function decode(cJson)
  8792.         local retValue
  8793.                 cJson = StrTran(cJson,chr(9),'')
  8794.                 cJson = StrTran(cJson,chr(10),'')
  8795.                 cJson = StrTran(cJson,chr(13),'')
  8796.                 cJson = this.fixUnicode(cJson)
  8797.                 this.nPos  = 1
  8798.                 this.cJson = cJson
  8799.                 this.nLen  = len(cJson)
  8800.                 this.cError = ''
  8801.                 retValue = this.parsevalue()
  8802.                 if not empty(this.cError)
  8803.                         return null
  8804.                 endif
  8805.                 if this.getToken()<>null
  8806.                         this.setError('Junk at the end of JSON input')
  8807.                         return null
  8808.                 endif
  8809.         return retValue
  8810.                
  8811.        
  8812.         function parseValue()
  8813.         local token
  8814.                 token = this.getToken()
  8815.                 if token==null
  8816.                         this.setError('Nothing to parse')
  8817.                         return null
  8818.                 endif
  8819.                 do case
  8820.                 case token=='"'
  8821.                         return this.parseString()
  8822.                 case isdigit(token) or token=='-'
  8823.                         return this.parseNumber()
  8824.                 case token=='n'
  8825.                         return this.expectedKeyword('null',null)
  8826.                 case token=='f'
  8827.                         return this.expectedKeyword('false',.f.)
  8828.                 case token=='t'
  8829.                         return this.expectedKeyword('true',.t.)
  8830.                 case token=='{'
  8831.                         return this.parseObject()
  8832.                 case token=='['
  8833.                         return this.parseArray()
  8834.                 otherwise
  8835.                         this.setError('Unexpected token')
  8836.                 endcase
  8837.         return
  8838.                
  8839.        
  8840.         function expectedKeyword(cWord,eValue)
  8841.                 for i=1 to len(cWord)
  8842.                         cChar = this.getChar()
  8843.                         if cChar <> substr(cWord,i,1)
  8844.                                 this.setError("Expected keyword '" + cWord + "'")
  8845.                                 return ''
  8846.                         endif
  8847.                         this.nPos = this.nPos + 1
  8848.                 next
  8849.         return eValue
  8850.        
  8851.  
  8852.         function parseObject()
  8853.         local retval, cPropName, xValue
  8854.                 retval = createObject('myObj')
  8855.                 this.nPos = this.nPos + 1 && Eat {
  8856.                 if this.getToken()<>'}'
  8857.                         do while .t.
  8858.                                 cPropName = this.parseString()
  8859.                                 if not empty(this.cError)
  8860.                                         return null
  8861.                                 endif
  8862.                                 if this.getToken()<>':'
  8863.                                         this.setError("Expected ':' when parsing object")
  8864.                                         return null
  8865.                                 endif
  8866.                                 this.nPos = this.nPos + 1
  8867.                                 xValue = this.parseValue()
  8868.                                 if not empty(this.cError)
  8869.                                         return null
  8870.                                 endif                          
  8871.                                 ** Debug ? cPropName, type('xValue')
  8872.                                 retval.set(cPropName, xValue)
  8873.                                 if this.getToken()<>','
  8874.                                         exit
  8875.                                 endif
  8876.                                 this.nPos = this.nPos + 1
  8877.                         enddo
  8878.                 endif
  8879.                 if this.getToken()<>'}'
  8880.                         this.setError("Expected '}' at the end of object")
  8881.                         return null
  8882.                 endif
  8883.                 this.nPos = this.nPos + 1
  8884.         return retval
  8885.  
  8886.  
  8887.         function parseArray()
  8888.         local retVal, xValue
  8889.                 retval = createObject('MyArray')
  8890.                 this.nPos = this.nPos + 1       && Eat [
  8891.                 if this.getToken() <> ']'
  8892.                         do while .t.
  8893.                                 xValue = this.parseValue()
  8894.                                 if not empty(this.cError)
  8895.                                         return null
  8896.                                 endif
  8897.                                 retval.add( xValue )
  8898.                                 if this.getToken()<>','
  8899.                                         exit
  8900.                                 endif
  8901.                                 this.nPos = this.nPos + 1
  8902.                         enddo
  8903.                         if this.getToken() <> ']'
  8904.                                 this.setError('Expected ] at the end of array')
  8905.                                 return null
  8906.                         endif
  8907.                 endif
  8908.                 this.nPos = this.nPos + 1
  8909.         return retval
  8910.        
  8911.  
  8912.         function parseString()
  8913.         local cRetVal, c
  8914.                 if this.getToken()<>'"'
  8915.                         this.setError('Expected "')
  8916.                         return ''
  8917.                 endif
  8918.                 this.nPos = this.nPos + 1       && Eat "
  8919.                 cRetVal = ''
  8920.                 do while .t.
  8921.                         c = this.getChar()
  8922.                         if c==''
  8923.                                 return ''
  8924.                         endif
  8925.                         if c == '"'
  8926.                                 this.nPos = this.nPos + 1
  8927.                                 exit
  8928.                         endif
  8929.                         if c == '\'
  8930.                                 this.nPos = this.nPos + 1
  8931.                                 if (this.nPos>this.nLen)
  8932.                                         this.setError('\\ at the end of input')
  8933.                                         return ''
  8934.                                 endif
  8935.                                 c = this.getChar()
  8936.                                 if c==''
  8937.                                         return ''
  8938.                                 endif
  8939.                                 do case
  8940.                                 case c=='"'
  8941.                                         c='"'
  8942.                                 case c=='\'
  8943.                                         c='\'
  8944.                                 case c=='/'
  8945.                                         c='/'
  8946.                                 case c=='b'
  8947.                                         c=chr(8)
  8948.                                 case c=='t'
  8949.                                         c=chr(9)
  8950.                                 case c=='n'
  8951.                                         c=chr(10)
  8952.                                 case c=='f'
  8953.                                         c=chr(12)
  8954.                                 case c=='r'
  8955.                                         c=chr(13)
  8956.                                 otherwise
  8957.                                         ******* FALTAN LOS UNICODE
  8958.                                         this.setError('Invalid escape sequence in string literal')
  8959.                                         return ''
  8960.                                 endcase
  8961.                         endif
  8962.                         cRetVal = cRetVal + c
  8963.                         this.nPos = this.nPos + 1
  8964.                 enddo
  8965.         return cRetVal
  8966.                                        
  8967.  
  8968.         **** Pendiente numeros con E
  8969.         function parseNumber()
  8970.         local nStartPos,c, isInt, cNumero
  8971.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  8972.                         this.setError('Expected number literal')
  8973.                         return 0
  8974.                 endif
  8975.                 nStartPos = this.nPos
  8976.                 c = this.getChar()
  8977.                 if c == '-'
  8978.                         c = this.nextChar()
  8979.                 endif
  8980.                 if c == '0'
  8981.                         c = this.nextChar()
  8982.                 else
  8983.                         if isdigit(c)
  8984.                                 c = this.nextChar()
  8985.                                 do while isdigit(c)
  8986.                                         c = this.nextChar()
  8987.                                 enddo
  8988.                         else
  8989.                                 this.setError('Expected digit when parsing number')
  8990.                                 return 0
  8991.                         endif
  8992.                 endif
  8993.                
  8994.                 isInt = .t.
  8995.                 if c=='.'
  8996.                         c = this.nextChar()
  8997.                         if isdigit(c)
  8998.                                 c = this.nextChar()
  8999.                                 isInt = .f.
  9000.                                 do while isDigit(c)
  9001.                                         c = this.nextChar()
  9002.                                 enddo
  9003.                         else
  9004.                                 this.setError('Expected digit following dot comma')
  9005.                                 return 0
  9006.                         endif
  9007.                 endif
  9008.                
  9009.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  9010.         return val(cNumero)
  9011.  
  9012.  
  9013.  
  9014.         function getToken()
  9015.         local char1
  9016.                 do while .t.
  9017.                         if this.nPos > this.nLen
  9018.                                 return null
  9019.                         endif
  9020.                         char1 = substr(this.cJson, this.nPos, 1)
  9021.                         if char1==' '
  9022.                                 this.nPos = this.nPos + 1
  9023.                                 loop
  9024.                         endif
  9025.                         return char1
  9026.                 enddo
  9027.         return
  9028.        
  9029.                
  9030.                
  9031.         function getChar()
  9032.                 if this.nPos > this.nLen
  9033.                         this.setError('Unexpected end of JSON stream')
  9034.                         return ''
  9035.                 endif
  9036.         return substr(this.cJson, this.nPos, 1)
  9037.        
  9038.         function nextChar()
  9039.                 this.nPos = this.nPos + 1
  9040.                 if this.nPos > this.nLen
  9041.                         return ''
  9042.                 endif
  9043.         return substr(this.cJson, this.nPos, 1)
  9044.        
  9045.         function setError(cMsg)
  9046.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  9047.         return
  9048.  
  9049.  
  9050.         function fixUnicode(cStr)
  9051.                 cStr = StrTran(cStr,'\u00e1','á')
  9052.                 cStr = StrTran(cStr,'\u00e9','é')
  9053.                 cStr = StrTran(cStr,'\u00ed','í')
  9054.                 cStr = StrTran(cStr,'\u00f3','ó')
  9055.                 cStr = StrTran(cStr,'\u00fa','ú')
  9056.                 cStr = StrTran(cStr,'\u00c1','Á')
  9057.                 cStr = StrTran(cStr,'\u00c9','É')
  9058.                 cStr = StrTran(cStr,'\u00cd','Í')
  9059.                 cStr = StrTran(cStr,'\u00d3','Ó')
  9060.                 cStr = StrTran(cStr,'\u00da','Ú')
  9061.                 cStr = StrTran(cStr,'\u00f1','ń')
  9062.                 cStr = StrTran(cStr,'\u00d1','Ń')
  9063.         return cStr
  9064.  
  9065.  
  9066.  
  9067. enddefine
  9068.  
  9069.  
  9070.  
  9071.  
  9072.  
  9073. *
  9074. * class used to return an array
  9075. *
  9076. define class myArray as custom
  9077.         nSize = 0
  9078.         dimension array[1]
  9079.  
  9080.         function add(xExpr)
  9081.                 this.nSize = this.nSize + 1
  9082.                 dimension this.array[this.nSize]
  9083.                 this.array[this.nSize] = xExpr
  9084.         return
  9085.  
  9086.         function get(n)
  9087.         return this.array[n]
  9088.  
  9089. enddefine
  9090.  
  9091.  
  9092.  
  9093. *
  9094. * class used to simulate an object
  9095. * all properties are prefixed with 'prop' to permit property names like: error, init
  9096. * that already exists like vfp methods
  9097. *
  9098. define class myObj as custom
  9099. Hidden ;
  9100.         ClassLibrary,Comment, ;
  9101.         BaseClass,ControlCount, ;
  9102.         Controls,Objects,Object,;
  9103.         Height,HelpContextID,Left,Name, ;
  9104.         Parent,ParentClass,Picture, ;
  9105.         Tag,Top,WhatsThisHelpID,Width
  9106.                
  9107.         function set(cPropName, xValue)
  9108.                 cPropName = '_'+cPropName
  9109.                 if type('this.'+cPropName)=='U'
  9110.                         this.addProperty(cPropName,xValue)
  9111.                 else
  9112.                         local cmd
  9113.                         cmd = 'this.'+cPropName+'=xValue'
  9114.                         &cmd
  9115.                 endif
  9116.         return
  9117.        
  9118.         procedure get (cPropName)
  9119.                 cPropName = '_'+cPropName
  9120.                 If type('this.'+cPropName)=='U'
  9121.                         return ''
  9122.                 Else
  9123.                         local cmd
  9124.                         cmd = 'return this.'+cPropName
  9125.                         &cmd
  9126.                 endif
  9127.         return ''
  9128. enddefine
  9129.  
  9130.  
  9131.  
  9132.  
  9133.  
  9134. function testJsonClass
  9135.         clear
  9136.         set decimal to 10
  9137.         oJson = newObject('json')
  9138.        
  9139.        
  9140.         ? 'Test Basic Types'
  9141.         ? '----------------'
  9142.         ? oJson.decode('null')
  9143.         ? oJson.decode('true')
  9144.         ? oJson.decode('false')
  9145.         ?
  9146.         ? oJson.decode('791123')
  9147.         ? oJson.decode('791123.45')
  9148.         ? oJson.decode('791123.45.')
  9149.         ? oJson.decode('"nacho gtz"')
  9150.         if not empty(oJson.cError)
  9151.                 ? oJson.cError
  9152.                 return
  9153.         endif
  9154.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  9155.         if not empty(oJson.cError)
  9156.                 ? oJson.cError
  9157.                 return
  9158.         endif
  9159.        
  9160.         ? 'Test Array'
  9161.         ? '----------'
  9162.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  9163.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  9164.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  9165.         nombres = arr.get(1)
  9166.         edades  = arr.get(2)
  9167.         ? nombres.get(1), edades.get(1)
  9168.         ? nombres.get(2), edades.get(2)
  9169.         ? nombres.get(3), edades.get(3)
  9170.         ?
  9171.         ? 'Test Object'
  9172.         ? '-----------'
  9173.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  9174.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  9175.         ? obj._Nombre, obj._Edad, obj._IsGood
  9176.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  9177.         ? obj.get('jsonrpc'), obj._jsonrpc
  9178.         ? obj.get('id'), obj._id
  9179.         ? obj.get('method'), obj._method
  9180.         ? obj._Params.array[1], obj._Params.get(1)
  9181.  
  9182.         ?
  9183.         ? 'Test nested object'
  9184.         ? '------------------'
  9185.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  9186.         obj = oJson.decode(cJson)
  9187.         if not empty(oJson.cError)
  9188.                 ? oJson.cError
  9189.                 return
  9190.         endif
  9191.         ? cJson
  9192.         ? 'method -->',obj._method
  9193.         ? 'usrkey -->',obj._params._data._usrkey
  9194.         ? 'sendto -->',obj._params._data._sendto
  9195.         ? 'name  --->',obj._params._data._name
  9196.         ? 'expires ->',obj._params._data._expires
  9197.  
  9198.         ?
  9199.         ? 'Test empty object'
  9200.         ? '-----------------'
  9201.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  9202.         obj = oJson.decode(cJson)
  9203.         if not empty(oJson.cError)     
  9204.                 ? oJson.cError
  9205.                 return
  9206.         endif
  9207.         ? cJson
  9208.         ? 'result -->',obj._result, obj.get('result')
  9209.         oError = obj.get('error')
  9210.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  9211.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  9212.         ? 'id  ----->',obj._id, obj.get('id')
  9213.         ?  type("oError._code")
  9214.  
  9215.         ?
  9216.         ? 'Probar decode-enconde-decode-encode'
  9217.         ? '------------------------------------'
  9218.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  9219.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  9220.         ? cJson
  9221.         oSmtp = json_decode(cJson)
  9222.         cJson =  json_encode(oSmtp)
  9223.         ? cJson
  9224.         oSmtp = json_decode(cJson)
  9225.         cJson =  json_encode(oSmtp)
  9226.         ? cJson
  9227.  
  9228.         * Probar falla
  9229.         ?
  9230.         ? 'Probar una falla en el json'
  9231.         ? '---------------------------'
  9232.         cJson = ' {"server":"", "user":"", "password":"" ,'
  9233.         oSmtp = json_decode(cJson)
  9234.         if not empty(json_getErrorMsg())
  9235.                 ? json_getErrorMsg()
  9236.         endif
  9237.  
  9238.         ?
  9239.         ? 'Pruebas Finalizadas'
  9240. retur
  9241. *     http://code.google.com/p/dart/source/browse/trunk/dart/lib/json/json.dart
  9242. *
  9243. *
  9244. * Codificar y Decodificar JSON
  9245. *
  9246. * Puedes usar las funciones:
  9247. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  9248. *               json_decode(cJson)  te regresa el objeto representado en cJson
  9249. *
  9250. * Tambien puedes usar directamente la clase:
  9251. *
  9252. *       oJson = newobject('json','json.prg')
  9253. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  9254. *       ? oJson.encode(oCliente)
  9255. *       ? oCliente.get('nombre')
  9256. *       ? oCliente.get('apellido')
  9257. *
  9258. *
  9259. * VFPJSON  Encode and Decode JSON for VFP
  9260. * Examples:
  9261. *       oJson = newobject('json','json.prg')
  9262. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  9263. *       ? oJson.encode(oCustomer)
  9264. *       ? oCustomer.get('name')
  9265. *       ? oCustomer.get('lastname')
  9266. *
  9267. *
  9268. lRunTest = .f.
  9269. if lRunTest
  9270.         testJsonClass()
  9271. endif
  9272. return
  9273.  
  9274.  
  9275. function json_encode(xExpr)
  9276.         if vartype(_json)<>'O'
  9277.                 public _json
  9278.                 _json = newobject('json')
  9279.         endif
  9280. return _json.encode(@xExpr)
  9281.  
  9282.  
  9283. function json_decode(cJson)
  9284. local retval
  9285.         if vartype(_json)<>'O'
  9286.                 public _json
  9287.                 _json = newobject('json')
  9288.         endif
  9289.         retval = _json.decode(cJson)
  9290.         if not empty(_json.cError)
  9291.                 return null
  9292.         endif
  9293. return retval
  9294.  
  9295. function json_getErrorMsg()
  9296. return _json.cError
  9297.        
  9298.  
  9299.  
  9300. *
  9301. * json class
  9302. *
  9303. *
  9304. define class json as custom
  9305.  
  9306.  
  9307.         nPos=0
  9308.         nLen=0
  9309.         cJson=''
  9310.         cError=''
  9311.  
  9312.  
  9313.         *
  9314.         * Genera el codigo cJson para parametro que se manda
  9315.         *
  9316.         function encode(xExpr)
  9317.         local cTipo
  9318.                 * Cuando se manda una arreglo,
  9319.                 if type('ALen(xExpr)')=='N'
  9320.                         cTipo = 'A'
  9321.                 Else
  9322.                         cTipo = VarType(xExpr)
  9323.                 Endif
  9324.                
  9325.                 Do Case
  9326.                 Case cTipo=='D'
  9327.                         return '"'+dtos(xExpr)+'"'
  9328.                 Case cTipo=='N'
  9329.                         return Transform(xExpr)
  9330.                 Case cTipo=='L'
  9331.                         return iif(xExpr,'true','false')
  9332.                 Case cTipo=='X'
  9333.                         return 'null'
  9334.                 Case cTipo=='C'
  9335.                         xExpr = allt(xExpr)
  9336.                         xExpr = StrTran(xExpr, '\', '\\' )
  9337.                         xExpr = StrTran(xExpr, '/', '\/' )
  9338.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  9339.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  9340.                         xExpr = StrTran(xExpr, '"', '\"' )
  9341.                         return '"'+xExpr+'"'
  9342.  
  9343.                 case cTipo=='O'
  9344.                         local cProp, cJsonValue, cRetVal, aProp[1]
  9345.                         =AMembers(aProp,xExpr)
  9346.                         cRetVal = ''
  9347.                         for each cProp in aProp
  9348.                                 *? cProp
  9349.                                 *? cRetVal
  9350.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  9351.                                         * algunas propiedades pueden no estar definidas
  9352.                                         * como: activecontrol, parent, etc
  9353.                                         loop
  9354.                                 endif
  9355.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  9356.                                         *
  9357.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  9358.                                         *
  9359.                                         Local i,nTotElem
  9360.                                         cJsonValue = ''
  9361.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  9362.                                         For i=1 to nTotElem
  9363.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  9364.                                         Next
  9365.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  9366.                                 else
  9367.                                         *
  9368.                                         * es otro tipo de dato normal C, N, L
  9369.                                         *
  9370.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  9371.                                 endif
  9372.                                 if left(cProp,1)=='_'
  9373.                                         cProp = substr(cProp,2)
  9374.                                 endif
  9375.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  9376.                         next
  9377.                         return '{' + substr(cRetVal,2) + '}'
  9378.  
  9379.                 case cTipo=='A'
  9380.                         local valor, cRetVal
  9381.                         cRetVal = ''   
  9382.                         for each valor in xExpr
  9383.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  9384.                         next
  9385.                         return  '[' + substr(cRetVal,2) + ']'
  9386.                        
  9387.                 endcase
  9388.  
  9389.         return ''
  9390.  
  9391.  
  9392.  
  9393.  
  9394.  
  9395.         *
  9396.         * regresa un elemento representado por la cadena json que se manda
  9397.         *
  9398.        
  9399.         function decode(cJson)
  9400.         local retValue
  9401.                 cJson = StrTran(cJson,chr(9),'')
  9402.                 cJson = StrTran(cJson,chr(10),'')
  9403.                 cJson = StrTran(cJson,chr(13),'')
  9404.                 cJson = this.fixUnicode(cJson)
  9405.                 this.nPos  = 1
  9406.                 this.cJson = cJson
  9407.                 this.nLen  = len(cJson)
  9408.                 this.cError = ''
  9409.                 retValue = this.parsevalue()
  9410.                 if not empty(this.cError)
  9411.                         return null
  9412.                 endif
  9413.                 if this.getToken()<>null
  9414.                         this.setError('Junk at the end of JSON input')
  9415.                         return null
  9416.                 endif
  9417.         return retValue
  9418.                
  9419.        
  9420.         function parseValue()
  9421.         local token
  9422.                 token = this.getToken()
  9423.                 if token==null
  9424.                         this.setError('Nothing to parse')
  9425.                         return null
  9426.                 endif
  9427.                 do case
  9428.                 case token=='"'
  9429.                         return this.parseString()
  9430.                 case isdigit(token) or token=='-'
  9431.                         return this.parseNumber()
  9432.                 case token=='n'
  9433.                         return this.expectedKeyword('null',null)
  9434.                 case token=='f'
  9435.                         return this.expectedKeyword('false',.f.)
  9436.                 case token=='t'
  9437.                         return this.expectedKeyword('true',.t.)
  9438.                 case token=='{'
  9439.                         return this.parseObject()
  9440.                 case token=='['
  9441.                         return this.parseArray()
  9442.                 otherwise
  9443.                         this.setError('Unexpected token')
  9444.                 endcase
  9445.         return
  9446.                
  9447.        
  9448.         function expectedKeyword(cWord,eValue)
  9449.                 for i=1 to len(cWord)
  9450.                         cChar = this.getChar()
  9451.                         if cChar <> substr(cWord,i,1)
  9452.                                 this.setError("Expected keyword '" + cWord + "'")
  9453.                                 return ''
  9454.                         endif
  9455.                         this.nPos = this.nPos + 1
  9456.                 next
  9457.         return eValue
  9458.        
  9459.  
  9460.         function parseObject()
  9461.         local retval, cPropName, xValue
  9462.                 retval = createObject('myObj')
  9463.                 this.nPos = this.nPos + 1 && Eat {
  9464.                 if this.getToken()<>'}'
  9465.                         do while .t.
  9466.                                 cPropName = this.parseString()
  9467.                                 if not empty(this.cError)
  9468.                                         return null
  9469.                                 endif
  9470.                                 if this.getToken()<>':'
  9471.                                         this.setError("Expected ':' when parsing object")
  9472.                                         return null
  9473.                                 endif
  9474.                                 this.nPos = this.nPos + 1
  9475.                                 xValue = this.parseValue()
  9476.                                 if not empty(this.cError)
  9477.                                         return null
  9478.                                 endif                          
  9479.                                 ** Debug ? cPropName, type('xValue')
  9480.                                 retval.set(cPropName, xValue)
  9481.                                 if this.getToken()<>','
  9482.                                         exit
  9483.                                 endif
  9484.                                 this.nPos = this.nPos + 1
  9485.                         enddo
  9486.                 endif
  9487.                 if this.getToken()<>'}'
  9488.                         this.setError("Expected '}' at the end of object")
  9489.                         return null
  9490.                 endif
  9491.                 this.nPos = this.nPos + 1
  9492.         return retval
  9493.  
  9494.  
  9495.         function parseArray()
  9496.         local retVal, xValue
  9497.                 retval = createObject('MyArray')
  9498.                 this.nPos = this.nPos + 1       && Eat [
  9499.                 if this.getToken() <> ']'
  9500.                         do while .t.
  9501.                                 xValue = this.parseValue()
  9502.                                 if not empty(this.cError)
  9503.                                         return null
  9504.                                 endif
  9505.                                 retval.add( xValue )
  9506.                                 if this.getToken()<>','
  9507.                                         exit
  9508.                                 endif
  9509.                                 this.nPos = this.nPos + 1
  9510.                         enddo
  9511.                         if this.getToken() <> ']'
  9512.                                 this.setError('Expected ] at the end of array')
  9513.                                 return null
  9514.                         endif
  9515.                 endif
  9516.                 this.nPos = this.nPos + 1
  9517.         return retval
  9518.        
  9519.  
  9520.         function parseString()
  9521.         local cRetVal, c
  9522.                 if this.getToken()<>'"'
  9523.                         this.setError('Expected "')
  9524.                         return ''
  9525.                 endif
  9526.                 this.nPos = this.nPos + 1       && Eat "
  9527.                 cRetVal = ''
  9528.                 do while .t.
  9529.                         c = this.getChar()
  9530.                         if c==''
  9531.                                 return ''
  9532.                         endif
  9533.                         if c == '"'
  9534.                                 this.nPos = this.nPos + 1
  9535.                                 exit
  9536.                         endif
  9537.                         if c == '\'
  9538.                                 this.nPos = this.nPos + 1
  9539.                                 if (this.nPos>this.nLen)
  9540.                                         this.setError('\\ at the end of input')
  9541.                                         return ''
  9542.                                 endif
  9543.                                 c = this.getChar()
  9544.                                 if c==''
  9545.                                         return ''
  9546.                                 endif
  9547.                                 do case
  9548.                                 case c=='"'
  9549.                                         c='"'
  9550.                                 case c=='\'
  9551.                                         c='\'
  9552.                                 case c=='/'
  9553.                                         c='/'
  9554.                                 case c=='b'
  9555.                                         c=chr(8)
  9556.                                 case c=='t'
  9557.                                         c=chr(9)
  9558.                                 case c=='n'
  9559.                                         c=chr(10)
  9560.                                 case c=='f'
  9561.                                         c=chr(12)
  9562.                                 case c=='r'
  9563.                                         c=chr(13)
  9564.                                 otherwise
  9565.                                         ******* FALTAN LOS UNICODE
  9566.                                         this.setError('Invalid escape sequence in string literal')
  9567.                                         return ''
  9568.                                 endcase
  9569.                         endif
  9570.                         cRetVal = cRetVal + c
  9571.                         this.nPos = this.nPos + 1
  9572.                 enddo
  9573.         return cRetVal
  9574.                                        
  9575.  
  9576.         **** Pendiente numeros con E
  9577.         function parseNumber()
  9578.         local nStartPos,c, isInt, cNumero
  9579.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  9580.                         this.setError('Expected number literal')
  9581.                         return 0
  9582.                 endif
  9583.                 nStartPos = this.nPos
  9584.                 c = this.getChar()
  9585.                 if c == '-'
  9586.                         c = this.nextChar()
  9587.                 endif
  9588.                 if c == '0'
  9589.                         c = this.nextChar()
  9590.                 else
  9591.                         if isdigit(c)
  9592.                                 c = this.nextChar()
  9593.                                 do while isdigit(c)
  9594.                                         c = this.nextChar()
  9595.                                 enddo
  9596.                         else
  9597.                                 this.setError('Expected digit when parsing number')
  9598.                                 return 0
  9599.                         endif
  9600.                 endif
  9601.                
  9602.                 isInt = .t.
  9603.                 if c=='.'
  9604.                         c = this.nextChar()
  9605.                         if isdigit(c)
  9606.                                 c = this.nextChar()
  9607.                                 isInt = .f.
  9608.                                 do while isDigit(c)
  9609.                                         c = this.nextChar()
  9610.                                 enddo
  9611.                         else
  9612.                                 this.setError('Expected digit following dot comma')
  9613.                                 return 0
  9614.                         endif
  9615.                 endif
  9616.                
  9617.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  9618.         return val(cNumero)
  9619.  
  9620.  
  9621.  
  9622.         function getToken()
  9623.         local char1
  9624.                 do while .t.
  9625.                         if this.nPos > this.nLen
  9626.                                 return null
  9627.                         endif
  9628.                         char1 = substr(this.cJson, this.nPos, 1)
  9629.                         if char1==' '
  9630.                                 this.nPos = this.nPos + 1
  9631.                                 loop
  9632.                         endif
  9633.                         return char1
  9634.                 enddo
  9635.         return
  9636.        
  9637.                
  9638.                
  9639.         function getChar()
  9640.                 if this.nPos > this.nLen
  9641.                         this.setError('Unexpected end of JSON stream')
  9642.                         return ''
  9643.                 endif
  9644.         return substr(this.cJson, this.nPos, 1)
  9645.        
  9646.         function nextChar()
  9647.                 this.nPos = this.nPos + 1
  9648.                 if this.nPos > this.nLen
  9649.                         return ''
  9650.                 endif
  9651.         return substr(this.cJson, this.nPos, 1)
  9652.        
  9653.         function setError(cMsg)
  9654.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  9655.         return
  9656.  
  9657.  
  9658.         function fixUnicode(cStr)
  9659.                 cStr = StrTran(cStr,'\u00e1','á')
  9660.                 cStr = StrTran(cStr,'\u00e9','é')
  9661.                 cStr = StrTran(cStr,'\u00ed','í')
  9662.                 cStr = StrTran(cStr,'\u00f3','ó')
  9663.                 cStr = StrTran(cStr,'\u00fa','ú')
  9664.                 cStr = StrTran(cStr,'\u00c1','Á')
  9665.                 cStr = StrTran(cStr,'\u00c9','É')
  9666.                 cStr = StrTran(cStr,'\u00cd','Í')
  9667.                 cStr = StrTran(cStr,'\u00d3','Ó')
  9668.                 cStr = StrTran(cStr,'\u00da','Ú')
  9669.                 cStr = StrTran(cStr,'\u00f1','ń')
  9670.                 cStr = StrTran(cStr,'\u00d1','Ń')
  9671.         return cStr
  9672.  
  9673.  
  9674.  
  9675. enddefine
  9676.  
  9677.  
  9678.  
  9679.  
  9680.  
  9681. *
  9682. * class used to return an array
  9683. *
  9684. define class myArray as custom
  9685.         nSize = 0
  9686.         dimension array[1]
  9687.  
  9688.         function add(xExpr)
  9689.                 this.nSize = this.nSize + 1
  9690.                 dimension this.array[this.nSize]
  9691.                 this.array[this.nSize] = xExpr
  9692.         return
  9693.  
  9694.         function get(n)
  9695.         return this.array[n]
  9696.  
  9697. enddefine
  9698.  
  9699.  
  9700.  
  9701. *
  9702. * class used to simulate an object
  9703. * all properties are prefixed with 'prop' to permit property names like: error, init
  9704. * that already exists like vfp methods
  9705. *
  9706. define class myObj as custom
  9707. Hidden ;
  9708.         ClassLibrary,Comment, ;
  9709.         BaseClass,ControlCount, ;
  9710.         Controls,Objects,Object,;
  9711.         Height,HelpContextID,Left,Name, ;
  9712.         Parent,ParentClass,Picture, ;
  9713.         Tag,Top,WhatsThisHelpID,Width
  9714.                
  9715.         function set(cPropName, xValue)
  9716.                 cPropName = '_'+cPropName
  9717.                 if type('this.'+cPropName)=='U'
  9718.                         this.addProperty(cPropName,xValue)
  9719.                 else
  9720.                         local cmd
  9721.                         cmd = 'this.'+cPropName+'=xValue'
  9722.                         &cmd
  9723.                 endif
  9724.         return
  9725.        
  9726.         procedure get (cPropName)
  9727.                 cPropName = '_'+cPropName
  9728.                 If type('this.'+cPropName)=='U'
  9729.                         return ''
  9730.                 Else
  9731.                         local cmd
  9732.                         cmd = 'return this.'+cPropName
  9733.                         &cmd
  9734.                 endif
  9735.         return ''
  9736. enddefine
  9737.  
  9738.  
  9739.  
  9740.  
  9741.  
  9742. function testJsonClass
  9743.         clear
  9744.         set decimal to 10
  9745.         oJson = newObject('json')
  9746.        
  9747.        
  9748.         ? 'Test Basic Types'
  9749.         ? '----------------'
  9750.         ? oJson.decode('null')
  9751.         ? oJson.decode('true')
  9752.         ? oJson.decode('false')
  9753.         ?
  9754.         ? oJson.decode('791123')
  9755.         ? oJson.decode('791123.45')
  9756.         ? oJson.decode('791123.45.')
  9757.         ? oJson.decode('"nacho gtz"')
  9758.         if not empty(oJson.cError)
  9759.                 ? oJson.cError
  9760.                 return
  9761.         endif
  9762.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  9763.         if not empty(oJson.cError)
  9764.                 ? oJson.cError
  9765.                 return
  9766.         endif
  9767.        
  9768.         ? 'Test Array'
  9769.         ? '----------'
  9770.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  9771.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  9772.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  9773.         nombres = arr.get(1)
  9774.         edades  = arr.get(2)
  9775.         ? nombres.get(1), edades.get(1)
  9776.         ? nombres.get(2), edades.get(2)
  9777.         ? nombres.get(3), edades.get(3)
  9778.         ?
  9779.         ? 'Test Object'
  9780.         ? '-----------'
  9781.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  9782.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  9783.         ? obj._Nombre, obj._Edad, obj._IsGood
  9784.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  9785.         ? obj.get('jsonrpc'), obj._jsonrpc
  9786.         ? obj.get('id'), obj._id
  9787.         ? obj.get('method'), obj._method
  9788.         ? obj._Params.array[1], obj._Params.get(1)
  9789.  
  9790.         ?
  9791.         ? 'Test nested object'
  9792.         ? '------------------'
  9793.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  9794.         obj = oJson.decode(cJson)
  9795.         if not empty(oJson.cError)
  9796.                 ? oJson.cError
  9797.                 return
  9798.         endif
  9799.         ? cJson
  9800.         ? 'method -->',obj._method
  9801.         ? 'usrkey -->',obj._params._data._usrkey
  9802.         ? 'sendto -->',obj._params._data._sendto
  9803.         ? 'name  --->',obj._params._data._name
  9804.         ? 'expires ->',obj._params._data._expires
  9805.  
  9806.         ?
  9807.         ? 'Test empty object'
  9808.         ? '-----------------'
  9809.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  9810.         obj = oJson.decode(cJson)
  9811.         if not empty(oJson.cError)     
  9812.                 ? oJson.cError
  9813.                 return
  9814.         endif
  9815.         ? cJson
  9816.         ? 'result -->',obj._result, obj.get('result')
  9817.         oError = obj.get('error')
  9818.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  9819.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  9820.         ? 'id  ----->',obj._id, obj.get('id')
  9821.         ?  type("oError._code")
  9822.  
  9823.         ?
  9824.         ? 'Probar decode-enconde-decode-encode'
  9825.         ? '------------------------------------'
  9826.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  9827.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  9828.         ? cJson
  9829.         oSmtp = json_decode(cJson)
  9830.         cJson =  json_encode(oSmtp)
  9831.         ? cJson
  9832.         oSmtp = json_decode(cJson)
  9833.         cJson =  json_encode(oSmtp)
  9834.         ? cJson
  9835.  
  9836.         * Probar falla
  9837.         ?
  9838.         ? 'Probar una falla en el json'
  9839.         ? '---------------------------'
  9840.         cJson = ' {"server":"", "user":"", "password":"" ,'
  9841.         oSmtp = json_decode(cJson)
  9842.         if not empty(json_getErrorMsg())
  9843.                 ? json_getErrorMsg()
  9844.         endif
  9845.  
  9846.         ?
  9847.         ? 'Pruebas Finalizadas'
  9848. retur
  9849. *
  9850. * Codificar y Decodificar JSON
  9851. *
  9852. * Puedes usar las funciones:
  9853. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  9854. *               json_decode(cJson)  te regresa el objeto representado en cJson
  9855. *
  9856. * Tambien puedes usar directamente la clase:
  9857. *
  9858. *       oJson = newobject('json','json.prg')
  9859. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  9860. *       ? oJson.encode(oCliente)
  9861. *       ? oCliente.get('nombre')
  9862. *       ? oCliente.get('apellido')
  9863. *
  9864. *
  9865. * VFPJSON  Encode and Decode JSON for VFP
  9866. * Examples:
  9867. *       oJson = newobject('json','json.prg')
  9868. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  9869. *       ? oJson.encode(oCustomer)
  9870. *       ? oCustomer.get('name')
  9871. *       ? oCustomer.get('lastname')
  9872. *
  9873. *
  9874. lRunTest = .f.
  9875. if lRunTest
  9876.         testJsonClass()
  9877. endif
  9878. return
  9879.  
  9880.  
  9881. function json_encode(xExpr)
  9882.         if vartype(_json)<>'O'
  9883.                 public _json
  9884.                 _json = newobject('json')
  9885.         endif
  9886. return _json.encode(@xExpr)
  9887.  
  9888.  
  9889. function json_decode(cJson)
  9890. local retval
  9891.         if vartype(_json)<>'O'
  9892.                 public _json
  9893.                 _json = newobject('json')
  9894.         endif
  9895.         retval = _json.decode(cJson)
  9896.         if not empty(_json.cError)
  9897.                 return null
  9898.         endif
  9899. return retval
  9900.  
  9901. function json_getErrorMsg()
  9902. return _json.cError
  9903.        
  9904.  
  9905.  
  9906. *
  9907. * json class
  9908. *
  9909. *
  9910. define class json as custom
  9911.  
  9912.  
  9913.         nPos=0
  9914.         nLen=0
  9915.         cJson=''
  9916.         cError=''
  9917.  
  9918.  
  9919.         *
  9920.         * Genera el codigo cJson para parametro que se manda
  9921.         *
  9922.         function encode(xExpr)
  9923.         local cTipo
  9924.                 * Cuando se manda una arreglo,
  9925.                 if type('ALen(xExpr)')=='N'
  9926.                         cTipo = 'A'
  9927.                 Else
  9928.                         cTipo = VarType(xExpr)
  9929.                 Endif
  9930.                
  9931.                 Do Case
  9932.                 Case cTipo=='D'
  9933.                         return '"'+dtos(xExpr)+'"'
  9934.                 Case cTipo=='N'
  9935.                         return Transform(xExpr)
  9936.                 Case cTipo=='L'
  9937.                         return iif(xExpr,'true','false')
  9938.                 Case cTipo=='X'
  9939.                         return 'null'
  9940.                 Case cTipo=='C'
  9941.                         xExpr = allt(xExpr)
  9942.                         xExpr = StrTran(xExpr, '\', '\\' )
  9943.                         xExpr = StrTran(xExpr, '/', '\/' )
  9944.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  9945.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  9946.                         xExpr = StrTran(xExpr, '"', '\"' )
  9947.                         return '"'+xExpr+'"'
  9948.  
  9949.                 case cTipo=='O'
  9950.                         local cProp, cJsonValue, cRetVal, aProp[1]
  9951.                         =AMembers(aProp,xExpr)
  9952.                         cRetVal = ''
  9953.                         for each cProp in aProp
  9954.                                 *? cProp
  9955.                                 *? cRetVal
  9956.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  9957.                                         * algunas propiedades pueden no estar definidas
  9958.                                         * como: activecontrol, parent, etc
  9959.                                         loop
  9960.                                 endif
  9961.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  9962.                                         *
  9963.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  9964.                                         *
  9965.                                         Local i,nTotElem
  9966.                                         cJsonValue = ''
  9967.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  9968.                                         For i=1 to nTotElem
  9969.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  9970.                                         Next
  9971.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  9972.                                 else
  9973.                                         *
  9974.                                         * es otro tipo de dato normal C, N, L
  9975.                                         *
  9976.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  9977.                                 endif
  9978.                                 if left(cProp,1)=='_'
  9979.                                         cProp = substr(cProp,2)
  9980.                                 endif
  9981.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  9982.                         next
  9983.                         return '{' + substr(cRetVal,2) + '}'
  9984.  
  9985.                 case cTipo=='A'
  9986.                         local valor, cRetVal
  9987.                         cRetVal = ''   
  9988.                         for each valor in xExpr
  9989.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  9990.                         next
  9991.                         return  '[' + substr(cRetVal,2) + ']'
  9992.                        
  9993.                 endcase
  9994.  
  9995.         return ''
  9996.  
  9997.  
  9998.  
  9999.  
  10000.  
  10001.         *
  10002.         * regresa un elemento representado por la cadena json que se manda
  10003.         *
  10004.        
  10005.         function decode(cJson)
  10006.         local retValue
  10007.                 cJson = StrTran(cJson,chr(9),'')
  10008.                 cJson = StrTran(cJson,chr(10),'')
  10009.                 cJson = StrTran(cJson,chr(13),'')
  10010.                 cJson = this.fixUnicode(cJson)
  10011.                 this.nPos  = 1
  10012.                 this.cJson = cJson
  10013.                 this.nLen  = len(cJson)
  10014.                 this.cError = ''
  10015.                 retValue = this.parsevalue()
  10016.                 if not empty(this.cError)
  10017.                         return null
  10018.                 endif
  10019.                 if this.getToken()<>null
  10020.                         this.setError('Junk at the end of JSON input')
  10021.                         return null
  10022.                 endif
  10023.         return retValue
  10024.                
  10025.        
  10026.         function parseValue()
  10027.         local token
  10028.                 token = this.getToken()
  10029.                 if token==null
  10030.                         this.setError('Nothing to parse')
  10031.                         return null
  10032.                 endif
  10033.                 do case
  10034.                 case token=='"'
  10035.                         return this.parseString()
  10036.                 case isdigit(token) or token=='-'
  10037.                         return this.parseNumber()
  10038.                 case token=='n'
  10039.                         return this.expectedKeyword('null',null)
  10040.                 case token=='f'
  10041.                         return this.expectedKeyword('false',.f.)
  10042.                 case token=='t'
  10043.                         return this.expectedKeyword('true',.t.)
  10044.                 case token=='{'
  10045.                         return this.parseObject()
  10046.                 case token=='['
  10047.                         return this.parseArray()
  10048.                 otherwise
  10049.                         this.setError('Unexpected token')
  10050.                 endcase
  10051.         return
  10052.                
  10053.        
  10054.         function expectedKeyword(cWord,eValue)
  10055.                 for i=1 to len(cWord)
  10056.                         cChar = this.getChar()
  10057.                         if cChar <> substr(cWord,i,1)
  10058.                                 this.setError("Expected keyword '" + cWord + "'")
  10059.                                 return ''
  10060.                         endif
  10061.                         this.nPos = this.nPos + 1
  10062.                 next
  10063.         return eValue
  10064.        
  10065.  
  10066.         function parseObject()
  10067.         local retval, cPropName, xValue
  10068.                 retval = createObject('myObj')
  10069.                 this.nPos = this.nPos + 1 && Eat {
  10070.                 if this.getToken()<>'}'
  10071.                         do while .t.
  10072.                                 cPropName = this.parseString()
  10073.                                 if not empty(this.cError)
  10074.                                         return null
  10075.                                 endif
  10076.                                 if this.getToken()<>':'
  10077.                                         this.setError("Expected ':' when parsing object")
  10078.                                         return null
  10079.                                 endif
  10080.                                 this.nPos = this.nPos + 1
  10081.                                 xValue = this.parseValue()
  10082.                                 if not empty(this.cError)
  10083.                                         return null
  10084.                                 endif                          
  10085.                                 ** Debug ? cPropName, type('xValue')
  10086.                                 retval.set(cPropName, xValue)
  10087.                                 if this.getToken()<>','
  10088.                                         exit
  10089.                                 endif
  10090.                                 this.nPos = this.nPos + 1
  10091.                         enddo
  10092.                 endif
  10093.                 if this.getToken()<>'}'
  10094.                         this.setError("Expected '}' at the end of object")
  10095.                         return null
  10096.                 endif
  10097.                 this.nPos = this.nPos + 1
  10098.         return retval
  10099.  
  10100.  
  10101.         function parseArray()
  10102.         local retVal, xValue
  10103.                 retval = createObject('MyArray')
  10104.                 this.nPos = this.nPos + 1       && Eat [
  10105.                 if this.getToken() <> ']'
  10106.                         do while .t.
  10107.                                 xValue = this.parseValue()
  10108.                                 if not empty(this.cError)
  10109.                                         return null
  10110.                                 endif
  10111.                                 retval.add( xValue )
  10112.                                 if this.getToken()<>','
  10113.                                         exit
  10114.                                 endif
  10115.                                 this.nPos = this.nPos + 1
  10116.                         enddo
  10117.                         if this.getToken() <> ']'
  10118.                                 this.setError('Expected ] at the end of array')
  10119.                                 return null
  10120.                         endif
  10121.                 endif
  10122.                 this.nPos = this.nPos + 1
  10123.         return retval
  10124.        
  10125.  
  10126.         function parseString()
  10127.         local cRetVal, c
  10128.                 if this.getToken()<>'"'
  10129.                         this.setError('Expected "')
  10130.                         return ''
  10131.                 endif
  10132.                 this.nPos = this.nPos + 1       && Eat "
  10133.                 cRetVal = ''
  10134.                 do while .t.
  10135.                         c = this.getChar()
  10136.                         if c==''
  10137.                                 return ''
  10138.                         endif
  10139.                         if c == '"'
  10140.                                 this.nPos = this.nPos + 1
  10141.                                 exit
  10142.                         endif
  10143.                         if c == '\'
  10144.                                 this.nPos = this.nPos + 1
  10145.                                 if (this.nPos>this.nLen)
  10146.                                         this.setError('\\ at the end of input')
  10147.                                         return ''
  10148.                                 endif
  10149.                                 c = this.getChar()
  10150.                                 if c==''
  10151.                                         return ''
  10152.                                 endif
  10153.                                 do case
  10154.                                 case c=='"'
  10155.                                         c='"'
  10156.                                 case c=='\'
  10157.                                         c='\'
  10158.                                 case c=='/'
  10159.                                         c='/'
  10160.                                 case c=='b'
  10161.                                         c=chr(8)
  10162.                                 case c=='t'
  10163.                                         c=chr(9)
  10164.                                 case c=='n'
  10165.                                         c=chr(10)
  10166.                                 case c=='f'
  10167.                                         c=chr(12)
  10168.                                 case c=='r'
  10169.                                         c=chr(13)
  10170.                                 otherwise
  10171.                                         ******* FALTAN LOS UNICODE
  10172.                                         this.setError('Invalid escape sequence in string literal')
  10173.                                         return ''
  10174.                                 endcase
  10175.                         endif
  10176.                         cRetVal = cRetVal + c
  10177.                         this.nPos = this.nPos + 1
  10178.                 enddo
  10179.         return cRetVal
  10180.                                        
  10181.  
  10182.         **** Pendiente numeros con E
  10183.         function parseNumber()
  10184.         local nStartPos,c, isInt, cNumero
  10185.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  10186.                         this.setError('Expected number literal')
  10187.                         return 0
  10188.                 endif
  10189.                 nStartPos = this.nPos
  10190.                 c = this.getChar()
  10191.                 if c == '-'
  10192.                         c = this.nextChar()
  10193.                 endif
  10194.                 if c == '0'
  10195.                         c = this.nextChar()
  10196.                 else
  10197.                         if isdigit(c)
  10198.                                 c = this.nextChar()
  10199.                                 do while isdigit(c)
  10200.                                         c = this.nextChar()
  10201.                                 enddo
  10202.                         else
  10203.                                 this.setError('Expected digit when parsing number')
  10204.                                 return 0
  10205.                         endif
  10206.                 endif
  10207.                
  10208.                 isInt = .t.
  10209.                 if c=='.'
  10210.                         c = this.nextChar()
  10211.                         if isdigit(c)
  10212.                                 c = this.nextChar()
  10213.                                 isInt = .f.
  10214.                                 do while isDigit(c)
  10215.                                         c = this.nextChar()
  10216.                                 enddo
  10217.                         else
  10218.                                 this.setError('Expected digit following dot comma')
  10219.                                 return 0
  10220.                         endif
  10221.                 endif
  10222.                
  10223.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  10224.         return val(cNumero)
  10225.  
  10226.  
  10227.  
  10228.         function getToken()
  10229.         local char1
  10230.                 do while .t.
  10231.                         if this.nPos > this.nLen
  10232.                                 return null
  10233.                         endif
  10234.                         char1 = substr(this.cJson, this.nPos, 1)
  10235.                         if char1==' '
  10236.                                 this.nPos = this.nPos + 1
  10237.                                 loop
  10238.                         endif
  10239.                         return char1
  10240.                 enddo
  10241.         return
  10242.        
  10243.                
  10244.                
  10245.         function getChar()
  10246.                 if this.nPos > this.nLen
  10247.                         this.setError('Unexpected end of JSON stream')
  10248.                         return ''
  10249.                 endif
  10250.         return substr(this.cJson, this.nPos, 1)
  10251.        
  10252.         function nextChar()
  10253.                 this.nPos = this.nPos + 1
  10254.                 if this.nPos > this.nLen
  10255.                         return ''
  10256.                 endif
  10257.         return substr(this.cJson, this.nPos, 1)
  10258.        
  10259.         function setError(cMsg)
  10260.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  10261.         return
  10262.  
  10263.  
  10264.         function fixUnicode(cStr)
  10265.                 cStr = StrTran(cStr,'\u00e1','á')
  10266.                 cStr = StrTran(cStr,'\u00e9','é')
  10267.                 cStr = StrTran(cStr,'\u00ed','í')
  10268.                 cStr = StrTran(cStr,'\u00f3','ó')
  10269.                 cStr = StrTran(cStr,'\u00fa','ú')
  10270.                 cStr = StrTran(cStr,'\u00c1','Á')
  10271.                 cStr = StrTran(cStr,'\u00c9','É')
  10272.                 cStr = StrTran(cStr,'\u00cd','Í')
  10273.                 cStr = StrTran(cStr,'\u00d3','Ó')
  10274.                 cStr = StrTran(cStr,'\u00da','Ú')
  10275.                 cStr = StrTran(cStr,'\u00f1','ń')
  10276.                 cStr = StrTran(cStr,'\u00d1','Ń')
  10277.         return cStr
  10278.  
  10279.  
  10280.  
  10281. enddefine
  10282.  
  10283.  
  10284.  
  10285.  
  10286.  
  10287. *
  10288. * class used to return an array
  10289. *
  10290. define class myArray as custom
  10291.         nSize = 0
  10292.         dimension array[1]
  10293.  
  10294.         function add(xExpr)
  10295.                 this.nSize = this.nSize + 1
  10296.                 dimension this.array[this.nSize]
  10297.                 this.array[this.nSize] = xExpr
  10298.         return
  10299.  
  10300.         function get(n)
  10301.         return this.array[n]
  10302.  
  10303. enddefine
  10304.  
  10305.  
  10306.  
  10307. *
  10308. * class used to simulate an object
  10309. * all properties are prefixed with 'prop' to permit property names like: error, init
  10310. * that already exists like vfp methods
  10311. *
  10312. define class myObj as custom
  10313. Hidden ;
  10314.         ClassLibrary,Comment, ;
  10315.         BaseClass,ControlCount, ;
  10316.         Controls,Objects,Object,;
  10317.         Height,HelpContextID,Left,Name, ;
  10318.         Parent,ParentClass,Picture, ;
  10319.         Tag,Top,WhatsThisHelpID,Width
  10320.                
  10321.         function set(cPropName, xValue)
  10322.                 cPropName = '_'+cPropName
  10323.                 if type('this.'+cPropName)=='U'
  10324.                         this.addProperty(cPropName,xValue)
  10325.                 else
  10326.                         local cmd
  10327.                         cmd = 'this.'+cPropName+'=xValue'
  10328.                         &cmd
  10329.                 endif
  10330.         return
  10331.        
  10332.         procedure get (cPropName)
  10333.                 cPropName = '_'+cPropName
  10334.                 If type('this.'+cPropName)=='U'
  10335.                         return ''
  10336.                 Else
  10337.                         local cmd
  10338.                         cmd = 'return this.'+cPropName
  10339.                         &cmd
  10340.                 endif
  10341.         return ''
  10342. enddefine
  10343.  
  10344.  
  10345.  
  10346.  
  10347.  
  10348. function testJsonClass
  10349.         clear
  10350.         set decimal to 10
  10351.         oJson = newObject('json')
  10352.        
  10353.        
  10354.         ? 'Test Basic Types'
  10355.         ? '----------------'
  10356.         ? oJson.decode('null')
  10357.         ? oJson.decode('true')
  10358.         ? oJson.decode('false')
  10359.         ?
  10360.         ? oJson.decode('791123')
  10361.         ? oJson.decode('791123.45')
  10362.         ? oJson.decode('791123.45.')
  10363.         ? oJson.decode('"nacho gtz"')
  10364.         if not empty(oJson.cError)
  10365.                 ? oJson.cError
  10366.                 return
  10367.         endif
  10368.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  10369.         if not empty(oJson.cError)
  10370.                 ? oJson.cError
  10371.                 return
  10372.         endif
  10373.        
  10374.         ? 'Test Array'
  10375.         ? '----------'
  10376.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  10377.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  10378.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  10379.         nombres = arr.get(1)
  10380.         edades  = arr.get(2)
  10381.         ? nombres.get(1), edades.get(1)
  10382.         ? nombres.get(2), edades.get(2)
  10383.         ? nombres.get(3), edades.get(3)
  10384.         ?
  10385.         ? 'Test Object'
  10386.         ? '-----------'
  10387.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  10388.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  10389.         ? obj._Nombre, obj._Edad, obj._IsGood
  10390.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  10391.         ? obj.get('jsonrpc'), obj._jsonrpc
  10392.         ? obj.get('id'), obj._id
  10393.         ? obj.get('method'), obj._method
  10394.         ? obj._Params.array[1], obj._Params.get(1)
  10395.  
  10396.         ?
  10397.         ? 'Test nested object'
  10398.         ? '------------------'
  10399.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  10400.         obj = oJson.decode(cJson)
  10401.         if not empty(oJson.cError)
  10402.                 ? oJson.cError
  10403.                 return
  10404.         endif
  10405.         ? cJson
  10406.         ? 'method -->',obj._method
  10407.         ? 'usrkey -->',obj._params._data._usrkey
  10408.         ? 'sendto -->',obj._params._data._sendto
  10409.         ? 'name  --->',obj._params._data._name
  10410.         ? 'expires ->',obj._params._data._expires
  10411.  
  10412.         ?
  10413.         ? 'Test empty object'
  10414.         ? '-----------------'
  10415.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  10416.         obj = oJson.decode(cJson)
  10417.         if not empty(oJson.cError)     
  10418.                 ? oJson.cError
  10419.                 return
  10420.         endif
  10421.         ? cJson
  10422.         ? 'result -->',obj._result, obj.get('result')
  10423.         oError = obj.get('error')
  10424.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  10425.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  10426.         ? 'id  ----->',obj._id, obj.get('id')
  10427.         ?  type("oError._code")
  10428.  
  10429.         ?
  10430.         ? 'Probar decode-enconde-decode-encode'
  10431.         ? '------------------------------------'
  10432.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  10433.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  10434.         ? cJson
  10435.         oSmtp = json_decode(cJson)
  10436.         cJson =  json_encode(oSmtp)
  10437.         ? cJson
  10438.         oSmtp = json_decode(cJson)
  10439.         cJson =  json_encode(oSmtp)
  10440.         ? cJson
  10441.  
  10442.         * Probar falla
  10443.         ?
  10444.         ? 'Probar una falla en el json'
  10445.         ? '---------------------------'
  10446.         cJson = ' {"server":"", "user":"", "password":"" ,'
  10447.         oSmtp = json_decode(cJson)
  10448.         if not empty(json_getErrorMsg())
  10449.                 ? json_getErrorMsg()
  10450.         endif
  10451.  
  10452.         ?
  10453.         ? 'Pruebas Finalizadas'
  10454. retur
  10455. * Codificar y Decodificar JSON
  10456. *
  10457. * Puedes usar las funciones:
  10458. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  10459. *               json_decode(cJson)  te regresa el objeto representado en cJson
  10460. *
  10461. * Tambien puedes usar directamente la clase:
  10462. *
  10463. *       oJson = newobject('json','json.prg')
  10464. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  10465. *       ? oJson.encode(oCliente)
  10466. *       ? oCliente.get('nombre')
  10467. *       ? oCliente.get('apellido')
  10468. *
  10469. *
  10470. * VFPJSON  Encode and Decode JSON for VFP
  10471. * Examples:
  10472. *       oJson = newobject('json','json.prg')
  10473. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  10474. *       ? oJson.encode(oCustomer)
  10475. *       ? oCustomer.get('name')
  10476. *       ? oCustomer.get('lastname')
  10477. *
  10478. *
  10479. lRunTest = .f.
  10480. if lRunTest
  10481.         testJsonClass()
  10482. endif
  10483. return
  10484.  
  10485.  
  10486. function json_encode(xExpr)
  10487.         if vartype(_json)<>'O'
  10488.                 public _json
  10489.                 _json = newobject('json')
  10490.         endif
  10491. return _json.encode(@xExpr)
  10492.  
  10493.  
  10494. function json_decode(cJson)
  10495. local retval
  10496.         if vartype(_json)<>'O'
  10497.                 public _json
  10498.                 _json = newobject('json')
  10499.         endif
  10500.         retval = _json.decode(cJson)
  10501.         if not empty(_json.cError)
  10502.                 return null
  10503.         endif
  10504. return retval
  10505.  
  10506. function json_getErrorMsg()
  10507. return _json.cError
  10508.        
  10509.  
  10510.  
  10511. *
  10512. * json class
  10513. *
  10514. *
  10515. define class json as custom
  10516.  
  10517.  
  10518.         nPos=0
  10519.         nLen=0
  10520.         cJson=''
  10521.         cError=''
  10522.  
  10523.  
  10524.         *
  10525.         * Genera el codigo cJson para parametro que se manda
  10526.         *
  10527.         function encode(xExpr)
  10528.         local cTipo
  10529.                 * Cuando se manda una arreglo,
  10530.                 if type('ALen(xExpr)')=='N'
  10531.                         cTipo = 'A'
  10532.                 Else
  10533.                         cTipo = VarType(xExpr)
  10534.                 Endif
  10535.                
  10536.                 Do Case
  10537.                 Case cTipo=='D'
  10538.                         return '"'+dtos(xExpr)+'"'
  10539.                 Case cTipo=='N'
  10540.                         return Transform(xExpr)
  10541.                 Case cTipo=='L'
  10542.                         return iif(xExpr,'true','false')
  10543.                 Case cTipo=='X'
  10544.                         return 'null'
  10545.                 Case cTipo=='C'
  10546.                         xExpr = allt(xExpr)
  10547.                         xExpr = StrTran(xExpr, '\', '\\' )
  10548.                         xExpr = StrTran(xExpr, '/', '\/' )
  10549.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  10550.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  10551.                         xExpr = StrTran(xExpr, '"', '\"' )
  10552.                         return '"'+xExpr+'"'
  10553.  
  10554.                 case cTipo=='O'
  10555.                         local cProp, cJsonValue, cRetVal, aProp[1]
  10556.                         =AMembers(aProp,xExpr)
  10557.                         cRetVal = ''
  10558.                         for each cProp in aProp
  10559.                                 *? cProp
  10560.                                 *? cRetVal
  10561.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  10562.                                         * algunas propiedades pueden no estar definidas
  10563.                                         * como: activecontrol, parent, etc
  10564.                                         loop
  10565.                                 endif
  10566.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  10567.                                         *
  10568.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  10569.                                         *
  10570.                                         Local i,nTotElem
  10571.                                         cJsonValue = ''
  10572.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  10573.                                         For i=1 to nTotElem
  10574.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  10575.                                         Next
  10576.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  10577.                                 else
  10578.                                         *
  10579.                                         * es otro tipo de dato normal C, N, L
  10580.                                         *
  10581.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  10582.                                 endif
  10583.                                 if left(cProp,1)=='_'
  10584.                                         cProp = substr(cProp,2)
  10585.                                 endif
  10586.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  10587.                         next
  10588.                         return '{' + substr(cRetVal,2) + '}'
  10589.  
  10590.                 case cTipo=='A'
  10591.                         local valor, cRetVal
  10592.                         cRetVal = ''   
  10593.                         for each valor in xExpr
  10594.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  10595.                         next
  10596.                         return  '[' + substr(cRetVal,2) + ']'
  10597.                        
  10598.                 endcase
  10599.  
  10600.         return ''
  10601.  
  10602.  
  10603.  
  10604.  
  10605.  
  10606.         *
  10607.         * regresa un elemento representado por la cadena json que se manda
  10608.         *
  10609.        
  10610.         function decode(cJson)
  10611.         local retValue
  10612.                 cJson = StrTran(cJson,chr(9),'')
  10613.                 cJson = StrTran(cJson,chr(10),'')
  10614.                 cJson = StrTran(cJson,chr(13),'')
  10615.                 cJson = this.fixUnicode(cJson)
  10616.                 this.nPos  = 1
  10617.                 this.cJson = cJson
  10618.                 this.nLen  = len(cJson)
  10619.                 this.cError = ''
  10620.                 retValue = this.parsevalue()
  10621.                 if not empty(this.cError)
  10622.                         return null
  10623.                 endif
  10624.                 if this.getToken()<>null
  10625.                         this.setError('Junk at the end of JSON input')
  10626.                         return null
  10627.                 endif
  10628.         return retValue
  10629.                
  10630.        
  10631.         function parseValue()
  10632.         local token
  10633.                 token = this.getToken()
  10634.                 if token==null
  10635.                         this.setError('Nothing to parse')
  10636.                         return null
  10637.                 endif
  10638.                 do case
  10639.                 case token=='"'
  10640.                         return this.parseString()
  10641.                 case isdigit(token) or token=='-'
  10642.                         return this.parseNumber()
  10643.                 case token=='n'
  10644.                         return this.expectedKeyword('null',null)
  10645.                 case token=='f'
  10646.                         return this.expectedKeyword('false',.f.)
  10647.                 case token=='t'
  10648.                         return this.expectedKeyword('true',.t.)
  10649.                 case token=='{'
  10650.                         return this.parseObject()
  10651.                 case token=='['
  10652.                         return this.parseArray()
  10653.                 otherwise
  10654.                         this.setError('Unexpected token')
  10655.                 endcase
  10656.         return
  10657.                
  10658.        
  10659.         function expectedKeyword(cWord,eValue)
  10660.                 for i=1 to len(cWord)
  10661.                         cChar = this.getChar()
  10662.                         if cChar <> substr(cWord,i,1)
  10663.                                 this.setError("Expected keyword '" + cWord + "'")
  10664.                                 return ''
  10665.                         endif
  10666.                         this.nPos = this.nPos + 1
  10667.                 next
  10668.         return eValue
  10669.        
  10670.  
  10671.         function parseObject()
  10672.         local retval, cPropName, xValue
  10673.                 retval = createObject('myObj')
  10674.                 this.nPos = this.nPos + 1 && Eat {
  10675.                 if this.getToken()<>'}'
  10676.                         do while .t.
  10677.                                 cPropName = this.parseString()
  10678.                                 if not empty(this.cError)
  10679.                                         return null
  10680.                                 endif
  10681.                                 if this.getToken()<>':'
  10682.                                         this.setError("Expected ':' when parsing object")
  10683.                                         return null
  10684.                                 endif
  10685.                                 this.nPos = this.nPos + 1
  10686.                                 xValue = this.parseValue()
  10687.                                 if not empty(this.cError)
  10688.                                         return null
  10689.                                 endif                          
  10690.                                 ** Debug ? cPropName, type('xValue')
  10691.                                 retval.set(cPropName, xValue)
  10692.                                 if this.getToken()<>','
  10693.                                         exit
  10694.                                 endif
  10695.                                 this.nPos = this.nPos + 1
  10696.                         enddo
  10697.                 endif
  10698.                 if this.getToken()<>'}'
  10699.                         this.setError("Expected '}' at the end of object")
  10700.                         return null
  10701.                 endif
  10702.                 this.nPos = this.nPos + 1
  10703.         return retval
  10704.  
  10705.  
  10706.         function parseArray()
  10707.         local retVal, xValue
  10708.                 retval = createObject('MyArray')
  10709.                 this.nPos = this.nPos + 1       && Eat [
  10710.                 if this.getToken() <> ']'
  10711.                         do while .t.
  10712.                                 xValue = this.parseValue()
  10713.                                 if not empty(this.cError)
  10714.                                         return null
  10715.                                 endif
  10716.                                 retval.add( xValue )
  10717.                                 if this.getToken()<>','
  10718.                                         exit
  10719.                                 endif
  10720.                                 this.nPos = this.nPos + 1
  10721.                         enddo
  10722.                         if this.getToken() <> ']'
  10723.                                 this.setError('Expected ] at the end of array')
  10724.                                 return null
  10725.                         endif
  10726.                 endif
  10727.                 this.nPos = this.nPos + 1
  10728.         return retval
  10729.        
  10730.  
  10731.         function parseString()
  10732.         local cRetVal, c
  10733.                 if this.getToken()<>'"'
  10734.                         this.setError('Expected "')
  10735.                         return ''
  10736.                 endif
  10737.                 this.nPos = this.nPos + 1       && Eat "
  10738.                 cRetVal = ''
  10739.                 do while .t.
  10740.                         c = this.getChar()
  10741.                         if c==''
  10742.                                 return ''
  10743.                         endif
  10744.                         if c == '"'
  10745.                                 this.nPos = this.nPos + 1
  10746.                                 exit
  10747.                         endif
  10748.                         if c == '\'
  10749.                                 this.nPos = this.nPos + 1
  10750.                                 if (this.nPos>this.nLen)
  10751.                                         this.setError('\\ at the end of input')
  10752.                                         return ''
  10753.                                 endif
  10754.                                 c = this.getChar()
  10755.                                 if c==''
  10756.                                         return ''
  10757.                                 endif
  10758.                                 do case
  10759.                                 case c=='"'
  10760.                                         c='"'
  10761.                                 case c=='\'
  10762.                                         c='\'
  10763.                                 case c=='/'
  10764.                                         c='/'
  10765.                                 case c=='b'
  10766.                                         c=chr(8)
  10767.                                 case c=='t'
  10768.                                         c=chr(9)
  10769.                                 case c=='n'
  10770.                                         c=chr(10)
  10771.                                 case c=='f'
  10772.                                         c=chr(12)
  10773.                                 case c=='r'
  10774.                                         c=chr(13)
  10775.                                 otherwise
  10776.                                         ******* FALTAN LOS UNICODE
  10777.                                         this.setError('Invalid escape sequence in string literal')
  10778.                                         return ''
  10779.                                 endcase
  10780.                         endif
  10781.                         cRetVal = cRetVal + c
  10782.                         this.nPos = this.nPos + 1
  10783.                 enddo
  10784.         return cRetVal
  10785.                                        
  10786.  
  10787.         **** Pendiente numeros con E
  10788.         function parseNumber()
  10789.         local nStartPos,c, isInt, cNumero
  10790.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  10791.                         this.setError('Expected number literal')
  10792.                         return 0
  10793.                 endif
  10794.                 nStartPos = this.nPos
  10795.                 c = this.getChar()
  10796.                 if c == '-'
  10797.                         c = this.nextChar()
  10798.                 endif
  10799.                 if c == '0'
  10800.                         c = this.nextChar()
  10801.                 else
  10802.                         if isdigit(c)
  10803.                                 c = this.nextChar()
  10804.                                 do while isdigit(c)
  10805.                                         c = this.nextChar()
  10806.                                 enddo
  10807.                         else
  10808.                                 this.setError('Expected digit when parsing number')
  10809.                                 return 0
  10810.                         endif
  10811.                 endif
  10812.                
  10813.                 isInt = .t.
  10814.                 if c=='.'
  10815.                         c = this.nextChar()
  10816.                         if isdigit(c)
  10817.                                 c = this.nextChar()
  10818.                                 isInt = .f.
  10819.                                 do while isDigit(c)
  10820.                                         c = this.nextChar()
  10821.                                 enddo
  10822.                         else
  10823.                                 this.setError('Expected digit following dot comma')
  10824.                                 return 0
  10825.                         endif
  10826.                 endif
  10827.                
  10828.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  10829.         return val(cNumero)
  10830.  
  10831.  
  10832.  
  10833.         function getToken()
  10834.         local char1
  10835.                 do while .t.
  10836.                         if this.nPos > this.nLen
  10837.                                 return null
  10838.                         endif
  10839.                         char1 = substr(this.cJson, this.nPos, 1)
  10840.                         if char1==' '
  10841.                                 this.nPos = this.nPos + 1
  10842.                                 loop
  10843.                         endif
  10844.                         return char1
  10845.                 enddo
  10846.         return
  10847.        
  10848.                
  10849.                
  10850.         function getChar()
  10851.                 if this.nPos > this.nLen
  10852.                         this.setError('Unexpected end of JSON stream')
  10853.                         return ''
  10854.                 endif
  10855.         return substr(this.cJson, this.nPos, 1)
  10856.        
  10857.         function nextChar()
  10858.                 this.nPos = this.nPos + 1
  10859.                 if this.nPos > this.nLen
  10860.                         return ''
  10861.                 endif
  10862.         return substr(this.cJson, this.nPos, 1)
  10863.        
  10864.         function setError(cMsg)
  10865.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  10866.         return
  10867.  
  10868.  
  10869.         function fixUnicode(cStr)
  10870.                 cStr = StrTran(cStr,'\u00e1','á')
  10871.                 cStr = StrTran(cStr,'\u00e9','é')
  10872.                 cStr = StrTran(cStr,'\u00ed','í')
  10873.                 cStr = StrTran(cStr,'\u00f3','ó')
  10874.                 cStr = StrTran(cStr,'\u00fa','ú')
  10875.                 cStr = StrTran(cStr,'\u00c1','Á')
  10876.                 cStr = StrTran(cStr,'\u00c9','É')
  10877.                 cStr = StrTran(cStr,'\u00cd','Í')
  10878.                 cStr = StrTran(cStr,'\u00d3','Ó')
  10879.                 cStr = StrTran(cStr,'\u00da','Ú')
  10880.                 cStr = StrTran(cStr,'\u00f1','ń')
  10881.                 cStr = StrTran(cStr,'\u00d1','Ń')
  10882.         return cStr
  10883.  
  10884.  
  10885.  
  10886. enddefine
  10887.  
  10888.  
  10889.  
  10890.  
  10891.  
  10892. *
  10893. * class used to return an array
  10894. *
  10895. define class myArray as custom
  10896.         nSize = 0
  10897.         dimension array[1]
  10898.  
  10899.         function add(xExpr)
  10900.                 this.nSize = this.nSize + 1
  10901.                 dimension this.array[this.nSize]
  10902.                 this.array[this.nSize] = xExpr
  10903.         return
  10904.  
  10905.         function get(n)
  10906.         return this.array[n]
  10907.  
  10908. enddefine
  10909.  
  10910.  
  10911.  
  10912. *
  10913. * class used to simulate an object
  10914. * all properties are prefixed with 'prop' to permit property names like: error, init
  10915. * that already exists like vfp methods
  10916. *
  10917. define class myObj as custom
  10918. Hidden ;
  10919.         ClassLibrary,Comment, ;
  10920.         BaseClass,ControlCount, ;
  10921.         Controls,Objects,Object,;
  10922.         Height,HelpContextID,Left,Name, ;
  10923.         Parent,ParentClass,Picture, ;
  10924.         Tag,Top,WhatsThisHelpID,Width
  10925.                
  10926.         function set(cPropName, xValue)
  10927.                 cPropName = '_'+cPropName
  10928.                 if type('this.'+cPropName)=='U'
  10929.                         this.addProperty(cPropName,xValue)
  10930.                 else
  10931.                         local cmd
  10932.                         cmd = 'this.'+cPropName+'=xValue'
  10933.                         &cmd
  10934.                 endif
  10935.         return
  10936.        
  10937.         procedure get (cPropName)
  10938.                 cPropName = '_'+cPropName
  10939.                 If type('this.'+cPropName)=='U'
  10940.                         return ''
  10941.                 Else
  10942.                         local cmd
  10943.                         cmd = 'return this.'+cPropName
  10944.                         &cmd
  10945.                 endif
  10946.         return ''
  10947. enddefine
  10948.  
  10949.  
  10950.  
  10951.  
  10952.  
  10953. function testJsonClass
  10954.         clear
  10955.         set decimal to 10
  10956.         oJson = newObject('json')
  10957.        
  10958.        
  10959.         ? 'Test Basic Types'
  10960.         ? '----------------'
  10961.         ? oJson.decode('null')
  10962.         ? oJson.decode('true')
  10963.         ? oJson.decode('false')
  10964.         ?
  10965.         ? oJson.decode('791123')
  10966.         ? oJson.decode('791123.45')
  10967.         ? oJson.decode('791123.45.')
  10968.         ? oJson.decode('"nacho gtz"')
  10969.         if not empty(oJson.cError)
  10970.                 ? oJson.cError
  10971.                 return
  10972.         endif
  10973.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  10974.         if not empty(oJson.cError)
  10975.                 ? oJson.cError
  10976.                 return
  10977.         endif
  10978.        
  10979.         ? 'Test Array'
  10980.         ? '----------'
  10981.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  10982.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  10983.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  10984.         nombres = arr.get(1)
  10985.         edades  = arr.get(2)
  10986.         ? nombres.get(1), edades.get(1)
  10987.         ? nombres.get(2), edades.get(2)
  10988.         ? nombres.get(3), edades.get(3)
  10989.         ?
  10990.         ? 'Test Object'
  10991.         ? '-----------'
  10992.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  10993.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  10994.         ? obj._Nombre, obj._Edad, obj._IsGood
  10995.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  10996.         ? obj.get('jsonrpc'), obj._jsonrpc
  10997.         ? obj.get('id'), obj._id
  10998.         ? obj.get('method'), obj._method
  10999.         ? obj._Params.array[1], obj._Params.get(1)
  11000.  
  11001.         ?
  11002.         ? 'Test nested object'
  11003.         ? '------------------'
  11004.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  11005.         obj = oJson.decode(cJson)
  11006.         if not empty(oJson.cError)
  11007.                 ? oJson.cError
  11008.                 return
  11009.         endif
  11010.         ? cJson
  11011.         ? 'method -->',obj._method
  11012.         ? 'usrkey -->',obj._params._data._usrkey
  11013.         ? 'sendto -->',obj._params._data._sendto
  11014.         ? 'name  --->',obj._params._data._name
  11015.         ? 'expires ->',obj._params._data._expires
  11016.  
  11017.         ?
  11018.         ? 'Test empty object'
  11019.         ? '-----------------'
  11020.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  11021.         obj = oJson.decode(cJson)
  11022.         if not empty(oJson.cError)     
  11023.                 ? oJson.cError
  11024.                 return
  11025.         endif
  11026.         ? cJson
  11027.         ? 'result -->',obj._result, obj.get('result')
  11028.         oError = obj.get('error')
  11029.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  11030.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  11031.         ? 'id  ----->',obj._id, obj.get('id')
  11032.         ?  type("oError._code")
  11033.  
  11034.         ?
  11035.         ? 'Probar decode-enconde-decode-encode'
  11036.         ? '------------------------------------'
  11037.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  11038.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  11039.         ? cJson
  11040.         oSmtp = json_decode(cJson)
  11041.         cJson =  json_encode(oSmtp)
  11042.         ? cJson
  11043.         oSmtp = json_decode(cJson)
  11044.         cJson =  json_encode(oSmtp)
  11045.         ? cJson
  11046.  
  11047.         * Probar falla
  11048.         ?
  11049.         ? 'Probar una falla en el json'
  11050.         ? '---------------------------'
  11051.         cJson = ' {"server":"", "user":"", "password":"" ,'
  11052.         oSmtp = json_decode(cJson)
  11053.         if not empty(json_getErrorMsg())
  11054.                 ? json_getErrorMsg()
  11055.         endif
  11056.  
  11057.         ?
  11058.         ? 'Pruebas Finalizadas'
  11059. retur
  11060. *
  11061. * Puedes usar las funciones:
  11062. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  11063. *               json_decode(cJson)  te regresa el objeto representado en cJson
  11064. *
  11065. * Tambien puedes usar directamente la clase:
  11066. *
  11067. *       oJson = newobject('json','json.prg')
  11068. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  11069. *       ? oJson.encode(oCliente)
  11070. *       ? oCliente.get('nombre')
  11071. *       ? oCliente.get('apellido')
  11072. *
  11073. *
  11074. * VFPJSON  Encode and Decode JSON for VFP
  11075. * Examples:
  11076. *       oJson = newobject('json','json.prg')
  11077. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  11078. *       ? oJson.encode(oCustomer)
  11079. *       ? oCustomer.get('name')
  11080. *       ? oCustomer.get('lastname')
  11081. *
  11082. *
  11083. lRunTest = .f.
  11084. if lRunTest
  11085.         testJsonClass()
  11086. endif
  11087. return
  11088.  
  11089.  
  11090. function json_encode(xExpr)
  11091.         if vartype(_json)<>'O'
  11092.                 public _json
  11093.                 _json = newobject('json')
  11094.         endif
  11095. return _json.encode(@xExpr)
  11096.  
  11097.  
  11098. function json_decode(cJson)
  11099. local retval
  11100.         if vartype(_json)<>'O'
  11101.                 public _json
  11102.                 _json = newobject('json')
  11103.         endif
  11104.         retval = _json.decode(cJson)
  11105.         if not empty(_json.cError)
  11106.                 return null
  11107.         endif
  11108. return retval
  11109.  
  11110. function json_getErrorMsg()
  11111. return _json.cError
  11112.        
  11113.  
  11114.  
  11115. *
  11116. * json class
  11117. *
  11118. *
  11119. define class json as custom
  11120.  
  11121.  
  11122.         nPos=0
  11123.         nLen=0
  11124.         cJson=''
  11125.         cError=''
  11126.  
  11127.  
  11128.         *
  11129.         * Genera el codigo cJson para parametro que se manda
  11130.         *
  11131.         function encode(xExpr)
  11132.         local cTipo
  11133.                 * Cuando se manda una arreglo,
  11134.                 if type('ALen(xExpr)')=='N'
  11135.                         cTipo = 'A'
  11136.                 Else
  11137.                         cTipo = VarType(xExpr)
  11138.                 Endif
  11139.                
  11140.                 Do Case
  11141.                 Case cTipo=='D'
  11142.                         return '"'+dtos(xExpr)+'"'
  11143.                 Case cTipo=='N'
  11144.                         return Transform(xExpr)
  11145.                 Case cTipo=='L'
  11146.                         return iif(xExpr,'true','false')
  11147.                 Case cTipo=='X'
  11148.                         return 'null'
  11149.                 Case cTipo=='C'
  11150.                         xExpr = allt(xExpr)
  11151.                         xExpr = StrTran(xExpr, '\', '\\' )
  11152.                         xExpr = StrTran(xExpr, '/', '\/' )
  11153.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  11154.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  11155.                         xExpr = StrTran(xExpr, '"', '\"' )
  11156.                         return '"'+xExpr+'"'
  11157.  
  11158.                 case cTipo=='O'
  11159.                         local cProp, cJsonValue, cRetVal, aProp[1]
  11160.                         =AMembers(aProp,xExpr)
  11161.                         cRetVal = ''
  11162.                         for each cProp in aProp
  11163.                                 *? cProp
  11164.                                 *? cRetVal
  11165.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  11166.                                         * algunas propiedades pueden no estar definidas
  11167.                                         * como: activecontrol, parent, etc
  11168.                                         loop
  11169.                                 endif
  11170.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  11171.                                         *
  11172.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  11173.                                         *
  11174.                                         Local i,nTotElem
  11175.                                         cJsonValue = ''
  11176.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  11177.                                         For i=1 to nTotElem
  11178.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  11179.                                         Next
  11180.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  11181.                                 else
  11182.                                         *
  11183.                                         * es otro tipo de dato normal C, N, L
  11184.                                         *
  11185.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  11186.                                 endif
  11187.                                 if left(cProp,1)=='_'
  11188.                                         cProp = substr(cProp,2)
  11189.                                 endif
  11190.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  11191.                         next
  11192.                         return '{' + substr(cRetVal,2) + '}'
  11193.  
  11194.                 case cTipo=='A'
  11195.                         local valor, cRetVal
  11196.                         cRetVal = ''   
  11197.                         for each valor in xExpr
  11198.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  11199.                         next
  11200.                         return  '[' + substr(cRetVal,2) + ']'
  11201.                        
  11202.                 endcase
  11203.  
  11204.         return ''
  11205.  
  11206.  
  11207.  
  11208.  
  11209.  
  11210.         *
  11211.         * regresa un elemento representado por la cadena json que se manda
  11212.         *
  11213.        
  11214.         function decode(cJson)
  11215.         local retValue
  11216.                 cJson = StrTran(cJson,chr(9),'')
  11217.                 cJson = StrTran(cJson,chr(10),'')
  11218.                 cJson = StrTran(cJson,chr(13),'')
  11219.                 cJson = this.fixUnicode(cJson)
  11220.                 this.nPos  = 1
  11221.                 this.cJson = cJson
  11222.                 this.nLen  = len(cJson)
  11223.                 this.cError = ''
  11224.                 retValue = this.parsevalue()
  11225.                 if not empty(this.cError)
  11226.                         return null
  11227.                 endif
  11228.                 if this.getToken()<>null
  11229.                         this.setError('Junk at the end of JSON input')
  11230.                         return null
  11231.                 endif
  11232.         return retValue
  11233.                
  11234.        
  11235.         function parseValue()
  11236.         local token
  11237.                 token = this.getToken()
  11238.                 if token==null
  11239.                         this.setError('Nothing to parse')
  11240.                         return null
  11241.                 endif
  11242.                 do case
  11243.                 case token=='"'
  11244.                         return this.parseString()
  11245.                 case isdigit(token) or token=='-'
  11246.                         return this.parseNumber()
  11247.                 case token=='n'
  11248.                         return this.expectedKeyword('null',null)
  11249.                 case token=='f'
  11250.                         return this.expectedKeyword('false',.f.)
  11251.                 case token=='t'
  11252.                         return this.expectedKeyword('true',.t.)
  11253.                 case token=='{'
  11254.                         return this.parseObject()
  11255.                 case token=='['
  11256.                         return this.parseArray()
  11257.                 otherwise
  11258.                         this.setError('Unexpected token')
  11259.                 endcase
  11260.         return
  11261.                
  11262.        
  11263.         function expectedKeyword(cWord,eValue)
  11264.                 for i=1 to len(cWord)
  11265.                         cChar = this.getChar()
  11266.                         if cChar <> substr(cWord,i,1)
  11267.                                 this.setError("Expected keyword '" + cWord + "'")
  11268.                                 return ''
  11269.                         endif
  11270.                         this.nPos = this.nPos + 1
  11271.                 next
  11272.         return eValue
  11273.        
  11274.  
  11275.         function parseObject()
  11276.         local retval, cPropName, xValue
  11277.                 retval = createObject('myObj')
  11278.                 this.nPos = this.nPos + 1 && Eat {
  11279.                 if this.getToken()<>'}'
  11280.                         do while .t.
  11281.                                 cPropName = this.parseString()
  11282.                                 if not empty(this.cError)
  11283.                                         return null
  11284.                                 endif
  11285.                                 if this.getToken()<>':'
  11286.                                         this.setError("Expected ':' when parsing object")
  11287.                                         return null
  11288.                                 endif
  11289.                                 this.nPos = this.nPos + 1
  11290.                                 xValue = this.parseValue()
  11291.                                 if not empty(this.cError)
  11292.                                         return null
  11293.                                 endif                          
  11294.                                 ** Debug ? cPropName, type('xValue')
  11295.                                 retval.set(cPropName, xValue)
  11296.                                 if this.getToken()<>','
  11297.                                         exit
  11298.                                 endif
  11299.                                 this.nPos = this.nPos + 1
  11300.                         enddo
  11301.                 endif
  11302.                 if this.getToken()<>'}'
  11303.                         this.setError("Expected '}' at the end of object")
  11304.                         return null
  11305.                 endif
  11306.                 this.nPos = this.nPos + 1
  11307.         return retval
  11308.  
  11309.  
  11310.         function parseArray()
  11311.         local retVal, xValue
  11312.                 retval = createObject('MyArray')
  11313.                 this.nPos = this.nPos + 1       && Eat [
  11314.                 if this.getToken() <> ']'
  11315.                         do while .t.
  11316.                                 xValue = this.parseValue()
  11317.                                 if not empty(this.cError)
  11318.                                         return null
  11319.                                 endif
  11320.                                 retval.add( xValue )
  11321.                                 if this.getToken()<>','
  11322.                                         exit
  11323.                                 endif
  11324.                                 this.nPos = this.nPos + 1
  11325.                         enddo
  11326.                         if this.getToken() <> ']'
  11327.                                 this.setError('Expected ] at the end of array')
  11328.                                 return null
  11329.                         endif
  11330.                 endif
  11331.                 this.nPos = this.nPos + 1
  11332.         return retval
  11333.        
  11334.  
  11335.         function parseString()
  11336.         local cRetVal, c
  11337.                 if this.getToken()<>'"'
  11338.                         this.setError('Expected "')
  11339.                         return ''
  11340.                 endif
  11341.                 this.nPos = this.nPos + 1       && Eat "
  11342.                 cRetVal = ''
  11343.                 do while .t.
  11344.                         c = this.getChar()
  11345.                         if c==''
  11346.                                 return ''
  11347.                         endif
  11348.                         if c == '"'
  11349.                                 this.nPos = this.nPos + 1
  11350.                                 exit
  11351.                         endif
  11352.                         if c == '\'
  11353.                                 this.nPos = this.nPos + 1
  11354.                                 if (this.nPos>this.nLen)
  11355.                                         this.setError('\\ at the end of input')
  11356.                                         return ''
  11357.                                 endif
  11358.                                 c = this.getChar()
  11359.                                 if c==''
  11360.                                         return ''
  11361.                                 endif
  11362.                                 do case
  11363.                                 case c=='"'
  11364.                                         c='"'
  11365.                                 case c=='\'
  11366.                                         c='\'
  11367.                                 case c=='/'
  11368.                                         c='/'
  11369.                                 case c=='b'
  11370.                                         c=chr(8)
  11371.                                 case c=='t'
  11372.                                         c=chr(9)
  11373.                                 case c=='n'
  11374.                                         c=chr(10)
  11375.                                 case c=='f'
  11376.                                         c=chr(12)
  11377.                                 case c=='r'
  11378.                                         c=chr(13)
  11379.                                 otherwise
  11380.                                         ******* FALTAN LOS UNICODE
  11381.                                         this.setError('Invalid escape sequence in string literal')
  11382.                                         return ''
  11383.                                 endcase
  11384.                         endif
  11385.                         cRetVal = cRetVal + c
  11386.                         this.nPos = this.nPos + 1
  11387.                 enddo
  11388.         return cRetVal
  11389.                                        
  11390.  
  11391.         **** Pendiente numeros con E
  11392.         function parseNumber()
  11393.         local nStartPos,c, isInt, cNumero
  11394.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  11395.                         this.setError('Expected number literal')
  11396.                         return 0
  11397.                 endif
  11398.                 nStartPos = this.nPos
  11399.                 c = this.getChar()
  11400.                 if c == '-'
  11401.                         c = this.nextChar()
  11402.                 endif
  11403.                 if c == '0'
  11404.                         c = this.nextChar()
  11405.                 else
  11406.                         if isdigit(c)
  11407.                                 c = this.nextChar()
  11408.                                 do while isdigit(c)
  11409.                                         c = this.nextChar()
  11410.                                 enddo
  11411.                         else
  11412.                                 this.setError('Expected digit when parsing number')
  11413.                                 return 0
  11414.                         endif
  11415.                 endif
  11416.                
  11417.                 isInt = .t.
  11418.                 if c=='.'
  11419.                         c = this.nextChar()
  11420.                         if isdigit(c)
  11421.                                 c = this.nextChar()
  11422.                                 isInt = .f.
  11423.                                 do while isDigit(c)
  11424.                                         c = this.nextChar()
  11425.                                 enddo
  11426.                         else
  11427.                                 this.setError('Expected digit following dot comma')
  11428.                                 return 0
  11429.                         endif
  11430.                 endif
  11431.                
  11432.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  11433.         return val(cNumero)
  11434.  
  11435.  
  11436.  
  11437.         function getToken()
  11438.         local char1
  11439.                 do while .t.
  11440.                         if this.nPos > this.nLen
  11441.                                 return null
  11442.                         endif
  11443.                         char1 = substr(this.cJson, this.nPos, 1)
  11444.                         if char1==' '
  11445.                                 this.nPos = this.nPos + 1
  11446.                                 loop
  11447.                         endif
  11448.                         return char1
  11449.                 enddo
  11450.         return
  11451.        
  11452.                
  11453.                
  11454.         function getChar()
  11455.                 if this.nPos > this.nLen
  11456.                         this.setError('Unexpected end of JSON stream')
  11457.                         return ''
  11458.                 endif
  11459.         return substr(this.cJson, this.nPos, 1)
  11460.        
  11461.         function nextChar()
  11462.                 this.nPos = this.nPos + 1
  11463.                 if this.nPos > this.nLen
  11464.                         return ''
  11465.                 endif
  11466.         return substr(this.cJson, this.nPos, 1)
  11467.        
  11468.         function setError(cMsg)
  11469.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  11470.         return
  11471.  
  11472.  
  11473.         function fixUnicode(cStr)
  11474.                 cStr = StrTran(cStr,'\u00e1','á')
  11475.                 cStr = StrTran(cStr,'\u00e9','é')
  11476.                 cStr = StrTran(cStr,'\u00ed','í')
  11477.                 cStr = StrTran(cStr,'\u00f3','ó')
  11478.                 cStr = StrTran(cStr,'\u00fa','ú')
  11479.                 cStr = StrTran(cStr,'\u00c1','Á')
  11480.                 cStr = StrTran(cStr,'\u00c9','É')
  11481.                 cStr = StrTran(cStr,'\u00cd','Í')
  11482.                 cStr = StrTran(cStr,'\u00d3','Ó')
  11483.                 cStr = StrTran(cStr,'\u00da','Ú')
  11484.                 cStr = StrTran(cStr,'\u00f1','ń')
  11485.                 cStr = StrTran(cStr,'\u00d1','Ń')
  11486.         return cStr
  11487.  
  11488.  
  11489.  
  11490. enddefine
  11491.  
  11492.  
  11493.  
  11494.  
  11495.  
  11496. *
  11497. * class used to return an array
  11498. *
  11499. define class myArray as custom
  11500.         nSize = 0
  11501.         dimension array[1]
  11502.  
  11503.         function add(xExpr)
  11504.                 this.nSize = this.nSize + 1
  11505.                 dimension this.array[this.nSize]
  11506.                 this.array[this.nSize] = xExpr
  11507.         return
  11508.  
  11509.         function get(n)
  11510.         return this.array[n]
  11511.  
  11512. enddefine
  11513.  
  11514.  
  11515.  
  11516. *
  11517. * class used to simulate an object
  11518. * all properties are prefixed with 'prop' to permit property names like: error, init
  11519. * that already exists like vfp methods
  11520. *
  11521. define class myObj as custom
  11522. Hidden ;
  11523.         ClassLibrary,Comment, ;
  11524.         BaseClass,ControlCount, ;
  11525.         Controls,Objects,Object,;
  11526.         Height,HelpContextID,Left,Name, ;
  11527.         Parent,ParentClass,Picture, ;
  11528.         Tag,Top,WhatsThisHelpID,Width
  11529.                
  11530.         function set(cPropName, xValue)
  11531.                 cPropName = '_'+cPropName
  11532.                 if type('this.'+cPropName)=='U'
  11533.                         this.addProperty(cPropName,xValue)
  11534.                 else
  11535.                         local cmd
  11536.                         cmd = 'this.'+cPropName+'=xValue'
  11537.                         &cmd
  11538.                 endif
  11539.         return
  11540.        
  11541.         procedure get (cPropName)
  11542.                 cPropName = '_'+cPropName
  11543.                 If type('this.'+cPropName)=='U'
  11544.                         return ''
  11545.                 Else
  11546.                         local cmd
  11547.                         cmd = 'return this.'+cPropName
  11548.                         &cmd
  11549.                 endif
  11550.         return ''
  11551. enddefine
  11552.  
  11553.  
  11554.  
  11555.  
  11556.  
  11557. function testJsonClass
  11558.         clear
  11559.         set decimal to 10
  11560.         oJson = newObject('json')
  11561.        
  11562.        
  11563.         ? 'Test Basic Types'
  11564.         ? '----------------'
  11565.         ? oJson.decode('null')
  11566.         ? oJson.decode('true')
  11567.         ? oJson.decode('false')
  11568.         ?
  11569.         ? oJson.decode('791123')
  11570.         ? oJson.decode('791123.45')
  11571.         ? oJson.decode('791123.45.')
  11572.         ? oJson.decode('"nacho gtz"')
  11573.         if not empty(oJson.cError)
  11574.                 ? oJson.cError
  11575.                 return
  11576.         endif
  11577.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  11578.         if not empty(oJson.cError)
  11579.                 ? oJson.cError
  11580.                 return
  11581.         endif
  11582.        
  11583.         ? 'Test Array'
  11584.         ? '----------'
  11585.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  11586.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  11587.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  11588.         nombres = arr.get(1)
  11589.         edades  = arr.get(2)
  11590.         ? nombres.get(1), edades.get(1)
  11591.         ? nombres.get(2), edades.get(2)
  11592.         ? nombres.get(3), edades.get(3)
  11593.         ?
  11594.         ? 'Test Object'
  11595.         ? '-----------'
  11596.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  11597.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  11598.         ? obj._Nombre, obj._Edad, obj._IsGood
  11599.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  11600.         ? obj.get('jsonrpc'), obj._jsonrpc
  11601.         ? obj.get('id'), obj._id
  11602.         ? obj.get('method'), obj._method
  11603.         ? obj._Params.array[1], obj._Params.get(1)
  11604.  
  11605.         ?
  11606.         ? 'Test nested object'
  11607.         ? '------------------'
  11608.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  11609.         obj = oJson.decode(cJson)
  11610.         if not empty(oJson.cError)
  11611.                 ? oJson.cError
  11612.                 return
  11613.         endif
  11614.         ? cJson
  11615.         ? 'method -->',obj._method
  11616.         ? 'usrkey -->',obj._params._data._usrkey
  11617.         ? 'sendto -->',obj._params._data._sendto
  11618.         ? 'name  --->',obj._params._data._name
  11619.         ? 'expires ->',obj._params._data._expires
  11620.  
  11621.         ?
  11622.         ? 'Test empty object'
  11623.         ? '-----------------'
  11624.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  11625.         obj = oJson.decode(cJson)
  11626.         if not empty(oJson.cError)     
  11627.                 ? oJson.cError
  11628.                 return
  11629.         endif
  11630.         ? cJson
  11631.         ? 'result -->',obj._result, obj.get('result')
  11632.         oError = obj.get('error')
  11633.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  11634.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  11635.         ? 'id  ----->',obj._id, obj.get('id')
  11636.         ?  type("oError._code")
  11637.  
  11638.         ?
  11639.         ? 'Probar decode-enconde-decode-encode'
  11640.         ? '------------------------------------'
  11641.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  11642.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  11643.         ? cJson
  11644.         oSmtp = json_decode(cJson)
  11645.         cJson =  json_encode(oSmtp)
  11646.         ? cJson
  11647.         oSmtp = json_decode(cJson)
  11648.         cJson =  json_encode(oSmtp)
  11649.         ? cJson
  11650.  
  11651.         * Probar falla
  11652.         ?
  11653.         ? 'Probar una falla en el json'
  11654.         ? '---------------------------'
  11655.         cJson = ' {"server":"", "user":"", "password":"" ,'
  11656.         oSmtp = json_decode(cJson)
  11657.         if not empty(json_getErrorMsg())
  11658.                 ? json_getErrorMsg()
  11659.         endif
  11660.  
  11661.         ?
  11662.         ? 'Pruebas Finalizadas'
  11663. retur
  11664. * Puedes usar las funciones:
  11665. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  11666. *               json_decode(cJson)  te regresa el objeto representado en cJson
  11667. *
  11668. * Tambien puedes usar directamente la clase:
  11669. *
  11670. *       oJson = newobject('json','json.prg')
  11671. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  11672. *       ? oJson.encode(oCliente)
  11673. *       ? oCliente.get('nombre')
  11674. *       ? oCliente.get('apellido')
  11675. *
  11676. *
  11677. * VFPJSON  Encode and Decode JSON for VFP
  11678. * Examples:
  11679. *       oJson = newobject('json','json.prg')
  11680. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  11681. *       ? oJson.encode(oCustomer)
  11682. *       ? oCustomer.get('name')
  11683. *       ? oCustomer.get('lastname')
  11684. *
  11685. *
  11686. lRunTest = .f.
  11687. if lRunTest
  11688.         testJsonClass()
  11689. endif
  11690. return
  11691.  
  11692.  
  11693. function json_encode(xExpr)
  11694.         if vartype(_json)<>'O'
  11695.                 public _json
  11696.                 _json = newobject('json')
  11697.         endif
  11698. return _json.encode(@xExpr)
  11699.  
  11700.  
  11701. function json_decode(cJson)
  11702. local retval
  11703.         if vartype(_json)<>'O'
  11704.                 public _json
  11705.                 _json = newobject('json')
  11706.         endif
  11707.         retval = _json.decode(cJson)
  11708.         if not empty(_json.cError)
  11709.                 return null
  11710.         endif
  11711. return retval
  11712.  
  11713. function json_getErrorMsg()
  11714. return _json.cError
  11715.        
  11716.  
  11717.  
  11718. *
  11719. * json class
  11720. *
  11721. *
  11722. define class json as custom
  11723.  
  11724.  
  11725.         nPos=0
  11726.         nLen=0
  11727.         cJson=''
  11728.         cError=''
  11729.  
  11730.  
  11731.         *
  11732.         * Genera el codigo cJson para parametro que se manda
  11733.         *
  11734.         function encode(xExpr)
  11735.         local cTipo
  11736.                 * Cuando se manda una arreglo,
  11737.                 if type('ALen(xExpr)')=='N'
  11738.                         cTipo = 'A'
  11739.                 Else
  11740.                         cTipo = VarType(xExpr)
  11741.                 Endif
  11742.                
  11743.                 Do Case
  11744.                 Case cTipo=='D'
  11745.                         return '"'+dtos(xExpr)+'"'
  11746.                 Case cTipo=='N'
  11747.                         return Transform(xExpr)
  11748.                 Case cTipo=='L'
  11749.                         return iif(xExpr,'true','false')
  11750.                 Case cTipo=='X'
  11751.                         return 'null'
  11752.                 Case cTipo=='C'
  11753.                         xExpr = allt(xExpr)
  11754.                         xExpr = StrTran(xExpr, '\', '\\' )
  11755.                         xExpr = StrTran(xExpr, '/', '\/' )
  11756.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  11757.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  11758.                         xExpr = StrTran(xExpr, '"', '\"' )
  11759.                         return '"'+xExpr+'"'
  11760.  
  11761.                 case cTipo=='O'
  11762.                         local cProp, cJsonValue, cRetVal, aProp[1]
  11763.                         =AMembers(aProp,xExpr)
  11764.                         cRetVal = ''
  11765.                         for each cProp in aProp
  11766.                                 *? cProp
  11767.                                 *? cRetVal
  11768.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  11769.                                         * algunas propiedades pueden no estar definidas
  11770.                                         * como: activecontrol, parent, etc
  11771.                                         loop
  11772.                                 endif
  11773.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  11774.                                         *
  11775.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  11776.                                         *
  11777.                                         Local i,nTotElem
  11778.                                         cJsonValue = ''
  11779.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  11780.                                         For i=1 to nTotElem
  11781.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  11782.                                         Next
  11783.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  11784.                                 else
  11785.                                         *
  11786.                                         * es otro tipo de dato normal C, N, L
  11787.                                         *
  11788.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  11789.                                 endif
  11790.                                 if left(cProp,1)=='_'
  11791.                                         cProp = substr(cProp,2)
  11792.                                 endif
  11793.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  11794.                         next
  11795.                         return '{' + substr(cRetVal,2) + '}'
  11796.  
  11797.                 case cTipo=='A'
  11798.                         local valor, cRetVal
  11799.                         cRetVal = ''   
  11800.                         for each valor in xExpr
  11801.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  11802.                         next
  11803.                         return  '[' + substr(cRetVal,2) + ']'
  11804.                        
  11805.                 endcase
  11806.  
  11807.         return ''
  11808.  
  11809.  
  11810.  
  11811.  
  11812.  
  11813.         *
  11814.         * regresa un elemento representado por la cadena json que se manda
  11815.         *
  11816.        
  11817.         function decode(cJson)
  11818.         local retValue
  11819.                 cJson = StrTran(cJson,chr(9),'')
  11820.                 cJson = StrTran(cJson,chr(10),'')
  11821.                 cJson = StrTran(cJson,chr(13),'')
  11822.                 cJson = this.fixUnicode(cJson)
  11823.                 this.nPos  = 1
  11824.                 this.cJson = cJson
  11825.                 this.nLen  = len(cJson)
  11826.                 this.cError = ''
  11827.                 retValue = this.parsevalue()
  11828.                 if not empty(this.cError)
  11829.                         return null
  11830.                 endif
  11831.                 if this.getToken()<>null
  11832.                         this.setError('Junk at the end of JSON input')
  11833.                         return null
  11834.                 endif
  11835.         return retValue
  11836.                
  11837.        
  11838.         function parseValue()
  11839.         local token
  11840.                 token = this.getToken()
  11841.                 if token==null
  11842.                         this.setError('Nothing to parse')
  11843.                         return null
  11844.                 endif
  11845.                 do case
  11846.                 case token=='"'
  11847.                         return this.parseString()
  11848.                 case isdigit(token) or token=='-'
  11849.                         return this.parseNumber()
  11850.                 case token=='n'
  11851.                         return this.expectedKeyword('null',null)
  11852.                 case token=='f'
  11853.                         return this.expectedKeyword('false',.f.)
  11854.                 case token=='t'
  11855.                         return this.expectedKeyword('true',.t.)
  11856.                 case token=='{'
  11857.                         return this.parseObject()
  11858.                 case token=='['
  11859.                         return this.parseArray()
  11860.                 otherwise
  11861.                         this.setError('Unexpected token')
  11862.                 endcase
  11863.         return
  11864.                
  11865.        
  11866.         function expectedKeyword(cWord,eValue)
  11867.                 for i=1 to len(cWord)
  11868.                         cChar = this.getChar()
  11869.                         if cChar <> substr(cWord,i,1)
  11870.                                 this.setError("Expected keyword '" + cWord + "'")
  11871.                                 return ''
  11872.                         endif
  11873.                         this.nPos = this.nPos + 1
  11874.                 next
  11875.         return eValue
  11876.        
  11877.  
  11878.         function parseObject()
  11879.         local retval, cPropName, xValue
  11880.                 retval = createObject('myObj')
  11881.                 this.nPos = this.nPos + 1 && Eat {
  11882.                 if this.getToken()<>'}'
  11883.                         do while .t.
  11884.                                 cPropName = this.parseString()
  11885.                                 if not empty(this.cError)
  11886.                                         return null
  11887.                                 endif
  11888.                                 if this.getToken()<>':'
  11889.                                         this.setError("Expected ':' when parsing object")
  11890.                                         return null
  11891.                                 endif
  11892.                                 this.nPos = this.nPos + 1
  11893.                                 xValue = this.parseValue()
  11894.                                 if not empty(this.cError)
  11895.                                         return null
  11896.                                 endif                          
  11897.                                 ** Debug ? cPropName, type('xValue')
  11898.                                 retval.set(cPropName, xValue)
  11899.                                 if this.getToken()<>','
  11900.                                         exit
  11901.                                 endif
  11902.                                 this.nPos = this.nPos + 1
  11903.                         enddo
  11904.                 endif
  11905.                 if this.getToken()<>'}'
  11906.                         this.setError("Expected '}' at the end of object")
  11907.                         return null
  11908.                 endif
  11909.                 this.nPos = this.nPos + 1
  11910.         return retval
  11911.  
  11912.  
  11913.         function parseArray()
  11914.         local retVal, xValue
  11915.                 retval = createObject('MyArray')
  11916.                 this.nPos = this.nPos + 1       && Eat [
  11917.                 if this.getToken() <> ']'
  11918.                         do while .t.
  11919.                                 xValue = this.parseValue()
  11920.                                 if not empty(this.cError)
  11921.                                         return null
  11922.                                 endif
  11923.                                 retval.add( xValue )
  11924.                                 if this.getToken()<>','
  11925.                                         exit
  11926.                                 endif
  11927.                                 this.nPos = this.nPos + 1
  11928.                         enddo
  11929.                         if this.getToken() <> ']'
  11930.                                 this.setError('Expected ] at the end of array')
  11931.                                 return null
  11932.                         endif
  11933.                 endif
  11934.                 this.nPos = this.nPos + 1
  11935.         return retval
  11936.        
  11937.  
  11938.         function parseString()
  11939.         local cRetVal, c
  11940.                 if this.getToken()<>'"'
  11941.                         this.setError('Expected "')
  11942.                         return ''
  11943.                 endif
  11944.                 this.nPos = this.nPos + 1       && Eat "
  11945.                 cRetVal = ''
  11946.                 do while .t.
  11947.                         c = this.getChar()
  11948.                         if c==''
  11949.                                 return ''
  11950.                         endif
  11951.                         if c == '"'
  11952.                                 this.nPos = this.nPos + 1
  11953.                                 exit
  11954.                         endif
  11955.                         if c == '\'
  11956.                                 this.nPos = this.nPos + 1
  11957.                                 if (this.nPos>this.nLen)
  11958.                                         this.setError('\\ at the end of input')
  11959.                                         return ''
  11960.                                 endif
  11961.                                 c = this.getChar()
  11962.                                 if c==''
  11963.                                         return ''
  11964.                                 endif
  11965.                                 do case
  11966.                                 case c=='"'
  11967.                                         c='"'
  11968.                                 case c=='\'
  11969.                                         c='\'
  11970.                                 case c=='/'
  11971.                                         c='/'
  11972.                                 case c=='b'
  11973.                                         c=chr(8)
  11974.                                 case c=='t'
  11975.                                         c=chr(9)
  11976.                                 case c=='n'
  11977.                                         c=chr(10)
  11978.                                 case c=='f'
  11979.                                         c=chr(12)
  11980.                                 case c=='r'
  11981.                                         c=chr(13)
  11982.                                 otherwise
  11983.                                         ******* FALTAN LOS UNICODE
  11984.                                         this.setError('Invalid escape sequence in string literal')
  11985.                                         return ''
  11986.                                 endcase
  11987.                         endif
  11988.                         cRetVal = cRetVal + c
  11989.                         this.nPos = this.nPos + 1
  11990.                 enddo
  11991.         return cRetVal
  11992.                                        
  11993.  
  11994.         **** Pendiente numeros con E
  11995.         function parseNumber()
  11996.         local nStartPos,c, isInt, cNumero
  11997.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  11998.                         this.setError('Expected number literal')
  11999.                         return 0
  12000.                 endif
  12001.                 nStartPos = this.nPos
  12002.                 c = this.getChar()
  12003.                 if c == '-'
  12004.                         c = this.nextChar()
  12005.                 endif
  12006.                 if c == '0'
  12007.                         c = this.nextChar()
  12008.                 else
  12009.                         if isdigit(c)
  12010.                                 c = this.nextChar()
  12011.                                 do while isdigit(c)
  12012.                                         c = this.nextChar()
  12013.                                 enddo
  12014.                         else
  12015.                                 this.setError('Expected digit when parsing number')
  12016.                                 return 0
  12017.                         endif
  12018.                 endif
  12019.                
  12020.                 isInt = .t.
  12021.                 if c=='.'
  12022.                         c = this.nextChar()
  12023.                         if isdigit(c)
  12024.                                 c = this.nextChar()
  12025.                                 isInt = .f.
  12026.                                 do while isDigit(c)
  12027.                                         c = this.nextChar()
  12028.                                 enddo
  12029.                         else
  12030.                                 this.setError('Expected digit following dot comma')
  12031.                                 return 0
  12032.                         endif
  12033.                 endif
  12034.                
  12035.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  12036.         return val(cNumero)
  12037.  
  12038.  
  12039.  
  12040.         function getToken()
  12041.         local char1
  12042.                 do while .t.
  12043.                         if this.nPos > this.nLen
  12044.                                 return null
  12045.                         endif
  12046.                         char1 = substr(this.cJson, this.nPos, 1)
  12047.                         if char1==' '
  12048.                                 this.nPos = this.nPos + 1
  12049.                                 loop
  12050.                         endif
  12051.                         return char1
  12052.                 enddo
  12053.         return
  12054.        
  12055.                
  12056.                
  12057.         function getChar()
  12058.                 if this.nPos > this.nLen
  12059.                         this.setError('Unexpected end of JSON stream')
  12060.                         return ''
  12061.                 endif
  12062.         return substr(this.cJson, this.nPos, 1)
  12063.        
  12064.         function nextChar()
  12065.                 this.nPos = this.nPos + 1
  12066.                 if this.nPos > this.nLen
  12067.                         return ''
  12068.                 endif
  12069.         return substr(this.cJson, this.nPos, 1)
  12070.        
  12071.         function setError(cMsg)
  12072.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  12073.         return
  12074.  
  12075.  
  12076.         function fixUnicode(cStr)
  12077.                 cStr = StrTran(cStr,'\u00e1','á')
  12078.                 cStr = StrTran(cStr,'\u00e9','é')
  12079.                 cStr = StrTran(cStr,'\u00ed','í')
  12080.                 cStr = StrTran(cStr,'\u00f3','ó')
  12081.                 cStr = StrTran(cStr,'\u00fa','ú')
  12082.                 cStr = StrTran(cStr,'\u00c1','Á')
  12083.                 cStr = StrTran(cStr,'\u00c9','É')
  12084.                 cStr = StrTran(cStr,'\u00cd','Í')
  12085.                 cStr = StrTran(cStr,'\u00d3','Ó')
  12086.                 cStr = StrTran(cStr,'\u00da','Ú')
  12087.                 cStr = StrTran(cStr,'\u00f1','ń')
  12088.                 cStr = StrTran(cStr,'\u00d1','Ń')
  12089.         return cStr
  12090.  
  12091.  
  12092.  
  12093. enddefine
  12094.  
  12095.  
  12096.  
  12097.  
  12098.  
  12099. *
  12100. * class used to return an array
  12101. *
  12102. define class myArray as custom
  12103.         nSize = 0
  12104.         dimension array[1]
  12105.  
  12106.         function add(xExpr)
  12107.                 this.nSize = this.nSize + 1
  12108.                 dimension this.array[this.nSize]
  12109.                 this.array[this.nSize] = xExpr
  12110.         return
  12111.  
  12112.         function get(n)
  12113.         return this.array[n]
  12114.  
  12115. enddefine
  12116.  
  12117.  
  12118.  
  12119. *
  12120. * class used to simulate an object
  12121. * all properties are prefixed with 'prop' to permit property names like: error, init
  12122. * that already exists like vfp methods
  12123. *
  12124. define class myObj as custom
  12125. Hidden ;
  12126.         ClassLibrary,Comment, ;
  12127.         BaseClass,ControlCount, ;
  12128.         Controls,Objects,Object,;
  12129.         Height,HelpContextID,Left,Name, ;
  12130.         Parent,ParentClass,Picture, ;
  12131.         Tag,Top,WhatsThisHelpID,Width
  12132.                
  12133.         function set(cPropName, xValue)
  12134.                 cPropName = '_'+cPropName
  12135.                 if type('this.'+cPropName)=='U'
  12136.                         this.addProperty(cPropName,xValue)
  12137.                 else
  12138.                         local cmd
  12139.                         cmd = 'this.'+cPropName+'=xValue'
  12140.                         &cmd
  12141.                 endif
  12142.         return
  12143.        
  12144.         procedure get (cPropName)
  12145.                 cPropName = '_'+cPropName
  12146.                 If type('this.'+cPropName)=='U'
  12147.                         return ''
  12148.                 Else
  12149.                         local cmd
  12150.                         cmd = 'return this.'+cPropName
  12151.                         &cmd
  12152.                 endif
  12153.         return ''
  12154. enddefine
  12155.  
  12156.  
  12157.  
  12158.  
  12159.  
  12160. function testJsonClass
  12161.         clear
  12162.         set decimal to 10
  12163.         oJson = newObject('json')
  12164.        
  12165.        
  12166.         ? 'Test Basic Types'
  12167.         ? '----------------'
  12168.         ? oJson.decode('null')
  12169.         ? oJson.decode('true')
  12170.         ? oJson.decode('false')
  12171.         ?
  12172.         ? oJson.decode('791123')
  12173.         ? oJson.decode('791123.45')
  12174.         ? oJson.decode('791123.45.')
  12175.         ? oJson.decode('"nacho gtz"')
  12176.         if not empty(oJson.cError)
  12177.                 ? oJson.cError
  12178.                 return
  12179.         endif
  12180.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  12181.         if not empty(oJson.cError)
  12182.                 ? oJson.cError
  12183.                 return
  12184.         endif
  12185.        
  12186.         ? 'Test Array'
  12187.         ? '----------'
  12188.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  12189.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  12190.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  12191.         nombres = arr.get(1)
  12192.         edades  = arr.get(2)
  12193.         ? nombres.get(1), edades.get(1)
  12194.         ? nombres.get(2), edades.get(2)
  12195.         ? nombres.get(3), edades.get(3)
  12196.         ?
  12197.         ? 'Test Object'
  12198.         ? '-----------'
  12199.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  12200.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  12201.         ? obj._Nombre, obj._Edad, obj._IsGood
  12202.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  12203.         ? obj.get('jsonrpc'), obj._jsonrpc
  12204.         ? obj.get('id'), obj._id
  12205.         ? obj.get('method'), obj._method
  12206.         ? obj._Params.array[1], obj._Params.get(1)
  12207.  
  12208.         ?
  12209.         ? 'Test nested object'
  12210.         ? '------------------'
  12211.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  12212.         obj = oJson.decode(cJson)
  12213.         if not empty(oJson.cError)
  12214.                 ? oJson.cError
  12215.                 return
  12216.         endif
  12217.         ? cJson
  12218.         ? 'method -->',obj._method
  12219.         ? 'usrkey -->',obj._params._data._usrkey
  12220.         ? 'sendto -->',obj._params._data._sendto
  12221.         ? 'name  --->',obj._params._data._name
  12222.         ? 'expires ->',obj._params._data._expires
  12223.  
  12224.         ?
  12225.         ? 'Test empty object'
  12226.         ? '-----------------'
  12227.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  12228.         obj = oJson.decode(cJson)
  12229.         if not empty(oJson.cError)     
  12230.                 ? oJson.cError
  12231.                 return
  12232.         endif
  12233.         ? cJson
  12234.         ? 'result -->',obj._result, obj.get('result')
  12235.         oError = obj.get('error')
  12236.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  12237.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  12238.         ? 'id  ----->',obj._id, obj.get('id')
  12239.         ?  type("oError._code")
  12240.  
  12241.         ?
  12242.         ? 'Probar decode-enconde-decode-encode'
  12243.         ? '------------------------------------'
  12244.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  12245.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  12246.         ? cJson
  12247.         oSmtp = json_decode(cJson)
  12248.         cJson =  json_encode(oSmtp)
  12249.         ? cJson
  12250.         oSmtp = json_decode(cJson)
  12251.         cJson =  json_encode(oSmtp)
  12252.         ? cJson
  12253.  
  12254.         * Probar falla
  12255.         ?
  12256.         ? 'Probar una falla en el json'
  12257.         ? '---------------------------'
  12258.         cJson = ' {"server":"", "user":"", "password":"" ,'
  12259.         oSmtp = json_decode(cJson)
  12260.         if not empty(json_getErrorMsg())
  12261.                 ? json_getErrorMsg()
  12262.         endif
  12263.  
  12264.         ?
  12265.         ? 'Pruebas Finalizadas'
  12266. retur
  12267. *               json_encode(xExpr)      te regresa la cadena Json, que representa al objeto que se pasa como parametro
  12268. *               json_decode(cJson)  te regresa el objeto representado en cJson
  12269. *
  12270. * Tambien puedes usar directamente la clase:
  12271. *
  12272. *       oJson = newobject('json','json.prg')
  12273. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  12274. *       ? oJson.encode(oCliente)
  12275. *       ? oCliente.get('nombre')
  12276. *       ? oCliente.get('apellido')
  12277. *
  12278. *
  12279. * VFPJSON  Encode and Decode JSON for VFP
  12280. * Examples:
  12281. *       oJson = newobject('json','json.prg')
  12282. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  12283. *       ? oJson.encode(oCustomer)
  12284. *       ? oCustomer.get('name')
  12285. *       ? oCustomer.get('lastname')
  12286. *
  12287. *
  12288. lRunTest = .f.
  12289. if lRunTest
  12290.         testJsonClass()
  12291. endif
  12292. return
  12293.  
  12294.  
  12295. function json_encode(xExpr)
  12296.         if vartype(_json)<>'O'
  12297.                 public _json
  12298.                 _json = newobject('json')
  12299.         endif
  12300. return _json.encode(@xExpr)
  12301.  
  12302.  
  12303. function json_decode(cJson)
  12304. local retval
  12305.         if vartype(_json)<>'O'
  12306.                 public _json
  12307.                 _json = newobject('json')
  12308.         endif
  12309.         retval = _json.decode(cJson)
  12310.         if not empty(_json.cError)
  12311.                 return null
  12312.         endif
  12313. return retval
  12314.  
  12315. function json_getErrorMsg()
  12316. return _json.cError
  12317.        
  12318.  
  12319.  
  12320. *
  12321. * json class
  12322. *
  12323. *
  12324. define class json as custom
  12325.  
  12326.  
  12327.         nPos=0
  12328.         nLen=0
  12329.         cJson=''
  12330.         cError=''
  12331.  
  12332.  
  12333.         *
  12334.         * Genera el codigo cJson para parametro que se manda
  12335.         *
  12336.         function encode(xExpr)
  12337.         local cTipo
  12338.                 * Cuando se manda una arreglo,
  12339.                 if type('ALen(xExpr)')=='N'
  12340.                         cTipo = 'A'
  12341.                 Else
  12342.                         cTipo = VarType(xExpr)
  12343.                 Endif
  12344.                
  12345.                 Do Case
  12346.                 Case cTipo=='D'
  12347.                         return '"'+dtos(xExpr)+'"'
  12348.                 Case cTipo=='N'
  12349.                         return Transform(xExpr)
  12350.                 Case cTipo=='L'
  12351.                         return iif(xExpr,'true','false')
  12352.                 Case cTipo=='X'
  12353.                         return 'null'
  12354.                 Case cTipo=='C'
  12355.                         xExpr = allt(xExpr)
  12356.                         xExpr = StrTran(xExpr, '\', '\\' )
  12357.                         xExpr = StrTran(xExpr, '/', '\/' )
  12358.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  12359.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  12360.                         xExpr = StrTran(xExpr, '"', '\"' )
  12361.                         return '"'+xExpr+'"'
  12362.  
  12363.                 case cTipo=='O'
  12364.                         local cProp, cJsonValue, cRetVal, aProp[1]
  12365.                         =AMembers(aProp,xExpr)
  12366.                         cRetVal = ''
  12367.                         for each cProp in aProp
  12368.                                 *? cProp
  12369.                                 *? cRetVal
  12370.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  12371.                                         * algunas propiedades pueden no estar definidas
  12372.                                         * como: activecontrol, parent, etc
  12373.                                         loop
  12374.                                 endif
  12375.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  12376.                                         *
  12377.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  12378.                                         *
  12379.                                         Local i,nTotElem
  12380.                                         cJsonValue = ''
  12381.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  12382.                                         For i=1 to nTotElem
  12383.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  12384.                                         Next
  12385.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  12386.                                 else
  12387.                                         *
  12388.                                         * es otro tipo de dato normal C, N, L
  12389.                                         *
  12390.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  12391.                                 endif
  12392.                                 if left(cProp,1)=='_'
  12393.                                         cProp = substr(cProp,2)
  12394.                                 endif
  12395.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  12396.                         next
  12397.                         return '{' + substr(cRetVal,2) + '}'
  12398.  
  12399.                 case cTipo=='A'
  12400.                         local valor, cRetVal
  12401.                         cRetVal = ''   
  12402.                         for each valor in xExpr
  12403.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  12404.                         next
  12405.                         return  '[' + substr(cRetVal,2) + ']'
  12406.                        
  12407.                 endcase
  12408.  
  12409.         return ''
  12410.  
  12411.  
  12412.  
  12413.  
  12414.  
  12415.         *
  12416.         * regresa un elemento representado por la cadena json que se manda
  12417.         *
  12418.        
  12419.         function decode(cJson)
  12420.         local retValue
  12421.                 cJson = StrTran(cJson,chr(9),'')
  12422.                 cJson = StrTran(cJson,chr(10),'')
  12423.                 cJson = StrTran(cJson,chr(13),'')
  12424.                 cJson = this.fixUnicode(cJson)
  12425.                 this.nPos  = 1
  12426.                 this.cJson = cJson
  12427.                 this.nLen  = len(cJson)
  12428.                 this.cError = ''
  12429.                 retValue = this.parsevalue()
  12430.                 if not empty(this.cError)
  12431.                         return null
  12432.                 endif
  12433.                 if this.getToken()<>null
  12434.                         this.setError('Junk at the end of JSON input')
  12435.                         return null
  12436.                 endif
  12437.         return retValue
  12438.                
  12439.        
  12440.         function parseValue()
  12441.         local token
  12442.                 token = this.getToken()
  12443.                 if token==null
  12444.                         this.setError('Nothing to parse')
  12445.                         return null
  12446.                 endif
  12447.                 do case
  12448.                 case token=='"'
  12449.                         return this.parseString()
  12450.                 case isdigit(token) or token=='-'
  12451.                         return this.parseNumber()
  12452.                 case token=='n'
  12453.                         return this.expectedKeyword('null',null)
  12454.                 case token=='f'
  12455.                         return this.expectedKeyword('false',.f.)
  12456.                 case token=='t'
  12457.                         return this.expectedKeyword('true',.t.)
  12458.                 case token=='{'
  12459.                         return this.parseObject()
  12460.                 case token=='['
  12461.                         return this.parseArray()
  12462.                 otherwise
  12463.                         this.setError('Unexpected token')
  12464.                 endcase
  12465.         return
  12466.                
  12467.        
  12468.         function expectedKeyword(cWord,eValue)
  12469.                 for i=1 to len(cWord)
  12470.                         cChar = this.getChar()
  12471.                         if cChar <> substr(cWord,i,1)
  12472.                                 this.setError("Expected keyword '" + cWord + "'")
  12473.                                 return ''
  12474.                         endif
  12475.                         this.nPos = this.nPos + 1
  12476.                 next
  12477.         return eValue
  12478.        
  12479.  
  12480.         function parseObject()
  12481.         local retval, cPropName, xValue
  12482.                 retval = createObject('myObj')
  12483.                 this.nPos = this.nPos + 1 && Eat {
  12484.                 if this.getToken()<>'}'
  12485.                         do while .t.
  12486.                                 cPropName = this.parseString()
  12487.                                 if not empty(this.cError)
  12488.                                         return null
  12489.                                 endif
  12490.                                 if this.getToken()<>':'
  12491.                                         this.setError("Expected ':' when parsing object")
  12492.                                         return null
  12493.                                 endif
  12494.                                 this.nPos = this.nPos + 1
  12495.                                 xValue = this.parseValue()
  12496.                                 if not empty(this.cError)
  12497.                                         return null
  12498.                                 endif                          
  12499.                                 ** Debug ? cPropName, type('xValue')
  12500.                                 retval.set(cPropName, xValue)
  12501.                                 if this.getToken()<>','
  12502.                                         exit
  12503.                                 endif
  12504.                                 this.nPos = this.nPos + 1
  12505.                         enddo
  12506.                 endif
  12507.                 if this.getToken()<>'}'
  12508.                         this.setError("Expected '}' at the end of object")
  12509.                         return null
  12510.                 endif
  12511.                 this.nPos = this.nPos + 1
  12512.         return retval
  12513.  
  12514.  
  12515.         function parseArray()
  12516.         local retVal, xValue
  12517.                 retval = createObject('MyArray')
  12518.                 this.nPos = this.nPos + 1       && Eat [
  12519.                 if this.getToken() <> ']'
  12520.                         do while .t.
  12521.                                 xValue = this.parseValue()
  12522.                                 if not empty(this.cError)
  12523.                                         return null
  12524.                                 endif
  12525.                                 retval.add( xValue )
  12526.                                 if this.getToken()<>','
  12527.                                         exit
  12528.                                 endif
  12529.                                 this.nPos = this.nPos + 1
  12530.                         enddo
  12531.                         if this.getToken() <> ']'
  12532.                                 this.setError('Expected ] at the end of array')
  12533.                                 return null
  12534.                         endif
  12535.                 endif
  12536.                 this.nPos = this.nPos + 1
  12537.         return retval
  12538.        
  12539.  
  12540.         function parseString()
  12541.         local cRetVal, c
  12542.                 if this.getToken()<>'"'
  12543.                         this.setError('Expected "')
  12544.                         return ''
  12545.                 endif
  12546.                 this.nPos = this.nPos + 1       && Eat "
  12547.                 cRetVal = ''
  12548.                 do while .t.
  12549.                         c = this.getChar()
  12550.                         if c==''
  12551.                                 return ''
  12552.                         endif
  12553.                         if c == '"'
  12554.                                 this.nPos = this.nPos + 1
  12555.                                 exit
  12556.                         endif
  12557.                         if c == '\'
  12558.                                 this.nPos = this.nPos + 1
  12559.                                 if (this.nPos>this.nLen)
  12560.                                         this.setError('\\ at the end of input')
  12561.                                         return ''
  12562.                                 endif
  12563.                                 c = this.getChar()
  12564.                                 if c==''
  12565.                                         return ''
  12566.                                 endif
  12567.                                 do case
  12568.                                 case c=='"'
  12569.                                         c='"'
  12570.                                 case c=='\'
  12571.                                         c='\'
  12572.                                 case c=='/'
  12573.                                         c='/'
  12574.                                 case c=='b'
  12575.                                         c=chr(8)
  12576.                                 case c=='t'
  12577.                                         c=chr(9)
  12578.                                 case c=='n'
  12579.                                         c=chr(10)
  12580.                                 case c=='f'
  12581.                                         c=chr(12)
  12582.                                 case c=='r'
  12583.                                         c=chr(13)
  12584.                                 otherwise
  12585.                                         ******* FALTAN LOS UNICODE
  12586.                                         this.setError('Invalid escape sequence in string literal')
  12587.                                         return ''
  12588.                                 endcase
  12589.                         endif
  12590.                         cRetVal = cRetVal + c
  12591.                         this.nPos = this.nPos + 1
  12592.                 enddo
  12593.         return cRetVal
  12594.                                        
  12595.  
  12596.         **** Pendiente numeros con E
  12597.         function parseNumber()
  12598.         local nStartPos,c, isInt, cNumero
  12599.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  12600.                         this.setError('Expected number literal')
  12601.                         return 0
  12602.                 endif
  12603.                 nStartPos = this.nPos
  12604.                 c = this.getChar()
  12605.                 if c == '-'
  12606.                         c = this.nextChar()
  12607.                 endif
  12608.                 if c == '0'
  12609.                         c = this.nextChar()
  12610.                 else
  12611.                         if isdigit(c)
  12612.                                 c = this.nextChar()
  12613.                                 do while isdigit(c)
  12614.                                         c = this.nextChar()
  12615.                                 enddo
  12616.                         else
  12617.                                 this.setError('Expected digit when parsing number')
  12618.                                 return 0
  12619.                         endif
  12620.                 endif
  12621.                
  12622.                 isInt = .t.
  12623.                 if c=='.'
  12624.                         c = this.nextChar()
  12625.                         if isdigit(c)
  12626.                                 c = this.nextChar()
  12627.                                 isInt = .f.
  12628.                                 do while isDigit(c)
  12629.                                         c = this.nextChar()
  12630.                                 enddo
  12631.                         else
  12632.                                 this.setError('Expected digit following dot comma')
  12633.                                 return 0
  12634.                         endif
  12635.                 endif
  12636.                
  12637.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  12638.         return val(cNumero)
  12639.  
  12640.  
  12641.  
  12642.         function getToken()
  12643.         local char1
  12644.                 do while .t.
  12645.                         if this.nPos > this.nLen
  12646.                                 return null
  12647.                         endif
  12648.                         char1 = substr(this.cJson, this.nPos, 1)
  12649.                         if char1==' '
  12650.                                 this.nPos = this.nPos + 1
  12651.                                 loop
  12652.                         endif
  12653.                         return char1
  12654.                 enddo
  12655.         return
  12656.        
  12657.                
  12658.                
  12659.         function getChar()
  12660.                 if this.nPos > this.nLen
  12661.                         this.setError('Unexpected end of JSON stream')
  12662.                         return ''
  12663.                 endif
  12664.         return substr(this.cJson, this.nPos, 1)
  12665.        
  12666.         function nextChar()
  12667.                 this.nPos = this.nPos + 1
  12668.                 if this.nPos > this.nLen
  12669.                         return ''
  12670.                 endif
  12671.         return substr(this.cJson, this.nPos, 1)
  12672.        
  12673.         function setError(cMsg)
  12674.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  12675.         return
  12676.  
  12677.  
  12678.         function fixUnicode(cStr)
  12679.                 cStr = StrTran(cStr,'\u00e1','á')
  12680.                 cStr = StrTran(cStr,'\u00e9','é')
  12681.                 cStr = StrTran(cStr,'\u00ed','í')
  12682.                 cStr = StrTran(cStr,'\u00f3','ó')
  12683.                 cStr = StrTran(cStr,'\u00fa','ú')
  12684.                 cStr = StrTran(cStr,'\u00c1','Á')
  12685.                 cStr = StrTran(cStr,'\u00c9','É')
  12686.                 cStr = StrTran(cStr,'\u00cd','Í')
  12687.                 cStr = StrTran(cStr,'\u00d3','Ó')
  12688.                 cStr = StrTran(cStr,'\u00da','Ú')
  12689.                 cStr = StrTran(cStr,'\u00f1','ń')
  12690.                 cStr = StrTran(cStr,'\u00d1','Ń')
  12691.         return cStr
  12692.  
  12693.  
  12694.  
  12695. enddefine
  12696.  
  12697.  
  12698.  
  12699.  
  12700.  
  12701. *
  12702. * class used to return an array
  12703. *
  12704. define class myArray as custom
  12705.         nSize = 0
  12706.         dimension array[1]
  12707.  
  12708.         function add(xExpr)
  12709.                 this.nSize = this.nSize + 1
  12710.                 dimension this.array[this.nSize]
  12711.                 this.array[this.nSize] = xExpr
  12712.         return
  12713.  
  12714.         function get(n)
  12715.         return this.array[n]
  12716.  
  12717. enddefine
  12718.  
  12719.  
  12720.  
  12721. *
  12722. * class used to simulate an object
  12723. * all properties are prefixed with 'prop' to permit property names like: error, init
  12724. * that already exists like vfp methods
  12725. *
  12726. define class myObj as custom
  12727. Hidden ;
  12728.         ClassLibrary,Comment, ;
  12729.         BaseClass,ControlCount, ;
  12730.         Controls,Objects,Object,;
  12731.         Height,HelpContextID,Left,Name, ;
  12732.         Parent,ParentClass,Picture, ;
  12733.         Tag,Top,WhatsThisHelpID,Width
  12734.                
  12735.         function set(cPropName, xValue)
  12736.                 cPropName = '_'+cPropName
  12737.                 if type('this.'+cPropName)=='U'
  12738.                         this.addProperty(cPropName,xValue)
  12739.                 else
  12740.                         local cmd
  12741.                         cmd = 'this.'+cPropName+'=xValue'
  12742.                         &cmd
  12743.                 endif
  12744.         return
  12745.        
  12746.         procedure get (cPropName)
  12747.                 cPropName = '_'+cPropName
  12748.                 If type('this.'+cPropName)=='U'
  12749.                         return ''
  12750.                 Else
  12751.                         local cmd
  12752.                         cmd = 'return this.'+cPropName
  12753.                         &cmd
  12754.                 endif
  12755.         return ''
  12756. enddefine
  12757.  
  12758.  
  12759.  
  12760.  
  12761.  
  12762. function testJsonClass
  12763.         clear
  12764.         set decimal to 10
  12765.         oJson = newObject('json')
  12766.        
  12767.        
  12768.         ? 'Test Basic Types'
  12769.         ? '----------------'
  12770.         ? oJson.decode('null')
  12771.         ? oJson.decode('true')
  12772.         ? oJson.decode('false')
  12773.         ?
  12774.         ? oJson.decode('791123')
  12775.         ? oJson.decode('791123.45')
  12776.         ? oJson.decode('791123.45.')
  12777.         ? oJson.decode('"nacho gtz"')
  12778.         if not empty(oJson.cError)
  12779.                 ? oJson.cError
  12780.                 return
  12781.         endif
  12782.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  12783.         if not empty(oJson.cError)
  12784.                 ? oJson.cError
  12785.                 return
  12786.         endif
  12787.        
  12788.         ? 'Test Array'
  12789.         ? '----------'
  12790.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  12791.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  12792.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  12793.         nombres = arr.get(1)
  12794.         edades  = arr.get(2)
  12795.         ? nombres.get(1), edades.get(1)
  12796.         ? nombres.get(2), edades.get(2)
  12797.         ? nombres.get(3), edades.get(3)
  12798.         ?
  12799.         ? 'Test Object'
  12800.         ? '-----------'
  12801.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  12802.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  12803.         ? obj._Nombre, obj._Edad, obj._IsGood
  12804.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  12805.         ? obj.get('jsonrpc'), obj._jsonrpc
  12806.         ? obj.get('id'), obj._id
  12807.         ? obj.get('method'), obj._method
  12808.         ? obj._Params.array[1], obj._Params.get(1)
  12809.  
  12810.         ?
  12811.         ? 'Test nested object'
  12812.         ? '------------------'
  12813.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  12814.         obj = oJson.decode(cJson)
  12815.         if not empty(oJson.cError)
  12816.                 ? oJson.cError
  12817.                 return
  12818.         endif
  12819.         ? cJson
  12820.         ? 'method -->',obj._method
  12821.         ? 'usrkey -->',obj._params._data._usrkey
  12822.         ? 'sendto -->',obj._params._data._sendto
  12823.         ? 'name  --->',obj._params._data._name
  12824.         ? 'expires ->',obj._params._data._expires
  12825.  
  12826.         ?
  12827.         ? 'Test empty object'
  12828.         ? '-----------------'
  12829.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  12830.         obj = oJson.decode(cJson)
  12831.         if not empty(oJson.cError)     
  12832.                 ? oJson.cError
  12833.                 return
  12834.         endif
  12835.         ? cJson
  12836.         ? 'result -->',obj._result, obj.get('result')
  12837.         oError = obj.get('error')
  12838.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  12839.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  12840.         ? 'id  ----->',obj._id, obj.get('id')
  12841.         ?  type("oError._code")
  12842.  
  12843.         ?
  12844.         ? 'Probar decode-enconde-decode-encode'
  12845.         ? '------------------------------------'
  12846.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  12847.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  12848.         ? cJson
  12849.         oSmtp = json_decode(cJson)
  12850.         cJson =  json_encode(oSmtp)
  12851.         ? cJson
  12852.         oSmtp = json_decode(cJson)
  12853.         cJson =  json_encode(oSmtp)
  12854.         ? cJson
  12855.  
  12856.         * Probar falla
  12857.         ?
  12858.         ? 'Probar una falla en el json'
  12859.         ? '---------------------------'
  12860.         cJson = ' {"server":"", "user":"", "password":"" ,'
  12861.         oSmtp = json_decode(cJson)
  12862.         if not empty(json_getErrorMsg())
  12863.                 ? json_getErrorMsg()
  12864.         endif
  12865.  
  12866.         ?
  12867.         ? 'Pruebas Finalizadas'
  12868. retur
  12869. *               json_decode(cJson)  te regresa el objeto representado en cJson
  12870. *
  12871. * Tambien puedes usar directamente la clase:
  12872. *
  12873. *       oJson = newobject('json','json.prg')
  12874. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  12875. *       ? oJson.encode(oCliente)
  12876. *       ? oCliente.get('nombre')
  12877. *       ? oCliente.get('apellido')
  12878. *
  12879. *
  12880. * VFPJSON  Encode and Decode JSON for VFP
  12881. * Examples:
  12882. *       oJson = newobject('json','json.prg')
  12883. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  12884. *       ? oJson.encode(oCustomer)
  12885. *       ? oCustomer.get('name')
  12886. *       ? oCustomer.get('lastname')
  12887. *
  12888. *
  12889. lRunTest = .f.
  12890. if lRunTest
  12891.         testJsonClass()
  12892. endif
  12893. return
  12894.  
  12895.  
  12896. function json_encode(xExpr)
  12897.         if vartype(_json)<>'O'
  12898.                 public _json
  12899.                 _json = newobject('json')
  12900.         endif
  12901. return _json.encode(@xExpr)
  12902.  
  12903.  
  12904. function json_decode(cJson)
  12905. local retval
  12906.         if vartype(_json)<>'O'
  12907.                 public _json
  12908.                 _json = newobject('json')
  12909.         endif
  12910.         retval = _json.decode(cJson)
  12911.         if not empty(_json.cError)
  12912.                 return null
  12913.         endif
  12914. return retval
  12915.  
  12916. function json_getErrorMsg()
  12917. return _json.cError
  12918.        
  12919.  
  12920.  
  12921. *
  12922. * json class
  12923. *
  12924. *
  12925. define class json as custom
  12926.  
  12927.  
  12928.         nPos=0
  12929.         nLen=0
  12930.         cJson=''
  12931.         cError=''
  12932.  
  12933.  
  12934.         *
  12935.         * Genera el codigo cJson para parametro que se manda
  12936.         *
  12937.         function encode(xExpr)
  12938.         local cTipo
  12939.                 * Cuando se manda una arreglo,
  12940.                 if type('ALen(xExpr)')=='N'
  12941.                         cTipo = 'A'
  12942.                 Else
  12943.                         cTipo = VarType(xExpr)
  12944.                 Endif
  12945.                
  12946.                 Do Case
  12947.                 Case cTipo=='D'
  12948.                         return '"'+dtos(xExpr)+'"'
  12949.                 Case cTipo=='N'
  12950.                         return Transform(xExpr)
  12951.                 Case cTipo=='L'
  12952.                         return iif(xExpr,'true','false')
  12953.                 Case cTipo=='X'
  12954.                         return 'null'
  12955.                 Case cTipo=='C'
  12956.                         xExpr = allt(xExpr)
  12957.                         xExpr = StrTran(xExpr, '\', '\\' )
  12958.                         xExpr = StrTran(xExpr, '/', '\/' )
  12959.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  12960.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  12961.                         xExpr = StrTran(xExpr, '"', '\"' )
  12962.                         return '"'+xExpr+'"'
  12963.  
  12964.                 case cTipo=='O'
  12965.                         local cProp, cJsonValue, cRetVal, aProp[1]
  12966.                         =AMembers(aProp,xExpr)
  12967.                         cRetVal = ''
  12968.                         for each cProp in aProp
  12969.                                 *? cProp
  12970.                                 *? cRetVal
  12971.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  12972.                                         * algunas propiedades pueden no estar definidas
  12973.                                         * como: activecontrol, parent, etc
  12974.                                         loop
  12975.                                 endif
  12976.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  12977.                                         *
  12978.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  12979.                                         *
  12980.                                         Local i,nTotElem
  12981.                                         cJsonValue = ''
  12982.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  12983.                                         For i=1 to nTotElem
  12984.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  12985.                                         Next
  12986.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  12987.                                 else
  12988.                                         *
  12989.                                         * es otro tipo de dato normal C, N, L
  12990.                                         *
  12991.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  12992.                                 endif
  12993.                                 if left(cProp,1)=='_'
  12994.                                         cProp = substr(cProp,2)
  12995.                                 endif
  12996.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  12997.                         next
  12998.                         return '{' + substr(cRetVal,2) + '}'
  12999.  
  13000.                 case cTipo=='A'
  13001.                         local valor, cRetVal
  13002.                         cRetVal = ''   
  13003.                         for each valor in xExpr
  13004.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  13005.                         next
  13006.                         return  '[' + substr(cRetVal,2) + ']'
  13007.                        
  13008.                 endcase
  13009.  
  13010.         return ''
  13011.  
  13012.  
  13013.  
  13014.  
  13015.  
  13016.         *
  13017.         * regresa un elemento representado por la cadena json que se manda
  13018.         *
  13019.        
  13020.         function decode(cJson)
  13021.         local retValue
  13022.                 cJson = StrTran(cJson,chr(9),'')
  13023.                 cJson = StrTran(cJson,chr(10),'')
  13024.                 cJson = StrTran(cJson,chr(13),'')
  13025.                 cJson = this.fixUnicode(cJson)
  13026.                 this.nPos  = 1
  13027.                 this.cJson = cJson
  13028.                 this.nLen  = len(cJson)
  13029.                 this.cError = ''
  13030.                 retValue = this.parsevalue()
  13031.                 if not empty(this.cError)
  13032.                         return null
  13033.                 endif
  13034.                 if this.getToken()<>null
  13035.                         this.setError('Junk at the end of JSON input')
  13036.                         return null
  13037.                 endif
  13038.         return retValue
  13039.                
  13040.        
  13041.         function parseValue()
  13042.         local token
  13043.                 token = this.getToken()
  13044.                 if token==null
  13045.                         this.setError('Nothing to parse')
  13046.                         return null
  13047.                 endif
  13048.                 do case
  13049.                 case token=='"'
  13050.                         return this.parseString()
  13051.                 case isdigit(token) or token=='-'
  13052.                         return this.parseNumber()
  13053.                 case token=='n'
  13054.                         return this.expectedKeyword('null',null)
  13055.                 case token=='f'
  13056.                         return this.expectedKeyword('false',.f.)
  13057.                 case token=='t'
  13058.                         return this.expectedKeyword('true',.t.)
  13059.                 case token=='{'
  13060.                         return this.parseObject()
  13061.                 case token=='['
  13062.                         return this.parseArray()
  13063.                 otherwise
  13064.                         this.setError('Unexpected token')
  13065.                 endcase
  13066.         return
  13067.                
  13068.        
  13069.         function expectedKeyword(cWord,eValue)
  13070.                 for i=1 to len(cWord)
  13071.                         cChar = this.getChar()
  13072.                         if cChar <> substr(cWord,i,1)
  13073.                                 this.setError("Expected keyword '" + cWord + "'")
  13074.                                 return ''
  13075.                         endif
  13076.                         this.nPos = this.nPos + 1
  13077.                 next
  13078.         return eValue
  13079.        
  13080.  
  13081.         function parseObject()
  13082.         local retval, cPropName, xValue
  13083.                 retval = createObject('myObj')
  13084.                 this.nPos = this.nPos + 1 && Eat {
  13085.                 if this.getToken()<>'}'
  13086.                         do while .t.
  13087.                                 cPropName = this.parseString()
  13088.                                 if not empty(this.cError)
  13089.                                         return null
  13090.                                 endif
  13091.                                 if this.getToken()<>':'
  13092.                                         this.setError("Expected ':' when parsing object")
  13093.                                         return null
  13094.                                 endif
  13095.                                 this.nPos = this.nPos + 1
  13096.                                 xValue = this.parseValue()
  13097.                                 if not empty(this.cError)
  13098.                                         return null
  13099.                                 endif                          
  13100.                                 ** Debug ? cPropName, type('xValue')
  13101.                                 retval.set(cPropName, xValue)
  13102.                                 if this.getToken()<>','
  13103.                                         exit
  13104.                                 endif
  13105.                                 this.nPos = this.nPos + 1
  13106.                         enddo
  13107.                 endif
  13108.                 if this.getToken()<>'}'
  13109.                         this.setError("Expected '}' at the end of object")
  13110.                         return null
  13111.                 endif
  13112.                 this.nPos = this.nPos + 1
  13113.         return retval
  13114.  
  13115.  
  13116.         function parseArray()
  13117.         local retVal, xValue
  13118.                 retval = createObject('MyArray')
  13119.                 this.nPos = this.nPos + 1       && Eat [
  13120.                 if this.getToken() <> ']'
  13121.                         do while .t.
  13122.                                 xValue = this.parseValue()
  13123.                                 if not empty(this.cError)
  13124.                                         return null
  13125.                                 endif
  13126.                                 retval.add( xValue )
  13127.                                 if this.getToken()<>','
  13128.                                         exit
  13129.                                 endif
  13130.                                 this.nPos = this.nPos + 1
  13131.                         enddo
  13132.                         if this.getToken() <> ']'
  13133.                                 this.setError('Expected ] at the end of array')
  13134.                                 return null
  13135.                         endif
  13136.                 endif
  13137.                 this.nPos = this.nPos + 1
  13138.         return retval
  13139.        
  13140.  
  13141.         function parseString()
  13142.         local cRetVal, c
  13143.                 if this.getToken()<>'"'
  13144.                         this.setError('Expected "')
  13145.                         return ''
  13146.                 endif
  13147.                 this.nPos = this.nPos + 1       && Eat "
  13148.                 cRetVal = ''
  13149.                 do while .t.
  13150.                         c = this.getChar()
  13151.                         if c==''
  13152.                                 return ''
  13153.                         endif
  13154.                         if c == '"'
  13155.                                 this.nPos = this.nPos + 1
  13156.                                 exit
  13157.                         endif
  13158.                         if c == '\'
  13159.                                 this.nPos = this.nPos + 1
  13160.                                 if (this.nPos>this.nLen)
  13161.                                         this.setError('\\ at the end of input')
  13162.                                         return ''
  13163.                                 endif
  13164.                                 c = this.getChar()
  13165.                                 if c==''
  13166.                                         return ''
  13167.                                 endif
  13168.                                 do case
  13169.                                 case c=='"'
  13170.                                         c='"'
  13171.                                 case c=='\'
  13172.                                         c='\'
  13173.                                 case c=='/'
  13174.                                         c='/'
  13175.                                 case c=='b'
  13176.                                         c=chr(8)
  13177.                                 case c=='t'
  13178.                                         c=chr(9)
  13179.                                 case c=='n'
  13180.                                         c=chr(10)
  13181.                                 case c=='f'
  13182.                                         c=chr(12)
  13183.                                 case c=='r'
  13184.                                         c=chr(13)
  13185.                                 otherwise
  13186.                                         ******* FALTAN LOS UNICODE
  13187.                                         this.setError('Invalid escape sequence in string literal')
  13188.                                         return ''
  13189.                                 endcase
  13190.                         endif
  13191.                         cRetVal = cRetVal + c
  13192.                         this.nPos = this.nPos + 1
  13193.                 enddo
  13194.         return cRetVal
  13195.                                        
  13196.  
  13197.         **** Pendiente numeros con E
  13198.         function parseNumber()
  13199.         local nStartPos,c, isInt, cNumero
  13200.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  13201.                         this.setError('Expected number literal')
  13202.                         return 0
  13203.                 endif
  13204.                 nStartPos = this.nPos
  13205.                 c = this.getChar()
  13206.                 if c == '-'
  13207.                         c = this.nextChar()
  13208.                 endif
  13209.                 if c == '0'
  13210.                         c = this.nextChar()
  13211.                 else
  13212.                         if isdigit(c)
  13213.                                 c = this.nextChar()
  13214.                                 do while isdigit(c)
  13215.                                         c = this.nextChar()
  13216.                                 enddo
  13217.                         else
  13218.                                 this.setError('Expected digit when parsing number')
  13219.                                 return 0
  13220.                         endif
  13221.                 endif
  13222.                
  13223.                 isInt = .t.
  13224.                 if c=='.'
  13225.                         c = this.nextChar()
  13226.                         if isdigit(c)
  13227.                                 c = this.nextChar()
  13228.                                 isInt = .f.
  13229.                                 do while isDigit(c)
  13230.                                         c = this.nextChar()
  13231.                                 enddo
  13232.                         else
  13233.                                 this.setError('Expected digit following dot comma')
  13234.                                 return 0
  13235.                         endif
  13236.                 endif
  13237.                
  13238.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  13239.         return val(cNumero)
  13240.  
  13241.  
  13242.  
  13243.         function getToken()
  13244.         local char1
  13245.                 do while .t.
  13246.                         if this.nPos > this.nLen
  13247.                                 return null
  13248.                         endif
  13249.                         char1 = substr(this.cJson, this.nPos, 1)
  13250.                         if char1==' '
  13251.                                 this.nPos = this.nPos + 1
  13252.                                 loop
  13253.                         endif
  13254.                         return char1
  13255.                 enddo
  13256.         return
  13257.        
  13258.                
  13259.                
  13260.         function getChar()
  13261.                 if this.nPos > this.nLen
  13262.                         this.setError('Unexpected end of JSON stream')
  13263.                         return ''
  13264.                 endif
  13265.         return substr(this.cJson, this.nPos, 1)
  13266.        
  13267.         function nextChar()
  13268.                 this.nPos = this.nPos + 1
  13269.                 if this.nPos > this.nLen
  13270.                         return ''
  13271.                 endif
  13272.         return substr(this.cJson, this.nPos, 1)
  13273.        
  13274.         function setError(cMsg)
  13275.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  13276.         return
  13277.  
  13278.  
  13279.         function fixUnicode(cStr)
  13280.                 cStr = StrTran(cStr,'\u00e1','á')
  13281.                 cStr = StrTran(cStr,'\u00e9','é')
  13282.                 cStr = StrTran(cStr,'\u00ed','í')
  13283.                 cStr = StrTran(cStr,'\u00f3','ó')
  13284.                 cStr = StrTran(cStr,'\u00fa','ú')
  13285.                 cStr = StrTran(cStr,'\u00c1','Á')
  13286.                 cStr = StrTran(cStr,'\u00c9','É')
  13287.                 cStr = StrTran(cStr,'\u00cd','Í')
  13288.                 cStr = StrTran(cStr,'\u00d3','Ó')
  13289.                 cStr = StrTran(cStr,'\u00da','Ú')
  13290.                 cStr = StrTran(cStr,'\u00f1','ń')
  13291.                 cStr = StrTran(cStr,'\u00d1','Ń')
  13292.         return cStr
  13293.  
  13294.  
  13295.  
  13296. enddefine
  13297.  
  13298.  
  13299.  
  13300.  
  13301.  
  13302. *
  13303. * class used to return an array
  13304. *
  13305. define class myArray as custom
  13306.         nSize = 0
  13307.         dimension array[1]
  13308.  
  13309.         function add(xExpr)
  13310.                 this.nSize = this.nSize + 1
  13311.                 dimension this.array[this.nSize]
  13312.                 this.array[this.nSize] = xExpr
  13313.         return
  13314.  
  13315.         function get(n)
  13316.         return this.array[n]
  13317.  
  13318. enddefine
  13319.  
  13320.  
  13321.  
  13322. *
  13323. * class used to simulate an object
  13324. * all properties are prefixed with 'prop' to permit property names like: error, init
  13325. * that already exists like vfp methods
  13326. *
  13327. define class myObj as custom
  13328. Hidden ;
  13329.         ClassLibrary,Comment, ;
  13330.         BaseClass,ControlCount, ;
  13331.         Controls,Objects,Object,;
  13332.         Height,HelpContextID,Left,Name, ;
  13333.         Parent,ParentClass,Picture, ;
  13334.         Tag,Top,WhatsThisHelpID,Width
  13335.                
  13336.         function set(cPropName, xValue)
  13337.                 cPropName = '_'+cPropName
  13338.                 if type('this.'+cPropName)=='U'
  13339.                         this.addProperty(cPropName,xValue)
  13340.                 else
  13341.                         local cmd
  13342.                         cmd = 'this.'+cPropName+'=xValue'
  13343.                         &cmd
  13344.                 endif
  13345.         return
  13346.        
  13347.         procedure get (cPropName)
  13348.                 cPropName = '_'+cPropName
  13349.                 If type('this.'+cPropName)=='U'
  13350.                         return ''
  13351.                 Else
  13352.                         local cmd
  13353.                         cmd = 'return this.'+cPropName
  13354.                         &cmd
  13355.                 endif
  13356.         return ''
  13357. enddefine
  13358.  
  13359.  
  13360.  
  13361.  
  13362.  
  13363. function testJsonClass
  13364.         clear
  13365.         set decimal to 10
  13366.         oJson = newObject('json')
  13367.        
  13368.        
  13369.         ? 'Test Basic Types'
  13370.         ? '----------------'
  13371.         ? oJson.decode('null')
  13372.         ? oJson.decode('true')
  13373.         ? oJson.decode('false')
  13374.         ?
  13375.         ? oJson.decode('791123')
  13376.         ? oJson.decode('791123.45')
  13377.         ? oJson.decode('791123.45.')
  13378.         ? oJson.decode('"nacho gtz"')
  13379.         if not empty(oJson.cError)
  13380.                 ? oJson.cError
  13381.                 return
  13382.         endif
  13383.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  13384.         if not empty(oJson.cError)
  13385.                 ? oJson.cError
  13386.                 return
  13387.         endif
  13388.        
  13389.         ? 'Test Array'
  13390.         ? '----------'
  13391.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  13392.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  13393.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  13394.         nombres = arr.get(1)
  13395.         edades  = arr.get(2)
  13396.         ? nombres.get(1), edades.get(1)
  13397.         ? nombres.get(2), edades.get(2)
  13398.         ? nombres.get(3), edades.get(3)
  13399.         ?
  13400.         ? 'Test Object'
  13401.         ? '-----------'
  13402.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  13403.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  13404.         ? obj._Nombre, obj._Edad, obj._IsGood
  13405.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  13406.         ? obj.get('jsonrpc'), obj._jsonrpc
  13407.         ? obj.get('id'), obj._id
  13408.         ? obj.get('method'), obj._method
  13409.         ? obj._Params.array[1], obj._Params.get(1)
  13410.  
  13411.         ?
  13412.         ? 'Test nested object'
  13413.         ? '------------------'
  13414.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  13415.         obj = oJson.decode(cJson)
  13416.         if not empty(oJson.cError)
  13417.                 ? oJson.cError
  13418.                 return
  13419.         endif
  13420.         ? cJson
  13421.         ? 'method -->',obj._method
  13422.         ? 'usrkey -->',obj._params._data._usrkey
  13423.         ? 'sendto -->',obj._params._data._sendto
  13424.         ? 'name  --->',obj._params._data._name
  13425.         ? 'expires ->',obj._params._data._expires
  13426.  
  13427.         ?
  13428.         ? 'Test empty object'
  13429.         ? '-----------------'
  13430.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  13431.         obj = oJson.decode(cJson)
  13432.         if not empty(oJson.cError)     
  13433.                 ? oJson.cError
  13434.                 return
  13435.         endif
  13436.         ? cJson
  13437.         ? 'result -->',obj._result, obj.get('result')
  13438.         oError = obj.get('error')
  13439.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  13440.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  13441.         ? 'id  ----->',obj._id, obj.get('id')
  13442.         ?  type("oError._code")
  13443.  
  13444.         ?
  13445.         ? 'Probar decode-enconde-decode-encode'
  13446.         ? '------------------------------------'
  13447.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  13448.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  13449.         ? cJson
  13450.         oSmtp = json_decode(cJson)
  13451.         cJson =  json_encode(oSmtp)
  13452.         ? cJson
  13453.         oSmtp = json_decode(cJson)
  13454.         cJson =  json_encode(oSmtp)
  13455.         ? cJson
  13456.  
  13457.         * Probar falla
  13458.         ?
  13459.         ? 'Probar una falla en el json'
  13460.         ? '---------------------------'
  13461.         cJson = ' {"server":"", "user":"", "password":"" ,'
  13462.         oSmtp = json_decode(cJson)
  13463.         if not empty(json_getErrorMsg())
  13464.                 ? json_getErrorMsg()
  13465.         endif
  13466.  
  13467.         ?
  13468.         ? 'Pruebas Finalizadas'
  13469. retur
  13470. *
  13471. * Tambien puedes usar directamente la clase:
  13472. *
  13473. *       oJson = newobject('json','json.prg')
  13474. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  13475. *       ? oJson.encode(oCliente)
  13476. *       ? oCliente.get('nombre')
  13477. *       ? oCliente.get('apellido')
  13478. *
  13479. *
  13480. * VFPJSON  Encode and Decode JSON for VFP
  13481. * Examples:
  13482. *       oJson = newobject('json','json.prg')
  13483. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  13484. *       ? oJson.encode(oCustomer)
  13485. *       ? oCustomer.get('name')
  13486. *       ? oCustomer.get('lastname')
  13487. *
  13488. *
  13489. lRunTest = .f.
  13490. if lRunTest
  13491.         testJsonClass()
  13492. endif
  13493. return
  13494.  
  13495.  
  13496. function json_encode(xExpr)
  13497.         if vartype(_json)<>'O'
  13498.                 public _json
  13499.                 _json = newobject('json')
  13500.         endif
  13501. return _json.encode(@xExpr)
  13502.  
  13503.  
  13504. function json_decode(cJson)
  13505. local retval
  13506.         if vartype(_json)<>'O'
  13507.                 public _json
  13508.                 _json = newobject('json')
  13509.         endif
  13510.         retval = _json.decode(cJson)
  13511.         if not empty(_json.cError)
  13512.                 return null
  13513.         endif
  13514. return retval
  13515.  
  13516. function json_getErrorMsg()
  13517. return _json.cError
  13518.        
  13519.  
  13520.  
  13521. *
  13522. * json class
  13523. *
  13524. *
  13525. define class json as custom
  13526.  
  13527.  
  13528.         nPos=0
  13529.         nLen=0
  13530.         cJson=''
  13531.         cError=''
  13532.  
  13533.  
  13534.         *
  13535.         * Genera el codigo cJson para parametro que se manda
  13536.         *
  13537.         function encode(xExpr)
  13538.         local cTipo
  13539.                 * Cuando se manda una arreglo,
  13540.                 if type('ALen(xExpr)')=='N'
  13541.                         cTipo = 'A'
  13542.                 Else
  13543.                         cTipo = VarType(xExpr)
  13544.                 Endif
  13545.                
  13546.                 Do Case
  13547.                 Case cTipo=='D'
  13548.                         return '"'+dtos(xExpr)+'"'
  13549.                 Case cTipo=='N'
  13550.                         return Transform(xExpr)
  13551.                 Case cTipo=='L'
  13552.                         return iif(xExpr,'true','false')
  13553.                 Case cTipo=='X'
  13554.                         return 'null'
  13555.                 Case cTipo=='C'
  13556.                         xExpr = allt(xExpr)
  13557.                         xExpr = StrTran(xExpr, '\', '\\' )
  13558.                         xExpr = StrTran(xExpr, '/', '\/' )
  13559.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  13560.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  13561.                         xExpr = StrTran(xExpr, '"', '\"' )
  13562.                         return '"'+xExpr+'"'
  13563.  
  13564.                 case cTipo=='O'
  13565.                         local cProp, cJsonValue, cRetVal, aProp[1]
  13566.                         =AMembers(aProp,xExpr)
  13567.                         cRetVal = ''
  13568.                         for each cProp in aProp
  13569.                                 *? cProp
  13570.                                 *? cRetVal
  13571.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  13572.                                         * algunas propiedades pueden no estar definidas
  13573.                                         * como: activecontrol, parent, etc
  13574.                                         loop
  13575.                                 endif
  13576.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  13577.                                         *
  13578.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  13579.                                         *
  13580.                                         Local i,nTotElem
  13581.                                         cJsonValue = ''
  13582.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  13583.                                         For i=1 to nTotElem
  13584.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  13585.                                         Next
  13586.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  13587.                                 else
  13588.                                         *
  13589.                                         * es otro tipo de dato normal C, N, L
  13590.                                         *
  13591.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  13592.                                 endif
  13593.                                 if left(cProp,1)=='_'
  13594.                                         cProp = substr(cProp,2)
  13595.                                 endif
  13596.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  13597.                         next
  13598.                         return '{' + substr(cRetVal,2) + '}'
  13599.  
  13600.                 case cTipo=='A'
  13601.                         local valor, cRetVal
  13602.                         cRetVal = ''   
  13603.                         for each valor in xExpr
  13604.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  13605.                         next
  13606.                         return  '[' + substr(cRetVal,2) + ']'
  13607.                        
  13608.                 endcase
  13609.  
  13610.         return ''
  13611.  
  13612.  
  13613.  
  13614.  
  13615.  
  13616.         *
  13617.         * regresa un elemento representado por la cadena json que se manda
  13618.         *
  13619.        
  13620.         function decode(cJson)
  13621.         local retValue
  13622.                 cJson = StrTran(cJson,chr(9),'')
  13623.                 cJson = StrTran(cJson,chr(10),'')
  13624.                 cJson = StrTran(cJson,chr(13),'')
  13625.                 cJson = this.fixUnicode(cJson)
  13626.                 this.nPos  = 1
  13627.                 this.cJson = cJson
  13628.                 this.nLen  = len(cJson)
  13629.                 this.cError = ''
  13630.                 retValue = this.parsevalue()
  13631.                 if not empty(this.cError)
  13632.                         return null
  13633.                 endif
  13634.                 if this.getToken()<>null
  13635.                         this.setError('Junk at the end of JSON input')
  13636.                         return null
  13637.                 endif
  13638.         return retValue
  13639.                
  13640.        
  13641.         function parseValue()
  13642.         local token
  13643.                 token = this.getToken()
  13644.                 if token==null
  13645.                         this.setError('Nothing to parse')
  13646.                         return null
  13647.                 endif
  13648.                 do case
  13649.                 case token=='"'
  13650.                         return this.parseString()
  13651.                 case isdigit(token) or token=='-'
  13652.                         return this.parseNumber()
  13653.                 case token=='n'
  13654.                         return this.expectedKeyword('null',null)
  13655.                 case token=='f'
  13656.                         return this.expectedKeyword('false',.f.)
  13657.                 case token=='t'
  13658.                         return this.expectedKeyword('true',.t.)
  13659.                 case token=='{'
  13660.                         return this.parseObject()
  13661.                 case token=='['
  13662.                         return this.parseArray()
  13663.                 otherwise
  13664.                         this.setError('Unexpected token')
  13665.                 endcase
  13666.         return
  13667.                
  13668.        
  13669.         function expectedKeyword(cWord,eValue)
  13670.                 for i=1 to len(cWord)
  13671.                         cChar = this.getChar()
  13672.                         if cChar <> substr(cWord,i,1)
  13673.                                 this.setError("Expected keyword '" + cWord + "'")
  13674.                                 return ''
  13675.                         endif
  13676.                         this.nPos = this.nPos + 1
  13677.                 next
  13678.         return eValue
  13679.        
  13680.  
  13681.         function parseObject()
  13682.         local retval, cPropName, xValue
  13683.                 retval = createObject('myObj')
  13684.                 this.nPos = this.nPos + 1 && Eat {
  13685.                 if this.getToken()<>'}'
  13686.                         do while .t.
  13687.                                 cPropName = this.parseString()
  13688.                                 if not empty(this.cError)
  13689.                                         return null
  13690.                                 endif
  13691.                                 if this.getToken()<>':'
  13692.                                         this.setError("Expected ':' when parsing object")
  13693.                                         return null
  13694.                                 endif
  13695.                                 this.nPos = this.nPos + 1
  13696.                                 xValue = this.parseValue()
  13697.                                 if not empty(this.cError)
  13698.                                         return null
  13699.                                 endif                          
  13700.                                 ** Debug ? cPropName, type('xValue')
  13701.                                 retval.set(cPropName, xValue)
  13702.                                 if this.getToken()<>','
  13703.                                         exit
  13704.                                 endif
  13705.                                 this.nPos = this.nPos + 1
  13706.                         enddo
  13707.                 endif
  13708.                 if this.getToken()<>'}'
  13709.                         this.setError("Expected '}' at the end of object")
  13710.                         return null
  13711.                 endif
  13712.                 this.nPos = this.nPos + 1
  13713.         return retval
  13714.  
  13715.  
  13716.         function parseArray()
  13717.         local retVal, xValue
  13718.                 retval = createObject('MyArray')
  13719.                 this.nPos = this.nPos + 1       && Eat [
  13720.                 if this.getToken() <> ']'
  13721.                         do while .t.
  13722.                                 xValue = this.parseValue()
  13723.                                 if not empty(this.cError)
  13724.                                         return null
  13725.                                 endif
  13726.                                 retval.add( xValue )
  13727.                                 if this.getToken()<>','
  13728.                                         exit
  13729.                                 endif
  13730.                                 this.nPos = this.nPos + 1
  13731.                         enddo
  13732.                         if this.getToken() <> ']'
  13733.                                 this.setError('Expected ] at the end of array')
  13734.                                 return null
  13735.                         endif
  13736.                 endif
  13737.                 this.nPos = this.nPos + 1
  13738.         return retval
  13739.        
  13740.  
  13741.         function parseString()
  13742.         local cRetVal, c
  13743.                 if this.getToken()<>'"'
  13744.                         this.setError('Expected "')
  13745.                         return ''
  13746.                 endif
  13747.                 this.nPos = this.nPos + 1       && Eat "
  13748.                 cRetVal = ''
  13749.                 do while .t.
  13750.                         c = this.getChar()
  13751.                         if c==''
  13752.                                 return ''
  13753.                         endif
  13754.                         if c == '"'
  13755.                                 this.nPos = this.nPos + 1
  13756.                                 exit
  13757.                         endif
  13758.                         if c == '\'
  13759.                                 this.nPos = this.nPos + 1
  13760.                                 if (this.nPos>this.nLen)
  13761.                                         this.setError('\\ at the end of input')
  13762.                                         return ''
  13763.                                 endif
  13764.                                 c = this.getChar()
  13765.                                 if c==''
  13766.                                         return ''
  13767.                                 endif
  13768.                                 do case
  13769.                                 case c=='"'
  13770.                                         c='"'
  13771.                                 case c=='\'
  13772.                                         c='\'
  13773.                                 case c=='/'
  13774.                                         c='/'
  13775.                                 case c=='b'
  13776.                                         c=chr(8)
  13777.                                 case c=='t'
  13778.                                         c=chr(9)
  13779.                                 case c=='n'
  13780.                                         c=chr(10)
  13781.                                 case c=='f'
  13782.                                         c=chr(12)
  13783.                                 case c=='r'
  13784.                                         c=chr(13)
  13785.                                 otherwise
  13786.                                         ******* FALTAN LOS UNICODE
  13787.                                         this.setError('Invalid escape sequence in string literal')
  13788.                                         return ''
  13789.                                 endcase
  13790.                         endif
  13791.                         cRetVal = cRetVal + c
  13792.                         this.nPos = this.nPos + 1
  13793.                 enddo
  13794.         return cRetVal
  13795.                                        
  13796.  
  13797.         **** Pendiente numeros con E
  13798.         function parseNumber()
  13799.         local nStartPos,c, isInt, cNumero
  13800.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  13801.                         this.setError('Expected number literal')
  13802.                         return 0
  13803.                 endif
  13804.                 nStartPos = this.nPos
  13805.                 c = this.getChar()
  13806.                 if c == '-'
  13807.                         c = this.nextChar()
  13808.                 endif
  13809.                 if c == '0'
  13810.                         c = this.nextChar()
  13811.                 else
  13812.                         if isdigit(c)
  13813.                                 c = this.nextChar()
  13814.                                 do while isdigit(c)
  13815.                                         c = this.nextChar()
  13816.                                 enddo
  13817.                         else
  13818.                                 this.setError('Expected digit when parsing number')
  13819.                                 return 0
  13820.                         endif
  13821.                 endif
  13822.                
  13823.                 isInt = .t.
  13824.                 if c=='.'
  13825.                         c = this.nextChar()
  13826.                         if isdigit(c)
  13827.                                 c = this.nextChar()
  13828.                                 isInt = .f.
  13829.                                 do while isDigit(c)
  13830.                                         c = this.nextChar()
  13831.                                 enddo
  13832.                         else
  13833.                                 this.setError('Expected digit following dot comma')
  13834.                                 return 0
  13835.                         endif
  13836.                 endif
  13837.                
  13838.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  13839.         return val(cNumero)
  13840.  
  13841.  
  13842.  
  13843.         function getToken()
  13844.         local char1
  13845.                 do while .t.
  13846.                         if this.nPos > this.nLen
  13847.                                 return null
  13848.                         endif
  13849.                         char1 = substr(this.cJson, this.nPos, 1)
  13850.                         if char1==' '
  13851.                                 this.nPos = this.nPos + 1
  13852.                                 loop
  13853.                         endif
  13854.                         return char1
  13855.                 enddo
  13856.         return
  13857.        
  13858.                
  13859.                
  13860.         function getChar()
  13861.                 if this.nPos > this.nLen
  13862.                         this.setError('Unexpected end of JSON stream')
  13863.                         return ''
  13864.                 endif
  13865.         return substr(this.cJson, this.nPos, 1)
  13866.        
  13867.         function nextChar()
  13868.                 this.nPos = this.nPos + 1
  13869.                 if this.nPos > this.nLen
  13870.                         return ''
  13871.                 endif
  13872.         return substr(this.cJson, this.nPos, 1)
  13873.        
  13874.         function setError(cMsg)
  13875.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  13876.         return
  13877.  
  13878.  
  13879.         function fixUnicode(cStr)
  13880.                 cStr = StrTran(cStr,'\u00e1','á')
  13881.                 cStr = StrTran(cStr,'\u00e9','é')
  13882.                 cStr = StrTran(cStr,'\u00ed','í')
  13883.                 cStr = StrTran(cStr,'\u00f3','ó')
  13884.                 cStr = StrTran(cStr,'\u00fa','ú')
  13885.                 cStr = StrTran(cStr,'\u00c1','Á')
  13886.                 cStr = StrTran(cStr,'\u00c9','É')
  13887.                 cStr = StrTran(cStr,'\u00cd','Í')
  13888.                 cStr = StrTran(cStr,'\u00d3','Ó')
  13889.                 cStr = StrTran(cStr,'\u00da','Ú')
  13890.                 cStr = StrTran(cStr,'\u00f1','ń')
  13891.                 cStr = StrTran(cStr,'\u00d1','Ń')
  13892.         return cStr
  13893.  
  13894.  
  13895.  
  13896. enddefine
  13897.  
  13898.  
  13899.  
  13900.  
  13901.  
  13902. *
  13903. * class used to return an array
  13904. *
  13905. define class myArray as custom
  13906.         nSize = 0
  13907.         dimension array[1]
  13908.  
  13909.         function add(xExpr)
  13910.                 this.nSize = this.nSize + 1
  13911.                 dimension this.array[this.nSize]
  13912.                 this.array[this.nSize] = xExpr
  13913.         return
  13914.  
  13915.         function get(n)
  13916.         return this.array[n]
  13917.  
  13918. enddefine
  13919.  
  13920.  
  13921.  
  13922. *
  13923. * class used to simulate an object
  13924. * all properties are prefixed with 'prop' to permit property names like: error, init
  13925. * that already exists like vfp methods
  13926. *
  13927. define class myObj as custom
  13928. Hidden ;
  13929.         ClassLibrary,Comment, ;
  13930.         BaseClass,ControlCount, ;
  13931.         Controls,Objects,Object,;
  13932.         Height,HelpContextID,Left,Name, ;
  13933.         Parent,ParentClass,Picture, ;
  13934.         Tag,Top,WhatsThisHelpID,Width
  13935.                
  13936.         function set(cPropName, xValue)
  13937.                 cPropName = '_'+cPropName
  13938.                 if type('this.'+cPropName)=='U'
  13939.                         this.addProperty(cPropName,xValue)
  13940.                 else
  13941.                         local cmd
  13942.                         cmd = 'this.'+cPropName+'=xValue'
  13943.                         &cmd
  13944.                 endif
  13945.         return
  13946.        
  13947.         procedure get (cPropName)
  13948.                 cPropName = '_'+cPropName
  13949.                 If type('this.'+cPropName)=='U'
  13950.                         return ''
  13951.                 Else
  13952.                         local cmd
  13953.                         cmd = 'return this.'+cPropName
  13954.                         &cmd
  13955.                 endif
  13956.         return ''
  13957. enddefine
  13958.  
  13959.  
  13960.  
  13961.  
  13962.  
  13963. function testJsonClass
  13964.         clear
  13965.         set decimal to 10
  13966.         oJson = newObject('json')
  13967.        
  13968.        
  13969.         ? 'Test Basic Types'
  13970.         ? '----------------'
  13971.         ? oJson.decode('null')
  13972.         ? oJson.decode('true')
  13973.         ? oJson.decode('false')
  13974.         ?
  13975.         ? oJson.decode('791123')
  13976.         ? oJson.decode('791123.45')
  13977.         ? oJson.decode('791123.45.')
  13978.         ? oJson.decode('"nacho gtz"')
  13979.         if not empty(oJson.cError)
  13980.                 ? oJson.cError
  13981.                 return
  13982.         endif
  13983.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  13984.         if not empty(oJson.cError)
  13985.                 ? oJson.cError
  13986.                 return
  13987.         endif
  13988.        
  13989.         ? 'Test Array'
  13990.         ? '----------'
  13991.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  13992.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  13993.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  13994.         nombres = arr.get(1)
  13995.         edades  = arr.get(2)
  13996.         ? nombres.get(1), edades.get(1)
  13997.         ? nombres.get(2), edades.get(2)
  13998.         ? nombres.get(3), edades.get(3)
  13999.         ?
  14000.         ? 'Test Object'
  14001.         ? '-----------'
  14002.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  14003.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  14004.         ? obj._Nombre, obj._Edad, obj._IsGood
  14005.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  14006.         ? obj.get('jsonrpc'), obj._jsonrpc
  14007.         ? obj.get('id'), obj._id
  14008.         ? obj.get('method'), obj._method
  14009.         ? obj._Params.array[1], obj._Params.get(1)
  14010.  
  14011.         ?
  14012.         ? 'Test nested object'
  14013.         ? '------------------'
  14014.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  14015.         obj = oJson.decode(cJson)
  14016.         if not empty(oJson.cError)
  14017.                 ? oJson.cError
  14018.                 return
  14019.         endif
  14020.         ? cJson
  14021.         ? 'method -->',obj._method
  14022.         ? 'usrkey -->',obj._params._data._usrkey
  14023.         ? 'sendto -->',obj._params._data._sendto
  14024.         ? 'name  --->',obj._params._data._name
  14025.         ? 'expires ->',obj._params._data._expires
  14026.  
  14027.         ?
  14028.         ? 'Test empty object'
  14029.         ? '-----------------'
  14030.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  14031.         obj = oJson.decode(cJson)
  14032.         if not empty(oJson.cError)     
  14033.                 ? oJson.cError
  14034.                 return
  14035.         endif
  14036.         ? cJson
  14037.         ? 'result -->',obj._result, obj.get('result')
  14038.         oError = obj.get('error')
  14039.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  14040.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  14041.         ? 'id  ----->',obj._id, obj.get('id')
  14042.         ?  type("oError._code")
  14043.  
  14044.         ?
  14045.         ? 'Probar decode-enconde-decode-encode'
  14046.         ? '------------------------------------'
  14047.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  14048.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  14049.         ? cJson
  14050.         oSmtp = json_decode(cJson)
  14051.         cJson =  json_encode(oSmtp)
  14052.         ? cJson
  14053.         oSmtp = json_decode(cJson)
  14054.         cJson =  json_encode(oSmtp)
  14055.         ? cJson
  14056.  
  14057.         * Probar falla
  14058.         ?
  14059.         ? 'Probar una falla en el json'
  14060.         ? '---------------------------'
  14061.         cJson = ' {"server":"", "user":"", "password":"" ,'
  14062.         oSmtp = json_decode(cJson)
  14063.         if not empty(json_getErrorMsg())
  14064.                 ? json_getErrorMsg()
  14065.         endif
  14066.  
  14067.         ?
  14068.         ? 'Pruebas Finalizadas'
  14069. retur
  14070. * Tambien puedes usar directamente la clase:
  14071. *
  14072. *       oJson = newobject('json','json.prg')
  14073. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  14074. *       ? oJson.encode(oCliente)
  14075. *       ? oCliente.get('nombre')
  14076. *       ? oCliente.get('apellido')
  14077. *
  14078. *
  14079. * VFPJSON  Encode and Decode JSON for VFP
  14080. * Examples:
  14081. *       oJson = newobject('json','json.prg')
  14082. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  14083. *       ? oJson.encode(oCustomer)
  14084. *       ? oCustomer.get('name')
  14085. *       ? oCustomer.get('lastname')
  14086. *
  14087. *
  14088. lRunTest = .f.
  14089. if lRunTest
  14090.         testJsonClass()
  14091. endif
  14092. return
  14093.  
  14094.  
  14095. function json_encode(xExpr)
  14096.         if vartype(_json)<>'O'
  14097.                 public _json
  14098.                 _json = newobject('json')
  14099.         endif
  14100. return _json.encode(@xExpr)
  14101.  
  14102.  
  14103. function json_decode(cJson)
  14104. local retval
  14105.         if vartype(_json)<>'O'
  14106.                 public _json
  14107.                 _json = newobject('json')
  14108.         endif
  14109.         retval = _json.decode(cJson)
  14110.         if not empty(_json.cError)
  14111.                 return null
  14112.         endif
  14113. return retval
  14114.  
  14115. function json_getErrorMsg()
  14116. return _json.cError
  14117.        
  14118.  
  14119.  
  14120. *
  14121. * json class
  14122. *
  14123. *
  14124. define class json as custom
  14125.  
  14126.  
  14127.         nPos=0
  14128.         nLen=0
  14129.         cJson=''
  14130.         cError=''
  14131.  
  14132.  
  14133.         *
  14134.         * Genera el codigo cJson para parametro que se manda
  14135.         *
  14136.         function encode(xExpr)
  14137.         local cTipo
  14138.                 * Cuando se manda una arreglo,
  14139.                 if type('ALen(xExpr)')=='N'
  14140.                         cTipo = 'A'
  14141.                 Else
  14142.                         cTipo = VarType(xExpr)
  14143.                 Endif
  14144.                
  14145.                 Do Case
  14146.                 Case cTipo=='D'
  14147.                         return '"'+dtos(xExpr)+'"'
  14148.                 Case cTipo=='N'
  14149.                         return Transform(xExpr)
  14150.                 Case cTipo=='L'
  14151.                         return iif(xExpr,'true','false')
  14152.                 Case cTipo=='X'
  14153.                         return 'null'
  14154.                 Case cTipo=='C'
  14155.                         xExpr = allt(xExpr)
  14156.                         xExpr = StrTran(xExpr, '\', '\\' )
  14157.                         xExpr = StrTran(xExpr, '/', '\/' )
  14158.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  14159.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  14160.                         xExpr = StrTran(xExpr, '"', '\"' )
  14161.                         return '"'+xExpr+'"'
  14162.  
  14163.                 case cTipo=='O'
  14164.                         local cProp, cJsonValue, cRetVal, aProp[1]
  14165.                         =AMembers(aProp,xExpr)
  14166.                         cRetVal = ''
  14167.                         for each cProp in aProp
  14168.                                 *? cProp
  14169.                                 *? cRetVal
  14170.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  14171.                                         * algunas propiedades pueden no estar definidas
  14172.                                         * como: activecontrol, parent, etc
  14173.                                         loop
  14174.                                 endif
  14175.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  14176.                                         *
  14177.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  14178.                                         *
  14179.                                         Local i,nTotElem
  14180.                                         cJsonValue = ''
  14181.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  14182.                                         For i=1 to nTotElem
  14183.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  14184.                                         Next
  14185.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  14186.                                 else
  14187.                                         *
  14188.                                         * es otro tipo de dato normal C, N, L
  14189.                                         *
  14190.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  14191.                                 endif
  14192.                                 if left(cProp,1)=='_'
  14193.                                         cProp = substr(cProp,2)
  14194.                                 endif
  14195.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  14196.                         next
  14197.                         return '{' + substr(cRetVal,2) + '}'
  14198.  
  14199.                 case cTipo=='A'
  14200.                         local valor, cRetVal
  14201.                         cRetVal = ''   
  14202.                         for each valor in xExpr
  14203.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  14204.                         next
  14205.                         return  '[' + substr(cRetVal,2) + ']'
  14206.                        
  14207.                 endcase
  14208.  
  14209.         return ''
  14210.  
  14211.  
  14212.  
  14213.  
  14214.  
  14215.         *
  14216.         * regresa un elemento representado por la cadena json que se manda
  14217.         *
  14218.        
  14219.         function decode(cJson)
  14220.         local retValue
  14221.                 cJson = StrTran(cJson,chr(9),'')
  14222.                 cJson = StrTran(cJson,chr(10),'')
  14223.                 cJson = StrTran(cJson,chr(13),'')
  14224.                 cJson = this.fixUnicode(cJson)
  14225.                 this.nPos  = 1
  14226.                 this.cJson = cJson
  14227.                 this.nLen  = len(cJson)
  14228.                 this.cError = ''
  14229.                 retValue = this.parsevalue()
  14230.                 if not empty(this.cError)
  14231.                         return null
  14232.                 endif
  14233.                 if this.getToken()<>null
  14234.                         this.setError('Junk at the end of JSON input')
  14235.                         return null
  14236.                 endif
  14237.         return retValue
  14238.                
  14239.        
  14240.         function parseValue()
  14241.         local token
  14242.                 token = this.getToken()
  14243.                 if token==null
  14244.                         this.setError('Nothing to parse')
  14245.                         return null
  14246.                 endif
  14247.                 do case
  14248.                 case token=='"'
  14249.                         return this.parseString()
  14250.                 case isdigit(token) or token=='-'
  14251.                         return this.parseNumber()
  14252.                 case token=='n'
  14253.                         return this.expectedKeyword('null',null)
  14254.                 case token=='f'
  14255.                         return this.expectedKeyword('false',.f.)
  14256.                 case token=='t'
  14257.                         return this.expectedKeyword('true',.t.)
  14258.                 case token=='{'
  14259.                         return this.parseObject()
  14260.                 case token=='['
  14261.                         return this.parseArray()
  14262.                 otherwise
  14263.                         this.setError('Unexpected token')
  14264.                 endcase
  14265.         return
  14266.                
  14267.        
  14268.         function expectedKeyword(cWord,eValue)
  14269.                 for i=1 to len(cWord)
  14270.                         cChar = this.getChar()
  14271.                         if cChar <> substr(cWord,i,1)
  14272.                                 this.setError("Expected keyword '" + cWord + "'")
  14273.                                 return ''
  14274.                         endif
  14275.                         this.nPos = this.nPos + 1
  14276.                 next
  14277.         return eValue
  14278.        
  14279.  
  14280.         function parseObject()
  14281.         local retval, cPropName, xValue
  14282.                 retval = createObject('myObj')
  14283.                 this.nPos = this.nPos + 1 && Eat {
  14284.                 if this.getToken()<>'}'
  14285.                         do while .t.
  14286.                                 cPropName = this.parseString()
  14287.                                 if not empty(this.cError)
  14288.                                         return null
  14289.                                 endif
  14290.                                 if this.getToken()<>':'
  14291.                                         this.setError("Expected ':' when parsing object")
  14292.                                         return null
  14293.                                 endif
  14294.                                 this.nPos = this.nPos + 1
  14295.                                 xValue = this.parseValue()
  14296.                                 if not empty(this.cError)
  14297.                                         return null
  14298.                                 endif                          
  14299.                                 ** Debug ? cPropName, type('xValue')
  14300.                                 retval.set(cPropName, xValue)
  14301.                                 if this.getToken()<>','
  14302.                                         exit
  14303.                                 endif
  14304.                                 this.nPos = this.nPos + 1
  14305.                         enddo
  14306.                 endif
  14307.                 if this.getToken()<>'}'
  14308.                         this.setError("Expected '}' at the end of object")
  14309.                         return null
  14310.                 endif
  14311.                 this.nPos = this.nPos + 1
  14312.         return retval
  14313.  
  14314.  
  14315.         function parseArray()
  14316.         local retVal, xValue
  14317.                 retval = createObject('MyArray')
  14318.                 this.nPos = this.nPos + 1       && Eat [
  14319.                 if this.getToken() <> ']'
  14320.                         do while .t.
  14321.                                 xValue = this.parseValue()
  14322.                                 if not empty(this.cError)
  14323.                                         return null
  14324.                                 endif
  14325.                                 retval.add( xValue )
  14326.                                 if this.getToken()<>','
  14327.                                         exit
  14328.                                 endif
  14329.                                 this.nPos = this.nPos + 1
  14330.                         enddo
  14331.                         if this.getToken() <> ']'
  14332.                                 this.setError('Expected ] at the end of array')
  14333.                                 return null
  14334.                         endif
  14335.                 endif
  14336.                 this.nPos = this.nPos + 1
  14337.         return retval
  14338.        
  14339.  
  14340.         function parseString()
  14341.         local cRetVal, c
  14342.                 if this.getToken()<>'"'
  14343.                         this.setError('Expected "')
  14344.                         return ''
  14345.                 endif
  14346.                 this.nPos = this.nPos + 1       && Eat "
  14347.                 cRetVal = ''
  14348.                 do while .t.
  14349.                         c = this.getChar()
  14350.                         if c==''
  14351.                                 return ''
  14352.                         endif
  14353.                         if c == '"'
  14354.                                 this.nPos = this.nPos + 1
  14355.                                 exit
  14356.                         endif
  14357.                         if c == '\'
  14358.                                 this.nPos = this.nPos + 1
  14359.                                 if (this.nPos>this.nLen)
  14360.                                         this.setError('\\ at the end of input')
  14361.                                         return ''
  14362.                                 endif
  14363.                                 c = this.getChar()
  14364.                                 if c==''
  14365.                                         return ''
  14366.                                 endif
  14367.                                 do case
  14368.                                 case c=='"'
  14369.                                         c='"'
  14370.                                 case c=='\'
  14371.                                         c='\'
  14372.                                 case c=='/'
  14373.                                         c='/'
  14374.                                 case c=='b'
  14375.                                         c=chr(8)
  14376.                                 case c=='t'
  14377.                                         c=chr(9)
  14378.                                 case c=='n'
  14379.                                         c=chr(10)
  14380.                                 case c=='f'
  14381.                                         c=chr(12)
  14382.                                 case c=='r'
  14383.                                         c=chr(13)
  14384.                                 otherwise
  14385.                                         ******* FALTAN LOS UNICODE
  14386.                                         this.setError('Invalid escape sequence in string literal')
  14387.                                         return ''
  14388.                                 endcase
  14389.                         endif
  14390.                         cRetVal = cRetVal + c
  14391.                         this.nPos = this.nPos + 1
  14392.                 enddo
  14393.         return cRetVal
  14394.                                        
  14395.  
  14396.         **** Pendiente numeros con E
  14397.         function parseNumber()
  14398.         local nStartPos,c, isInt, cNumero
  14399.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  14400.                         this.setError('Expected number literal')
  14401.                         return 0
  14402.                 endif
  14403.                 nStartPos = this.nPos
  14404.                 c = this.getChar()
  14405.                 if c == '-'
  14406.                         c = this.nextChar()
  14407.                 endif
  14408.                 if c == '0'
  14409.                         c = this.nextChar()
  14410.                 else
  14411.                         if isdigit(c)
  14412.                                 c = this.nextChar()
  14413.                                 do while isdigit(c)
  14414.                                         c = this.nextChar()
  14415.                                 enddo
  14416.                         else
  14417.                                 this.setError('Expected digit when parsing number')
  14418.                                 return 0
  14419.                         endif
  14420.                 endif
  14421.                
  14422.                 isInt = .t.
  14423.                 if c=='.'
  14424.                         c = this.nextChar()
  14425.                         if isdigit(c)
  14426.                                 c = this.nextChar()
  14427.                                 isInt = .f.
  14428.                                 do while isDigit(c)
  14429.                                         c = this.nextChar()
  14430.                                 enddo
  14431.                         else
  14432.                                 this.setError('Expected digit following dot comma')
  14433.                                 return 0
  14434.                         endif
  14435.                 endif
  14436.                
  14437.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  14438.         return val(cNumero)
  14439.  
  14440.  
  14441.  
  14442.         function getToken()
  14443.         local char1
  14444.                 do while .t.
  14445.                         if this.nPos > this.nLen
  14446.                                 return null
  14447.                         endif
  14448.                         char1 = substr(this.cJson, this.nPos, 1)
  14449.                         if char1==' '
  14450.                                 this.nPos = this.nPos + 1
  14451.                                 loop
  14452.                         endif
  14453.                         return char1
  14454.                 enddo
  14455.         return
  14456.        
  14457.                
  14458.                
  14459.         function getChar()
  14460.                 if this.nPos > this.nLen
  14461.                         this.setError('Unexpected end of JSON stream')
  14462.                         return ''
  14463.                 endif
  14464.         return substr(this.cJson, this.nPos, 1)
  14465.        
  14466.         function nextChar()
  14467.                 this.nPos = this.nPos + 1
  14468.                 if this.nPos > this.nLen
  14469.                         return ''
  14470.                 endif
  14471.         return substr(this.cJson, this.nPos, 1)
  14472.        
  14473.         function setError(cMsg)
  14474.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  14475.         return
  14476.  
  14477.  
  14478.         function fixUnicode(cStr)
  14479.                 cStr = StrTran(cStr,'\u00e1','á')
  14480.                 cStr = StrTran(cStr,'\u00e9','é')
  14481.                 cStr = StrTran(cStr,'\u00ed','í')
  14482.                 cStr = StrTran(cStr,'\u00f3','ó')
  14483.                 cStr = StrTran(cStr,'\u00fa','ú')
  14484.                 cStr = StrTran(cStr,'\u00c1','Á')
  14485.                 cStr = StrTran(cStr,'\u00c9','É')
  14486.                 cStr = StrTran(cStr,'\u00cd','Í')
  14487.                 cStr = StrTran(cStr,'\u00d3','Ó')
  14488.                 cStr = StrTran(cStr,'\u00da','Ú')
  14489.                 cStr = StrTran(cStr,'\u00f1','ń')
  14490.                 cStr = StrTran(cStr,'\u00d1','Ń')
  14491.         return cStr
  14492.  
  14493.  
  14494.  
  14495. enddefine
  14496.  
  14497.  
  14498.  
  14499.  
  14500.  
  14501. *
  14502. * class used to return an array
  14503. *
  14504. define class myArray as custom
  14505.         nSize = 0
  14506.         dimension array[1]
  14507.  
  14508.         function add(xExpr)
  14509.                 this.nSize = this.nSize + 1
  14510.                 dimension this.array[this.nSize]
  14511.                 this.array[this.nSize] = xExpr
  14512.         return
  14513.  
  14514.         function get(n)
  14515.         return this.array[n]
  14516.  
  14517. enddefine
  14518.  
  14519.  
  14520.  
  14521. *
  14522. * class used to simulate an object
  14523. * all properties are prefixed with 'prop' to permit property names like: error, init
  14524. * that already exists like vfp methods
  14525. *
  14526. define class myObj as custom
  14527. Hidden ;
  14528.         ClassLibrary,Comment, ;
  14529.         BaseClass,ControlCount, ;
  14530.         Controls,Objects,Object,;
  14531.         Height,HelpContextID,Left,Name, ;
  14532.         Parent,ParentClass,Picture, ;
  14533.         Tag,Top,WhatsThisHelpID,Width
  14534.                
  14535.         function set(cPropName, xValue)
  14536.                 cPropName = '_'+cPropName
  14537.                 if type('this.'+cPropName)=='U'
  14538.                         this.addProperty(cPropName,xValue)
  14539.                 else
  14540.                         local cmd
  14541.                         cmd = 'this.'+cPropName+'=xValue'
  14542.                         &cmd
  14543.                 endif
  14544.         return
  14545.        
  14546.         procedure get (cPropName)
  14547.                 cPropName = '_'+cPropName
  14548.                 If type('this.'+cPropName)=='U'
  14549.                         return ''
  14550.                 Else
  14551.                         local cmd
  14552.                         cmd = 'return this.'+cPropName
  14553.                         &cmd
  14554.                 endif
  14555.         return ''
  14556. enddefine
  14557.  
  14558.  
  14559.  
  14560.  
  14561.  
  14562. function testJsonClass
  14563.         clear
  14564.         set decimal to 10
  14565.         oJson = newObject('json')
  14566.        
  14567.        
  14568.         ? 'Test Basic Types'
  14569.         ? '----------------'
  14570.         ? oJson.decode('null')
  14571.         ? oJson.decode('true')
  14572.         ? oJson.decode('false')
  14573.         ?
  14574.         ? oJson.decode('791123')
  14575.         ? oJson.decode('791123.45')
  14576.         ? oJson.decode('791123.45.')
  14577.         ? oJson.decode('"nacho gtz"')
  14578.         if not empty(oJson.cError)
  14579.                 ? oJson.cError
  14580.                 return
  14581.         endif
  14582.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  14583.         if not empty(oJson.cError)
  14584.                 ? oJson.cError
  14585.                 return
  14586.         endif
  14587.        
  14588.         ? 'Test Array'
  14589.         ? '----------'
  14590.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  14591.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  14592.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  14593.         nombres = arr.get(1)
  14594.         edades  = arr.get(2)
  14595.         ? nombres.get(1), edades.get(1)
  14596.         ? nombres.get(2), edades.get(2)
  14597.         ? nombres.get(3), edades.get(3)
  14598.         ?
  14599.         ? 'Test Object'
  14600.         ? '-----------'
  14601.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  14602.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  14603.         ? obj._Nombre, obj._Edad, obj._IsGood
  14604.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  14605.         ? obj.get('jsonrpc'), obj._jsonrpc
  14606.         ? obj.get('id'), obj._id
  14607.         ? obj.get('method'), obj._method
  14608.         ? obj._Params.array[1], obj._Params.get(1)
  14609.  
  14610.         ?
  14611.         ? 'Test nested object'
  14612.         ? '------------------'
  14613.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  14614.         obj = oJson.decode(cJson)
  14615.         if not empty(oJson.cError)
  14616.                 ? oJson.cError
  14617.                 return
  14618.         endif
  14619.         ? cJson
  14620.         ? 'method -->',obj._method
  14621.         ? 'usrkey -->',obj._params._data._usrkey
  14622.         ? 'sendto -->',obj._params._data._sendto
  14623.         ? 'name  --->',obj._params._data._name
  14624.         ? 'expires ->',obj._params._data._expires
  14625.  
  14626.         ?
  14627.         ? 'Test empty object'
  14628.         ? '-----------------'
  14629.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  14630.         obj = oJson.decode(cJson)
  14631.         if not empty(oJson.cError)     
  14632.                 ? oJson.cError
  14633.                 return
  14634.         endif
  14635.         ? cJson
  14636.         ? 'result -->',obj._result, obj.get('result')
  14637.         oError = obj.get('error')
  14638.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  14639.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  14640.         ? 'id  ----->',obj._id, obj.get('id')
  14641.         ?  type("oError._code")
  14642.  
  14643.         ?
  14644.         ? 'Probar decode-enconde-decode-encode'
  14645.         ? '------------------------------------'
  14646.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  14647.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  14648.         ? cJson
  14649.         oSmtp = json_decode(cJson)
  14650.         cJson =  json_encode(oSmtp)
  14651.         ? cJson
  14652.         oSmtp = json_decode(cJson)
  14653.         cJson =  json_encode(oSmtp)
  14654.         ? cJson
  14655.  
  14656.         * Probar falla
  14657.         ?
  14658.         ? 'Probar una falla en el json'
  14659.         ? '---------------------------'
  14660.         cJson = ' {"server":"", "user":"", "password":"" ,'
  14661.         oSmtp = json_decode(cJson)
  14662.         if not empty(json_getErrorMsg())
  14663.                 ? json_getErrorMsg()
  14664.         endif
  14665.  
  14666.         ?
  14667.         ? 'Pruebas Finalizadas'
  14668. retur
  14669. *
  14670. *       oJson = newobject('json','json.prg')
  14671. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  14672. *       ? oJson.encode(oCliente)
  14673. *       ? oCliente.get('nombre')
  14674. *       ? oCliente.get('apellido')
  14675. *
  14676. *
  14677. * VFPJSON  Encode and Decode JSON for VFP
  14678. * Examples:
  14679. *       oJson = newobject('json','json.prg')
  14680. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  14681. *       ? oJson.encode(oCustomer)
  14682. *       ? oCustomer.get('name')
  14683. *       ? oCustomer.get('lastname')
  14684. *
  14685. *
  14686. lRunTest = .f.
  14687. if lRunTest
  14688.         testJsonClass()
  14689. endif
  14690. return
  14691.  
  14692.  
  14693. function json_encode(xExpr)
  14694.         if vartype(_json)<>'O'
  14695.                 public _json
  14696.                 _json = newobject('json')
  14697.         endif
  14698. return _json.encode(@xExpr)
  14699.  
  14700.  
  14701. function json_decode(cJson)
  14702. local retval
  14703.         if vartype(_json)<>'O'
  14704.                 public _json
  14705.                 _json = newobject('json')
  14706.         endif
  14707.         retval = _json.decode(cJson)
  14708.         if not empty(_json.cError)
  14709.                 return null
  14710.         endif
  14711. return retval
  14712.  
  14713. function json_getErrorMsg()
  14714. return _json.cError
  14715.        
  14716.  
  14717.  
  14718. *
  14719. * json class
  14720. *
  14721. *
  14722. define class json as custom
  14723.  
  14724.  
  14725.         nPos=0
  14726.         nLen=0
  14727.         cJson=''
  14728.         cError=''
  14729.  
  14730.  
  14731.         *
  14732.         * Genera el codigo cJson para parametro que se manda
  14733.         *
  14734.         function encode(xExpr)
  14735.         local cTipo
  14736.                 * Cuando se manda una arreglo,
  14737.                 if type('ALen(xExpr)')=='N'
  14738.                         cTipo = 'A'
  14739.                 Else
  14740.                         cTipo = VarType(xExpr)
  14741.                 Endif
  14742.                
  14743.                 Do Case
  14744.                 Case cTipo=='D'
  14745.                         return '"'+dtos(xExpr)+'"'
  14746.                 Case cTipo=='N'
  14747.                         return Transform(xExpr)
  14748.                 Case cTipo=='L'
  14749.                         return iif(xExpr,'true','false')
  14750.                 Case cTipo=='X'
  14751.                         return 'null'
  14752.                 Case cTipo=='C'
  14753.                         xExpr = allt(xExpr)
  14754.                         xExpr = StrTran(xExpr, '\', '\\' )
  14755.                         xExpr = StrTran(xExpr, '/', '\/' )
  14756.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  14757.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  14758.                         xExpr = StrTran(xExpr, '"', '\"' )
  14759.                         return '"'+xExpr+'"'
  14760.  
  14761.                 case cTipo=='O'
  14762.                         local cProp, cJsonValue, cRetVal, aProp[1]
  14763.                         =AMembers(aProp,xExpr)
  14764.                         cRetVal = ''
  14765.                         for each cProp in aProp
  14766.                                 *? cProp
  14767.                                 *? cRetVal
  14768.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  14769.                                         * algunas propiedades pueden no estar definidas
  14770.                                         * como: activecontrol, parent, etc
  14771.                                         loop
  14772.                                 endif
  14773.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  14774.                                         *
  14775.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  14776.                                         *
  14777.                                         Local i,nTotElem
  14778.                                         cJsonValue = ''
  14779.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  14780.                                         For i=1 to nTotElem
  14781.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  14782.                                         Next
  14783.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  14784.                                 else
  14785.                                         *
  14786.                                         * es otro tipo de dato normal C, N, L
  14787.                                         *
  14788.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  14789.                                 endif
  14790.                                 if left(cProp,1)=='_'
  14791.                                         cProp = substr(cProp,2)
  14792.                                 endif
  14793.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  14794.                         next
  14795.                         return '{' + substr(cRetVal,2) + '}'
  14796.  
  14797.                 case cTipo=='A'
  14798.                         local valor, cRetVal
  14799.                         cRetVal = ''   
  14800.                         for each valor in xExpr
  14801.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  14802.                         next
  14803.                         return  '[' + substr(cRetVal,2) + ']'
  14804.                        
  14805.                 endcase
  14806.  
  14807.         return ''
  14808.  
  14809.  
  14810.  
  14811.  
  14812.  
  14813.         *
  14814.         * regresa un elemento representado por la cadena json que se manda
  14815.         *
  14816.        
  14817.         function decode(cJson)
  14818.         local retValue
  14819.                 cJson = StrTran(cJson,chr(9),'')
  14820.                 cJson = StrTran(cJson,chr(10),'')
  14821.                 cJson = StrTran(cJson,chr(13),'')
  14822.                 cJson = this.fixUnicode(cJson)
  14823.                 this.nPos  = 1
  14824.                 this.cJson = cJson
  14825.                 this.nLen  = len(cJson)
  14826.                 this.cError = ''
  14827.                 retValue = this.parsevalue()
  14828.                 if not empty(this.cError)
  14829.                         return null
  14830.                 endif
  14831.                 if this.getToken()<>null
  14832.                         this.setError('Junk at the end of JSON input')
  14833.                         return null
  14834.                 endif
  14835.         return retValue
  14836.                
  14837.        
  14838.         function parseValue()
  14839.         local token
  14840.                 token = this.getToken()
  14841.                 if token==null
  14842.                         this.setError('Nothing to parse')
  14843.                         return null
  14844.                 endif
  14845.                 do case
  14846.                 case token=='"'
  14847.                         return this.parseString()
  14848.                 case isdigit(token) or token=='-'
  14849.                         return this.parseNumber()
  14850.                 case token=='n'
  14851.                         return this.expectedKeyword('null',null)
  14852.                 case token=='f'
  14853.                         return this.expectedKeyword('false',.f.)
  14854.                 case token=='t'
  14855.                         return this.expectedKeyword('true',.t.)
  14856.                 case token=='{'
  14857.                         return this.parseObject()
  14858.                 case token=='['
  14859.                         return this.parseArray()
  14860.                 otherwise
  14861.                         this.setError('Unexpected token')
  14862.                 endcase
  14863.         return
  14864.                
  14865.        
  14866.         function expectedKeyword(cWord,eValue)
  14867.                 for i=1 to len(cWord)
  14868.                         cChar = this.getChar()
  14869.                         if cChar <> substr(cWord,i,1)
  14870.                                 this.setError("Expected keyword '" + cWord + "'")
  14871.                                 return ''
  14872.                         endif
  14873.                         this.nPos = this.nPos + 1
  14874.                 next
  14875.         return eValue
  14876.        
  14877.  
  14878.         function parseObject()
  14879.         local retval, cPropName, xValue
  14880.                 retval = createObject('myObj')
  14881.                 this.nPos = this.nPos + 1 && Eat {
  14882.                 if this.getToken()<>'}'
  14883.                         do while .t.
  14884.                                 cPropName = this.parseString()
  14885.                                 if not empty(this.cError)
  14886.                                         return null
  14887.                                 endif
  14888.                                 if this.getToken()<>':'
  14889.                                         this.setError("Expected ':' when parsing object")
  14890.                                         return null
  14891.                                 endif
  14892.                                 this.nPos = this.nPos + 1
  14893.                                 xValue = this.parseValue()
  14894.                                 if not empty(this.cError)
  14895.                                         return null
  14896.                                 endif                          
  14897.                                 ** Debug ? cPropName, type('xValue')
  14898.                                 retval.set(cPropName, xValue)
  14899.                                 if this.getToken()<>','
  14900.                                         exit
  14901.                                 endif
  14902.                                 this.nPos = this.nPos + 1
  14903.                         enddo
  14904.                 endif
  14905.                 if this.getToken()<>'}'
  14906.                         this.setError("Expected '}' at the end of object")
  14907.                         return null
  14908.                 endif
  14909.                 this.nPos = this.nPos + 1
  14910.         return retval
  14911.  
  14912.  
  14913.         function parseArray()
  14914.         local retVal, xValue
  14915.                 retval = createObject('MyArray')
  14916.                 this.nPos = this.nPos + 1       && Eat [
  14917.                 if this.getToken() <> ']'
  14918.                         do while .t.
  14919.                                 xValue = this.parseValue()
  14920.                                 if not empty(this.cError)
  14921.                                         return null
  14922.                                 endif
  14923.                                 retval.add( xValue )
  14924.                                 if this.getToken()<>','
  14925.                                         exit
  14926.                                 endif
  14927.                                 this.nPos = this.nPos + 1
  14928.                         enddo
  14929.                         if this.getToken() <> ']'
  14930.                                 this.setError('Expected ] at the end of array')
  14931.                                 return null
  14932.                         endif
  14933.                 endif
  14934.                 this.nPos = this.nPos + 1
  14935.         return retval
  14936.        
  14937.  
  14938.         function parseString()
  14939.         local cRetVal, c
  14940.                 if this.getToken()<>'"'
  14941.                         this.setError('Expected "')
  14942.                         return ''
  14943.                 endif
  14944.                 this.nPos = this.nPos + 1       && Eat "
  14945.                 cRetVal = ''
  14946.                 do while .t.
  14947.                         c = this.getChar()
  14948.                         if c==''
  14949.                                 return ''
  14950.                         endif
  14951.                         if c == '"'
  14952.                                 this.nPos = this.nPos + 1
  14953.                                 exit
  14954.                         endif
  14955.                         if c == '\'
  14956.                                 this.nPos = this.nPos + 1
  14957.                                 if (this.nPos>this.nLen)
  14958.                                         this.setError('\\ at the end of input')
  14959.                                         return ''
  14960.                                 endif
  14961.                                 c = this.getChar()
  14962.                                 if c==''
  14963.                                         return ''
  14964.                                 endif
  14965.                                 do case
  14966.                                 case c=='"'
  14967.                                         c='"'
  14968.                                 case c=='\'
  14969.                                         c='\'
  14970.                                 case c=='/'
  14971.                                         c='/'
  14972.                                 case c=='b'
  14973.                                         c=chr(8)
  14974.                                 case c=='t'
  14975.                                         c=chr(9)
  14976.                                 case c=='n'
  14977.                                         c=chr(10)
  14978.                                 case c=='f'
  14979.                                         c=chr(12)
  14980.                                 case c=='r'
  14981.                                         c=chr(13)
  14982.                                 otherwise
  14983.                                         ******* FALTAN LOS UNICODE
  14984.                                         this.setError('Invalid escape sequence in string literal')
  14985.                                         return ''
  14986.                                 endcase
  14987.                         endif
  14988.                         cRetVal = cRetVal + c
  14989.                         this.nPos = this.nPos + 1
  14990.                 enddo
  14991.         return cRetVal
  14992.                                        
  14993.  
  14994.         **** Pendiente numeros con E
  14995.         function parseNumber()
  14996.         local nStartPos,c, isInt, cNumero
  14997.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  14998.                         this.setError('Expected number literal')
  14999.                         return 0
  15000.                 endif
  15001.                 nStartPos = this.nPos
  15002.                 c = this.getChar()
  15003.                 if c == '-'
  15004.                         c = this.nextChar()
  15005.                 endif
  15006.                 if c == '0'
  15007.                         c = this.nextChar()
  15008.                 else
  15009.                         if isdigit(c)
  15010.                                 c = this.nextChar()
  15011.                                 do while isdigit(c)
  15012.                                         c = this.nextChar()
  15013.                                 enddo
  15014.                         else
  15015.                                 this.setError('Expected digit when parsing number')
  15016.                                 return 0
  15017.                         endif
  15018.                 endif
  15019.                
  15020.                 isInt = .t.
  15021.                 if c=='.'
  15022.                         c = this.nextChar()
  15023.                         if isdigit(c)
  15024.                                 c = this.nextChar()
  15025.                                 isInt = .f.
  15026.                                 do while isDigit(c)
  15027.                                         c = this.nextChar()
  15028.                                 enddo
  15029.                         else
  15030.                                 this.setError('Expected digit following dot comma')
  15031.                                 return 0
  15032.                         endif
  15033.                 endif
  15034.                
  15035.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  15036.         return val(cNumero)
  15037.  
  15038.  
  15039.  
  15040.         function getToken()
  15041.         local char1
  15042.                 do while .t.
  15043.                         if this.nPos > this.nLen
  15044.                                 return null
  15045.                         endif
  15046.                         char1 = substr(this.cJson, this.nPos, 1)
  15047.                         if char1==' '
  15048.                                 this.nPos = this.nPos + 1
  15049.                                 loop
  15050.                         endif
  15051.                         return char1
  15052.                 enddo
  15053.         return
  15054.        
  15055.                
  15056.                
  15057.         function getChar()
  15058.                 if this.nPos > this.nLen
  15059.                         this.setError('Unexpected end of JSON stream')
  15060.                         return ''
  15061.                 endif
  15062.         return substr(this.cJson, this.nPos, 1)
  15063.        
  15064.         function nextChar()
  15065.                 this.nPos = this.nPos + 1
  15066.                 if this.nPos > this.nLen
  15067.                         return ''
  15068.                 endif
  15069.         return substr(this.cJson, this.nPos, 1)
  15070.        
  15071.         function setError(cMsg)
  15072.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  15073.         return
  15074.  
  15075.  
  15076.         function fixUnicode(cStr)
  15077.                 cStr = StrTran(cStr,'\u00e1','á')
  15078.                 cStr = StrTran(cStr,'\u00e9','é')
  15079.                 cStr = StrTran(cStr,'\u00ed','í')
  15080.                 cStr = StrTran(cStr,'\u00f3','ó')
  15081.                 cStr = StrTran(cStr,'\u00fa','ú')
  15082.                 cStr = StrTran(cStr,'\u00c1','Á')
  15083.                 cStr = StrTran(cStr,'\u00c9','É')
  15084.                 cStr = StrTran(cStr,'\u00cd','Í')
  15085.                 cStr = StrTran(cStr,'\u00d3','Ó')
  15086.                 cStr = StrTran(cStr,'\u00da','Ú')
  15087.                 cStr = StrTran(cStr,'\u00f1','ń')
  15088.                 cStr = StrTran(cStr,'\u00d1','Ń')
  15089.         return cStr
  15090.  
  15091.  
  15092.  
  15093. enddefine
  15094.  
  15095.  
  15096.  
  15097.  
  15098.  
  15099. *
  15100. * class used to return an array
  15101. *
  15102. define class myArray as custom
  15103.         nSize = 0
  15104.         dimension array[1]
  15105.  
  15106.         function add(xExpr)
  15107.                 this.nSize = this.nSize + 1
  15108.                 dimension this.array[this.nSize]
  15109.                 this.array[this.nSize] = xExpr
  15110.         return
  15111.  
  15112.         function get(n)
  15113.         return this.array[n]
  15114.  
  15115. enddefine
  15116.  
  15117.  
  15118.  
  15119. *
  15120. * class used to simulate an object
  15121. * all properties are prefixed with 'prop' to permit property names like: error, init
  15122. * that already exists like vfp methods
  15123. *
  15124. define class myObj as custom
  15125. Hidden ;
  15126.         ClassLibrary,Comment, ;
  15127.         BaseClass,ControlCount, ;
  15128.         Controls,Objects,Object,;
  15129.         Height,HelpContextID,Left,Name, ;
  15130.         Parent,ParentClass,Picture, ;
  15131.         Tag,Top,WhatsThisHelpID,Width
  15132.                
  15133.         function set(cPropName, xValue)
  15134.                 cPropName = '_'+cPropName
  15135.                 if type('this.'+cPropName)=='U'
  15136.                         this.addProperty(cPropName,xValue)
  15137.                 else
  15138.                         local cmd
  15139.                         cmd = 'this.'+cPropName+'=xValue'
  15140.                         &cmd
  15141.                 endif
  15142.         return
  15143.        
  15144.         procedure get (cPropName)
  15145.                 cPropName = '_'+cPropName
  15146.                 If type('this.'+cPropName)=='U'
  15147.                         return ''
  15148.                 Else
  15149.                         local cmd
  15150.                         cmd = 'return this.'+cPropName
  15151.                         &cmd
  15152.                 endif
  15153.         return ''
  15154. enddefine
  15155.  
  15156.  
  15157.  
  15158.  
  15159.  
  15160. function testJsonClass
  15161.         clear
  15162.         set decimal to 10
  15163.         oJson = newObject('json')
  15164.        
  15165.        
  15166.         ? 'Test Basic Types'
  15167.         ? '----------------'
  15168.         ? oJson.decode('null')
  15169.         ? oJson.decode('true')
  15170.         ? oJson.decode('false')
  15171.         ?
  15172.         ? oJson.decode('791123')
  15173.         ? oJson.decode('791123.45')
  15174.         ? oJson.decode('791123.45.')
  15175.         ? oJson.decode('"nacho gtz"')
  15176.         if not empty(oJson.cError)
  15177.                 ? oJson.cError
  15178.                 return
  15179.         endif
  15180.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  15181.         if not empty(oJson.cError)
  15182.                 ? oJson.cError
  15183.                 return
  15184.         endif
  15185.        
  15186.         ? 'Test Array'
  15187.         ? '----------'
  15188.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  15189.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  15190.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  15191.         nombres = arr.get(1)
  15192.         edades  = arr.get(2)
  15193.         ? nombres.get(1), edades.get(1)
  15194.         ? nombres.get(2), edades.get(2)
  15195.         ? nombres.get(3), edades.get(3)
  15196.         ?
  15197.         ? 'Test Object'
  15198.         ? '-----------'
  15199.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  15200.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  15201.         ? obj._Nombre, obj._Edad, obj._IsGood
  15202.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  15203.         ? obj.get('jsonrpc'), obj._jsonrpc
  15204.         ? obj.get('id'), obj._id
  15205.         ? obj.get('method'), obj._method
  15206.         ? obj._Params.array[1], obj._Params.get(1)
  15207.  
  15208.         ?
  15209.         ? 'Test nested object'
  15210.         ? '------------------'
  15211.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  15212.         obj = oJson.decode(cJson)
  15213.         if not empty(oJson.cError)
  15214.                 ? oJson.cError
  15215.                 return
  15216.         endif
  15217.         ? cJson
  15218.         ? 'method -->',obj._method
  15219.         ? 'usrkey -->',obj._params._data._usrkey
  15220.         ? 'sendto -->',obj._params._data._sendto
  15221.         ? 'name  --->',obj._params._data._name
  15222.         ? 'expires ->',obj._params._data._expires
  15223.  
  15224.         ?
  15225.         ? 'Test empty object'
  15226.         ? '-----------------'
  15227.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  15228.         obj = oJson.decode(cJson)
  15229.         if not empty(oJson.cError)     
  15230.                 ? oJson.cError
  15231.                 return
  15232.         endif
  15233.         ? cJson
  15234.         ? 'result -->',obj._result, obj.get('result')
  15235.         oError = obj.get('error')
  15236.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  15237.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  15238.         ? 'id  ----->',obj._id, obj.get('id')
  15239.         ?  type("oError._code")
  15240.  
  15241.         ?
  15242.         ? 'Probar decode-enconde-decode-encode'
  15243.         ? '------------------------------------'
  15244.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  15245.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  15246.         ? cJson
  15247.         oSmtp = json_decode(cJson)
  15248.         cJson =  json_encode(oSmtp)
  15249.         ? cJson
  15250.         oSmtp = json_decode(cJson)
  15251.         cJson =  json_encode(oSmtp)
  15252.         ? cJson
  15253.  
  15254.         * Probar falla
  15255.         ?
  15256.         ? 'Probar una falla en el json'
  15257.         ? '---------------------------'
  15258.         cJson = ' {"server":"", "user":"", "password":"" ,'
  15259.         oSmtp = json_decode(cJson)
  15260.         if not empty(json_getErrorMsg())
  15261.                 ? json_getErrorMsg()
  15262.         endif
  15263.  
  15264.         ?
  15265.         ? 'Pruebas Finalizadas'
  15266. retur
  15267. *       oJson = newobject('json','json.prg')
  15268. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  15269. *       ? oJson.encode(oCliente)
  15270. *       ? oCliente.get('nombre')
  15271. *       ? oCliente.get('apellido')
  15272. *
  15273. *
  15274. * VFPJSON  Encode and Decode JSON for VFP
  15275. * Examples:
  15276. *       oJson = newobject('json','json.prg')
  15277. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  15278. *       ? oJson.encode(oCustomer)
  15279. *       ? oCustomer.get('name')
  15280. *       ? oCustomer.get('lastname')
  15281. *
  15282. *
  15283. lRunTest = .f.
  15284. if lRunTest
  15285.         testJsonClass()
  15286. endif
  15287. return
  15288.  
  15289.  
  15290. function json_encode(xExpr)
  15291.         if vartype(_json)<>'O'
  15292.                 public _json
  15293.                 _json = newobject('json')
  15294.         endif
  15295. return _json.encode(@xExpr)
  15296.  
  15297.  
  15298. function json_decode(cJson)
  15299. local retval
  15300.         if vartype(_json)<>'O'
  15301.                 public _json
  15302.                 _json = newobject('json')
  15303.         endif
  15304.         retval = _json.decode(cJson)
  15305.         if not empty(_json.cError)
  15306.                 return null
  15307.         endif
  15308. return retval
  15309.  
  15310. function json_getErrorMsg()
  15311. return _json.cError
  15312.        
  15313.  
  15314.  
  15315. *
  15316. * json class
  15317. *
  15318. *
  15319. define class json as custom
  15320.  
  15321.  
  15322.         nPos=0
  15323.         nLen=0
  15324.         cJson=''
  15325.         cError=''
  15326.  
  15327.  
  15328.         *
  15329.         * Genera el codigo cJson para parametro que se manda
  15330.         *
  15331.         function encode(xExpr)
  15332.         local cTipo
  15333.                 * Cuando se manda una arreglo,
  15334.                 if type('ALen(xExpr)')=='N'
  15335.                         cTipo = 'A'
  15336.                 Else
  15337.                         cTipo = VarType(xExpr)
  15338.                 Endif
  15339.                
  15340.                 Do Case
  15341.                 Case cTipo=='D'
  15342.                         return '"'+dtos(xExpr)+'"'
  15343.                 Case cTipo=='N'
  15344.                         return Transform(xExpr)
  15345.                 Case cTipo=='L'
  15346.                         return iif(xExpr,'true','false')
  15347.                 Case cTipo=='X'
  15348.                         return 'null'
  15349.                 Case cTipo=='C'
  15350.                         xExpr = allt(xExpr)
  15351.                         xExpr = StrTran(xExpr, '\', '\\' )
  15352.                         xExpr = StrTran(xExpr, '/', '\/' )
  15353.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  15354.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  15355.                         xExpr = StrTran(xExpr, '"', '\"' )
  15356.                         return '"'+xExpr+'"'
  15357.  
  15358.                 case cTipo=='O'
  15359.                         local cProp, cJsonValue, cRetVal, aProp[1]
  15360.                         =AMembers(aProp,xExpr)
  15361.                         cRetVal = ''
  15362.                         for each cProp in aProp
  15363.                                 *? cProp
  15364.                                 *? cRetVal
  15365.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  15366.                                         * algunas propiedades pueden no estar definidas
  15367.                                         * como: activecontrol, parent, etc
  15368.                                         loop
  15369.                                 endif
  15370.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  15371.                                         *
  15372.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  15373.                                         *
  15374.                                         Local i,nTotElem
  15375.                                         cJsonValue = ''
  15376.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  15377.                                         For i=1 to nTotElem
  15378.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  15379.                                         Next
  15380.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  15381.                                 else
  15382.                                         *
  15383.                                         * es otro tipo de dato normal C, N, L
  15384.                                         *
  15385.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  15386.                                 endif
  15387.                                 if left(cProp,1)=='_'
  15388.                                         cProp = substr(cProp,2)
  15389.                                 endif
  15390.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  15391.                         next
  15392.                         return '{' + substr(cRetVal,2) + '}'
  15393.  
  15394.                 case cTipo=='A'
  15395.                         local valor, cRetVal
  15396.                         cRetVal = ''   
  15397.                         for each valor in xExpr
  15398.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  15399.                         next
  15400.                         return  '[' + substr(cRetVal,2) + ']'
  15401.                        
  15402.                 endcase
  15403.  
  15404.         return ''
  15405.  
  15406.  
  15407.  
  15408.  
  15409.  
  15410.         *
  15411.         * regresa un elemento representado por la cadena json que se manda
  15412.         *
  15413.        
  15414.         function decode(cJson)
  15415.         local retValue
  15416.                 cJson = StrTran(cJson,chr(9),'')
  15417.                 cJson = StrTran(cJson,chr(10),'')
  15418.                 cJson = StrTran(cJson,chr(13),'')
  15419.                 cJson = this.fixUnicode(cJson)
  15420.                 this.nPos  = 1
  15421.                 this.cJson = cJson
  15422.                 this.nLen  = len(cJson)
  15423.                 this.cError = ''
  15424.                 retValue = this.parsevalue()
  15425.                 if not empty(this.cError)
  15426.                         return null
  15427.                 endif
  15428.                 if this.getToken()<>null
  15429.                         this.setError('Junk at the end of JSON input')
  15430.                         return null
  15431.                 endif
  15432.         return retValue
  15433.                
  15434.        
  15435.         function parseValue()
  15436.         local token
  15437.                 token = this.getToken()
  15438.                 if token==null
  15439.                         this.setError('Nothing to parse')
  15440.                         return null
  15441.                 endif
  15442.                 do case
  15443.                 case token=='"'
  15444.                         return this.parseString()
  15445.                 case isdigit(token) or token=='-'
  15446.                         return this.parseNumber()
  15447.                 case token=='n'
  15448.                         return this.expectedKeyword('null',null)
  15449.                 case token=='f'
  15450.                         return this.expectedKeyword('false',.f.)
  15451.                 case token=='t'
  15452.                         return this.expectedKeyword('true',.t.)
  15453.                 case token=='{'
  15454.                         return this.parseObject()
  15455.                 case token=='['
  15456.                         return this.parseArray()
  15457.                 otherwise
  15458.                         this.setError('Unexpected token')
  15459.                 endcase
  15460.         return
  15461.                
  15462.        
  15463.         function expectedKeyword(cWord,eValue)
  15464.                 for i=1 to len(cWord)
  15465.                         cChar = this.getChar()
  15466.                         if cChar <> substr(cWord,i,1)
  15467.                                 this.setError("Expected keyword '" + cWord + "'")
  15468.                                 return ''
  15469.                         endif
  15470.                         this.nPos = this.nPos + 1
  15471.                 next
  15472.         return eValue
  15473.        
  15474.  
  15475.         function parseObject()
  15476.         local retval, cPropName, xValue
  15477.                 retval = createObject('myObj')
  15478.                 this.nPos = this.nPos + 1 && Eat {
  15479.                 if this.getToken()<>'}'
  15480.                         do while .t.
  15481.                                 cPropName = this.parseString()
  15482.                                 if not empty(this.cError)
  15483.                                         return null
  15484.                                 endif
  15485.                                 if this.getToken()<>':'
  15486.                                         this.setError("Expected ':' when parsing object")
  15487.                                         return null
  15488.                                 endif
  15489.                                 this.nPos = this.nPos + 1
  15490.                                 xValue = this.parseValue()
  15491.                                 if not empty(this.cError)
  15492.                                         return null
  15493.                                 endif                          
  15494.                                 ** Debug ? cPropName, type('xValue')
  15495.                                 retval.set(cPropName, xValue)
  15496.                                 if this.getToken()<>','
  15497.                                         exit
  15498.                                 endif
  15499.                                 this.nPos = this.nPos + 1
  15500.                         enddo
  15501.                 endif
  15502.                 if this.getToken()<>'}'
  15503.                         this.setError("Expected '}' at the end of object")
  15504.                         return null
  15505.                 endif
  15506.                 this.nPos = this.nPos + 1
  15507.         return retval
  15508.  
  15509.  
  15510.         function parseArray()
  15511.         local retVal, xValue
  15512.                 retval = createObject('MyArray')
  15513.                 this.nPos = this.nPos + 1       && Eat [
  15514.                 if this.getToken() <> ']'
  15515.                         do while .t.
  15516.                                 xValue = this.parseValue()
  15517.                                 if not empty(this.cError)
  15518.                                         return null
  15519.                                 endif
  15520.                                 retval.add( xValue )
  15521.                                 if this.getToken()<>','
  15522.                                         exit
  15523.                                 endif
  15524.                                 this.nPos = this.nPos + 1
  15525.                         enddo
  15526.                         if this.getToken() <> ']'
  15527.                                 this.setError('Expected ] at the end of array')
  15528.                                 return null
  15529.                         endif
  15530.                 endif
  15531.                 this.nPos = this.nPos + 1
  15532.         return retval
  15533.        
  15534.  
  15535.         function parseString()
  15536.         local cRetVal, c
  15537.                 if this.getToken()<>'"'
  15538.                         this.setError('Expected "')
  15539.                         return ''
  15540.                 endif
  15541.                 this.nPos = this.nPos + 1       && Eat "
  15542.                 cRetVal = ''
  15543.                 do while .t.
  15544.                         c = this.getChar()
  15545.                         if c==''
  15546.                                 return ''
  15547.                         endif
  15548.                         if c == '"'
  15549.                                 this.nPos = this.nPos + 1
  15550.                                 exit
  15551.                         endif
  15552.                         if c == '\'
  15553.                                 this.nPos = this.nPos + 1
  15554.                                 if (this.nPos>this.nLen)
  15555.                                         this.setError('\\ at the end of input')
  15556.                                         return ''
  15557.                                 endif
  15558.                                 c = this.getChar()
  15559.                                 if c==''
  15560.                                         return ''
  15561.                                 endif
  15562.                                 do case
  15563.                                 case c=='"'
  15564.                                         c='"'
  15565.                                 case c=='\'
  15566.                                         c='\'
  15567.                                 case c=='/'
  15568.                                         c='/'
  15569.                                 case c=='b'
  15570.                                         c=chr(8)
  15571.                                 case c=='t'
  15572.                                         c=chr(9)
  15573.                                 case c=='n'
  15574.                                         c=chr(10)
  15575.                                 case c=='f'
  15576.                                         c=chr(12)
  15577.                                 case c=='r'
  15578.                                         c=chr(13)
  15579.                                 otherwise
  15580.                                         ******* FALTAN LOS UNICODE
  15581.                                         this.setError('Invalid escape sequence in string literal')
  15582.                                         return ''
  15583.                                 endcase
  15584.                         endif
  15585.                         cRetVal = cRetVal + c
  15586.                         this.nPos = this.nPos + 1
  15587.                 enddo
  15588.         return cRetVal
  15589.                                        
  15590.  
  15591.         **** Pendiente numeros con E
  15592.         function parseNumber()
  15593.         local nStartPos,c, isInt, cNumero
  15594.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  15595.                         this.setError('Expected number literal')
  15596.                         return 0
  15597.                 endif
  15598.                 nStartPos = this.nPos
  15599.                 c = this.getChar()
  15600.                 if c == '-'
  15601.                         c = this.nextChar()
  15602.                 endif
  15603.                 if c == '0'
  15604.                         c = this.nextChar()
  15605.                 else
  15606.                         if isdigit(c)
  15607.                                 c = this.nextChar()
  15608.                                 do while isdigit(c)
  15609.                                         c = this.nextChar()
  15610.                                 enddo
  15611.                         else
  15612.                                 this.setError('Expected digit when parsing number')
  15613.                                 return 0
  15614.                         endif
  15615.                 endif
  15616.                
  15617.                 isInt = .t.
  15618.                 if c=='.'
  15619.                         c = this.nextChar()
  15620.                         if isdigit(c)
  15621.                                 c = this.nextChar()
  15622.                                 isInt = .f.
  15623.                                 do while isDigit(c)
  15624.                                         c = this.nextChar()
  15625.                                 enddo
  15626.                         else
  15627.                                 this.setError('Expected digit following dot comma')
  15628.                                 return 0
  15629.                         endif
  15630.                 endif
  15631.                
  15632.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  15633.         return val(cNumero)
  15634.  
  15635.  
  15636.  
  15637.         function getToken()
  15638.         local char1
  15639.                 do while .t.
  15640.                         if this.nPos > this.nLen
  15641.                                 return null
  15642.                         endif
  15643.                         char1 = substr(this.cJson, this.nPos, 1)
  15644.                         if char1==' '
  15645.                                 this.nPos = this.nPos + 1
  15646.                                 loop
  15647.                         endif
  15648.                         return char1
  15649.                 enddo
  15650.         return
  15651.        
  15652.                
  15653.                
  15654.         function getChar()
  15655.                 if this.nPos > this.nLen
  15656.                         this.setError('Unexpected end of JSON stream')
  15657.                         return ''
  15658.                 endif
  15659.         return substr(this.cJson, this.nPos, 1)
  15660.        
  15661.         function nextChar()
  15662.                 this.nPos = this.nPos + 1
  15663.                 if this.nPos > this.nLen
  15664.                         return ''
  15665.                 endif
  15666.         return substr(this.cJson, this.nPos, 1)
  15667.        
  15668.         function setError(cMsg)
  15669.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  15670.         return
  15671.  
  15672.  
  15673.         function fixUnicode(cStr)
  15674.                 cStr = StrTran(cStr,'\u00e1','á')
  15675.                 cStr = StrTran(cStr,'\u00e9','é')
  15676.                 cStr = StrTran(cStr,'\u00ed','í')
  15677.                 cStr = StrTran(cStr,'\u00f3','ó')
  15678.                 cStr = StrTran(cStr,'\u00fa','ú')
  15679.                 cStr = StrTran(cStr,'\u00c1','Á')
  15680.                 cStr = StrTran(cStr,'\u00c9','É')
  15681.                 cStr = StrTran(cStr,'\u00cd','Í')
  15682.                 cStr = StrTran(cStr,'\u00d3','Ó')
  15683.                 cStr = StrTran(cStr,'\u00da','Ú')
  15684.                 cStr = StrTran(cStr,'\u00f1','ń')
  15685.                 cStr = StrTran(cStr,'\u00d1','Ń')
  15686.         return cStr
  15687.  
  15688.  
  15689.  
  15690. enddefine
  15691.  
  15692.  
  15693.  
  15694.  
  15695.  
  15696. *
  15697. * class used to return an array
  15698. *
  15699. define class myArray as custom
  15700.         nSize = 0
  15701.         dimension array[1]
  15702.  
  15703.         function add(xExpr)
  15704.                 this.nSize = this.nSize + 1
  15705.                 dimension this.array[this.nSize]
  15706.                 this.array[this.nSize] = xExpr
  15707.         return
  15708.  
  15709.         function get(n)
  15710.         return this.array[n]
  15711.  
  15712. enddefine
  15713.  
  15714.  
  15715.  
  15716. *
  15717. * class used to simulate an object
  15718. * all properties are prefixed with 'prop' to permit property names like: error, init
  15719. * that already exists like vfp methods
  15720. *
  15721. define class myObj as custom
  15722. Hidden ;
  15723.         ClassLibrary,Comment, ;
  15724.         BaseClass,ControlCount, ;
  15725.         Controls,Objects,Object,;
  15726.         Height,HelpContextID,Left,Name, ;
  15727.         Parent,ParentClass,Picture, ;
  15728.         Tag,Top,WhatsThisHelpID,Width
  15729.                
  15730.         function set(cPropName, xValue)
  15731.                 cPropName = '_'+cPropName
  15732.                 if type('this.'+cPropName)=='U'
  15733.                         this.addProperty(cPropName,xValue)
  15734.                 else
  15735.                         local cmd
  15736.                         cmd = 'this.'+cPropName+'=xValue'
  15737.                         &cmd
  15738.                 endif
  15739.         return
  15740.        
  15741.         procedure get (cPropName)
  15742.                 cPropName = '_'+cPropName
  15743.                 If type('this.'+cPropName)=='U'
  15744.                         return ''
  15745.                 Else
  15746.                         local cmd
  15747.                         cmd = 'return this.'+cPropName
  15748.                         &cmd
  15749.                 endif
  15750.         return ''
  15751. enddefine
  15752.  
  15753.  
  15754.  
  15755.  
  15756.  
  15757. function testJsonClass
  15758.         clear
  15759.         set decimal to 10
  15760.         oJson = newObject('json')
  15761.        
  15762.        
  15763.         ? 'Test Basic Types'
  15764.         ? '----------------'
  15765.         ? oJson.decode('null')
  15766.         ? oJson.decode('true')
  15767.         ? oJson.decode('false')
  15768.         ?
  15769.         ? oJson.decode('791123')
  15770.         ? oJson.decode('791123.45')
  15771.         ? oJson.decode('791123.45.')
  15772.         ? oJson.decode('"nacho gtz"')
  15773.         if not empty(oJson.cError)
  15774.                 ? oJson.cError
  15775.                 return
  15776.         endif
  15777.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  15778.         if not empty(oJson.cError)
  15779.                 ? oJson.cError
  15780.                 return
  15781.         endif
  15782.        
  15783.         ? 'Test Array'
  15784.         ? '----------'
  15785.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  15786.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  15787.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  15788.         nombres = arr.get(1)
  15789.         edades  = arr.get(2)
  15790.         ? nombres.get(1), edades.get(1)
  15791.         ? nombres.get(2), edades.get(2)
  15792.         ? nombres.get(3), edades.get(3)
  15793.         ?
  15794.         ? 'Test Object'
  15795.         ? '-----------'
  15796.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  15797.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  15798.         ? obj._Nombre, obj._Edad, obj._IsGood
  15799.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  15800.         ? obj.get('jsonrpc'), obj._jsonrpc
  15801.         ? obj.get('id'), obj._id
  15802.         ? obj.get('method'), obj._method
  15803.         ? obj._Params.array[1], obj._Params.get(1)
  15804.  
  15805.         ?
  15806.         ? 'Test nested object'
  15807.         ? '------------------'
  15808.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  15809.         obj = oJson.decode(cJson)
  15810.         if not empty(oJson.cError)
  15811.                 ? oJson.cError
  15812.                 return
  15813.         endif
  15814.         ? cJson
  15815.         ? 'method -->',obj._method
  15816.         ? 'usrkey -->',obj._params._data._usrkey
  15817.         ? 'sendto -->',obj._params._data._sendto
  15818.         ? 'name  --->',obj._params._data._name
  15819.         ? 'expires ->',obj._params._data._expires
  15820.  
  15821.         ?
  15822.         ? 'Test empty object'
  15823.         ? '-----------------'
  15824.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  15825.         obj = oJson.decode(cJson)
  15826.         if not empty(oJson.cError)     
  15827.                 ? oJson.cError
  15828.                 return
  15829.         endif
  15830.         ? cJson
  15831.         ? 'result -->',obj._result, obj.get('result')
  15832.         oError = obj.get('error')
  15833.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  15834.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  15835.         ? 'id  ----->',obj._id, obj.get('id')
  15836.         ?  type("oError._code")
  15837.  
  15838.         ?
  15839.         ? 'Probar decode-enconde-decode-encode'
  15840.         ? '------------------------------------'
  15841.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  15842.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  15843.         ? cJson
  15844.         oSmtp = json_decode(cJson)
  15845.         cJson =  json_encode(oSmtp)
  15846.         ? cJson
  15847.         oSmtp = json_decode(cJson)
  15848.         cJson =  json_encode(oSmtp)
  15849.         ? cJson
  15850.  
  15851.         * Probar falla
  15852.         ?
  15853.         ? 'Probar una falla en el json'
  15854.         ? '---------------------------'
  15855.         cJson = ' {"server":"", "user":"", "password":"" ,'
  15856.         oSmtp = json_decode(cJson)
  15857.         if not empty(json_getErrorMsg())
  15858.                 ? json_getErrorMsg()
  15859.         endif
  15860.  
  15861.         ?
  15862.         ? 'Pruebas Finalizadas'
  15863. retur
  15864. *       oCliente = oJson.decode( ' { "nombre":"Ignacio" , "apellido":"Gutierrez", "edad":33 } ')
  15865. *       ? oJson.encode(oCliente)
  15866. *       ? oCliente.get('nombre')
  15867. *       ? oCliente.get('apellido')
  15868. *
  15869. *
  15870. * VFPJSON  Encode and Decode JSON for VFP
  15871. * Examples:
  15872. *       oJson = newobject('json','json.prg')
  15873. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  15874. *       ? oJson.encode(oCustomer)
  15875. *       ? oCustomer.get('name')
  15876. *       ? oCustomer.get('lastname')
  15877. *
  15878. *
  15879. lRunTest = .f.
  15880. if lRunTest
  15881.         testJsonClass()
  15882. endif
  15883. return
  15884.  
  15885.  
  15886. function json_encode(xExpr)
  15887.         if vartype(_json)<>'O'
  15888.                 public _json
  15889.                 _json = newobject('json')
  15890.         endif
  15891. return _json.encode(@xExpr)
  15892.  
  15893.  
  15894. function json_decode(cJson)
  15895. local retval
  15896.         if vartype(_json)<>'O'
  15897.                 public _json
  15898.                 _json = newobject('json')
  15899.         endif
  15900.         retval = _json.decode(cJson)
  15901.         if not empty(_json.cError)
  15902.                 return null
  15903.         endif
  15904. return retval
  15905.  
  15906. function json_getErrorMsg()
  15907. return _json.cError
  15908.        
  15909.  
  15910.  
  15911. *
  15912. * json class
  15913. *
  15914. *
  15915. define class json as custom
  15916.  
  15917.  
  15918.         nPos=0
  15919.         nLen=0
  15920.         cJson=''
  15921.         cError=''
  15922.  
  15923.  
  15924.         *
  15925.         * Genera el codigo cJson para parametro que se manda
  15926.         *
  15927.         function encode(xExpr)
  15928.         local cTipo
  15929.                 * Cuando se manda una arreglo,
  15930.                 if type('ALen(xExpr)')=='N'
  15931.                         cTipo = 'A'
  15932.                 Else
  15933.                         cTipo = VarType(xExpr)
  15934.                 Endif
  15935.                
  15936.                 Do Case
  15937.                 Case cTipo=='D'
  15938.                         return '"'+dtos(xExpr)+'"'
  15939.                 Case cTipo=='N'
  15940.                         return Transform(xExpr)
  15941.                 Case cTipo=='L'
  15942.                         return iif(xExpr,'true','false')
  15943.                 Case cTipo=='X'
  15944.                         return 'null'
  15945.                 Case cTipo=='C'
  15946.                         xExpr = allt(xExpr)
  15947.                         xExpr = StrTran(xExpr, '\', '\\' )
  15948.                         xExpr = StrTran(xExpr, '/', '\/' )
  15949.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  15950.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  15951.                         xExpr = StrTran(xExpr, '"', '\"' )
  15952.                         return '"'+xExpr+'"'
  15953.  
  15954.                 case cTipo=='O'
  15955.                         local cProp, cJsonValue, cRetVal, aProp[1]
  15956.                         =AMembers(aProp,xExpr)
  15957.                         cRetVal = ''
  15958.                         for each cProp in aProp
  15959.                                 *? cProp
  15960.                                 *? cRetVal
  15961.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  15962.                                         * algunas propiedades pueden no estar definidas
  15963.                                         * como: activecontrol, parent, etc
  15964.                                         loop
  15965.                                 endif
  15966.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  15967.                                         *
  15968.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  15969.                                         *
  15970.                                         Local i,nTotElem
  15971.                                         cJsonValue = ''
  15972.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  15973.                                         For i=1 to nTotElem
  15974.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  15975.                                         Next
  15976.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  15977.                                 else
  15978.                                         *
  15979.                                         * es otro tipo de dato normal C, N, L
  15980.                                         *
  15981.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  15982.                                 endif
  15983.                                 if left(cProp,1)=='_'
  15984.                                         cProp = substr(cProp,2)
  15985.                                 endif
  15986.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  15987.                         next
  15988.                         return '{' + substr(cRetVal,2) + '}'
  15989.  
  15990.                 case cTipo=='A'
  15991.                         local valor, cRetVal
  15992.                         cRetVal = ''   
  15993.                         for each valor in xExpr
  15994.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  15995.                         next
  15996.                         return  '[' + substr(cRetVal,2) + ']'
  15997.                        
  15998.                 endcase
  15999.  
  16000.         return ''
  16001.  
  16002.  
  16003.  
  16004.  
  16005.  
  16006.         *
  16007.         * regresa un elemento representado por la cadena json que se manda
  16008.         *
  16009.        
  16010.         function decode(cJson)
  16011.         local retValue
  16012.                 cJson = StrTran(cJson,chr(9),'')
  16013.                 cJson = StrTran(cJson,chr(10),'')
  16014.                 cJson = StrTran(cJson,chr(13),'')
  16015.                 cJson = this.fixUnicode(cJson)
  16016.                 this.nPos  = 1
  16017.                 this.cJson = cJson
  16018.                 this.nLen  = len(cJson)
  16019.                 this.cError = ''
  16020.                 retValue = this.parsevalue()
  16021.                 if not empty(this.cError)
  16022.                         return null
  16023.                 endif
  16024.                 if this.getToken()<>null
  16025.                         this.setError('Junk at the end of JSON input')
  16026.                         return null
  16027.                 endif
  16028.         return retValue
  16029.                
  16030.        
  16031.         function parseValue()
  16032.         local token
  16033.                 token = this.getToken()
  16034.                 if token==null
  16035.                         this.setError('Nothing to parse')
  16036.                         return null
  16037.                 endif
  16038.                 do case
  16039.                 case token=='"'
  16040.                         return this.parseString()
  16041.                 case isdigit(token) or token=='-'
  16042.                         return this.parseNumber()
  16043.                 case token=='n'
  16044.                         return this.expectedKeyword('null',null)
  16045.                 case token=='f'
  16046.                         return this.expectedKeyword('false',.f.)
  16047.                 case token=='t'
  16048.                         return this.expectedKeyword('true',.t.)
  16049.                 case token=='{'
  16050.                         return this.parseObject()
  16051.                 case token=='['
  16052.                         return this.parseArray()
  16053.                 otherwise
  16054.                         this.setError('Unexpected token')
  16055.                 endcase
  16056.         return
  16057.                
  16058.        
  16059.         function expectedKeyword(cWord,eValue)
  16060.                 for i=1 to len(cWord)
  16061.                         cChar = this.getChar()
  16062.                         if cChar <> substr(cWord,i,1)
  16063.                                 this.setError("Expected keyword '" + cWord + "'")
  16064.                                 return ''
  16065.                         endif
  16066.                         this.nPos = this.nPos + 1
  16067.                 next
  16068.         return eValue
  16069.        
  16070.  
  16071.         function parseObject()
  16072.         local retval, cPropName, xValue
  16073.                 retval = createObject('myObj')
  16074.                 this.nPos = this.nPos + 1 && Eat {
  16075.                 if this.getToken()<>'}'
  16076.                         do while .t.
  16077.                                 cPropName = this.parseString()
  16078.                                 if not empty(this.cError)
  16079.                                         return null
  16080.                                 endif
  16081.                                 if this.getToken()<>':'
  16082.                                         this.setError("Expected ':' when parsing object")
  16083.                                         return null
  16084.                                 endif
  16085.                                 this.nPos = this.nPos + 1
  16086.                                 xValue = this.parseValue()
  16087.                                 if not empty(this.cError)
  16088.                                         return null
  16089.                                 endif                          
  16090.                                 ** Debug ? cPropName, type('xValue')
  16091.                                 retval.set(cPropName, xValue)
  16092.                                 if this.getToken()<>','
  16093.                                         exit
  16094.                                 endif
  16095.                                 this.nPos = this.nPos + 1
  16096.                         enddo
  16097.                 endif
  16098.                 if this.getToken()<>'}'
  16099.                         this.setError("Expected '}' at the end of object")
  16100.                         return null
  16101.                 endif
  16102.                 this.nPos = this.nPos + 1
  16103.         return retval
  16104.  
  16105.  
  16106.         function parseArray()
  16107.         local retVal, xValue
  16108.                 retval = createObject('MyArray')
  16109.                 this.nPos = this.nPos + 1       && Eat [
  16110.                 if this.getToken() <> ']'
  16111.                         do while .t.
  16112.                                 xValue = this.parseValue()
  16113.                                 if not empty(this.cError)
  16114.                                         return null
  16115.                                 endif
  16116.                                 retval.add( xValue )
  16117.                                 if this.getToken()<>','
  16118.                                         exit
  16119.                                 endif
  16120.                                 this.nPos = this.nPos + 1
  16121.                         enddo
  16122.                         if this.getToken() <> ']'
  16123.                                 this.setError('Expected ] at the end of array')
  16124.                                 return null
  16125.                         endif
  16126.                 endif
  16127.                 this.nPos = this.nPos + 1
  16128.         return retval
  16129.        
  16130.  
  16131.         function parseString()
  16132.         local cRetVal, c
  16133.                 if this.getToken()<>'"'
  16134.                         this.setError('Expected "')
  16135.                         return ''
  16136.                 endif
  16137.                 this.nPos = this.nPos + 1       && Eat "
  16138.                 cRetVal = ''
  16139.                 do while .t.
  16140.                         c = this.getChar()
  16141.                         if c==''
  16142.                                 return ''
  16143.                         endif
  16144.                         if c == '"'
  16145.                                 this.nPos = this.nPos + 1
  16146.                                 exit
  16147.                         endif
  16148.                         if c == '\'
  16149.                                 this.nPos = this.nPos + 1
  16150.                                 if (this.nPos>this.nLen)
  16151.                                         this.setError('\\ at the end of input')
  16152.                                         return ''
  16153.                                 endif
  16154.                                 c = this.getChar()
  16155.                                 if c==''
  16156.                                         return ''
  16157.                                 endif
  16158.                                 do case
  16159.                                 case c=='"'
  16160.                                         c='"'
  16161.                                 case c=='\'
  16162.                                         c='\'
  16163.                                 case c=='/'
  16164.                                         c='/'
  16165.                                 case c=='b'
  16166.                                         c=chr(8)
  16167.                                 case c=='t'
  16168.                                         c=chr(9)
  16169.                                 case c=='n'
  16170.                                         c=chr(10)
  16171.                                 case c=='f'
  16172.                                         c=chr(12)
  16173.                                 case c=='r'
  16174.                                         c=chr(13)
  16175.                                 otherwise
  16176.                                         ******* FALTAN LOS UNICODE
  16177.                                         this.setError('Invalid escape sequence in string literal')
  16178.                                         return ''
  16179.                                 endcase
  16180.                         endif
  16181.                         cRetVal = cRetVal + c
  16182.                         this.nPos = this.nPos + 1
  16183.                 enddo
  16184.         return cRetVal
  16185.                                        
  16186.  
  16187.         **** Pendiente numeros con E
  16188.         function parseNumber()
  16189.         local nStartPos,c, isInt, cNumero
  16190.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  16191.                         this.setError('Expected number literal')
  16192.                         return 0
  16193.                 endif
  16194.                 nStartPos = this.nPos
  16195.                 c = this.getChar()
  16196.                 if c == '-'
  16197.                         c = this.nextChar()
  16198.                 endif
  16199.                 if c == '0'
  16200.                         c = this.nextChar()
  16201.                 else
  16202.                         if isdigit(c)
  16203.                                 c = this.nextChar()
  16204.                                 do while isdigit(c)
  16205.                                         c = this.nextChar()
  16206.                                 enddo
  16207.                         else
  16208.                                 this.setError('Expected digit when parsing number')
  16209.                                 return 0
  16210.                         endif
  16211.                 endif
  16212.                
  16213.                 isInt = .t.
  16214.                 if c=='.'
  16215.                         c = this.nextChar()
  16216.                         if isdigit(c)
  16217.                                 c = this.nextChar()
  16218.                                 isInt = .f.
  16219.                                 do while isDigit(c)
  16220.                                         c = this.nextChar()
  16221.                                 enddo
  16222.                         else
  16223.                                 this.setError('Expected digit following dot comma')
  16224.                                 return 0
  16225.                         endif
  16226.                 endif
  16227.                
  16228.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  16229.         return val(cNumero)
  16230.  
  16231.  
  16232.  
  16233.         function getToken()
  16234.         local char1
  16235.                 do while .t.
  16236.                         if this.nPos > this.nLen
  16237.                                 return null
  16238.                         endif
  16239.                         char1 = substr(this.cJson, this.nPos, 1)
  16240.                         if char1==' '
  16241.                                 this.nPos = this.nPos + 1
  16242.                                 loop
  16243.                         endif
  16244.                         return char1
  16245.                 enddo
  16246.         return
  16247.        
  16248.                
  16249.                
  16250.         function getChar()
  16251.                 if this.nPos > this.nLen
  16252.                         this.setError('Unexpected end of JSON stream')
  16253.                         return ''
  16254.                 endif
  16255.         return substr(this.cJson, this.nPos, 1)
  16256.        
  16257.         function nextChar()
  16258.                 this.nPos = this.nPos + 1
  16259.                 if this.nPos > this.nLen
  16260.                         return ''
  16261.                 endif
  16262.         return substr(this.cJson, this.nPos, 1)
  16263.        
  16264.         function setError(cMsg)
  16265.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  16266.         return
  16267.  
  16268.  
  16269.         function fixUnicode(cStr)
  16270.                 cStr = StrTran(cStr,'\u00e1','á')
  16271.                 cStr = StrTran(cStr,'\u00e9','é')
  16272.                 cStr = StrTran(cStr,'\u00ed','í')
  16273.                 cStr = StrTran(cStr,'\u00f3','ó')
  16274.                 cStr = StrTran(cStr,'\u00fa','ú')
  16275.                 cStr = StrTran(cStr,'\u00c1','Á')
  16276.                 cStr = StrTran(cStr,'\u00c9','É')
  16277.                 cStr = StrTran(cStr,'\u00cd','Í')
  16278.                 cStr = StrTran(cStr,'\u00d3','Ó')
  16279.                 cStr = StrTran(cStr,'\u00da','Ú')
  16280.                 cStr = StrTran(cStr,'\u00f1','ń')
  16281.                 cStr = StrTran(cStr,'\u00d1','Ń')
  16282.         return cStr
  16283.  
  16284.  
  16285.  
  16286. enddefine
  16287.  
  16288.  
  16289.  
  16290.  
  16291.  
  16292. *
  16293. * class used to return an array
  16294. *
  16295. define class myArray as custom
  16296.         nSize = 0
  16297.         dimension array[1]
  16298.  
  16299.         function add(xExpr)
  16300.                 this.nSize = this.nSize + 1
  16301.                 dimension this.array[this.nSize]
  16302.                 this.array[this.nSize] = xExpr
  16303.         return
  16304.  
  16305.         function get(n)
  16306.         return this.array[n]
  16307.  
  16308. enddefine
  16309.  
  16310.  
  16311.  
  16312. *
  16313. * class used to simulate an object
  16314. * all properties are prefixed with 'prop' to permit property names like: error, init
  16315. * that already exists like vfp methods
  16316. *
  16317. define class myObj as custom
  16318. Hidden ;
  16319.         ClassLibrary,Comment, ;
  16320.         BaseClass,ControlCount, ;
  16321.         Controls,Objects,Object,;
  16322.         Height,HelpContextID,Left,Name, ;
  16323.         Parent,ParentClass,Picture, ;
  16324.         Tag,Top,WhatsThisHelpID,Width
  16325.                
  16326.         function set(cPropName, xValue)
  16327.                 cPropName = '_'+cPropName
  16328.                 if type('this.'+cPropName)=='U'
  16329.                         this.addProperty(cPropName,xValue)
  16330.                 else
  16331.                         local cmd
  16332.                         cmd = 'this.'+cPropName+'=xValue'
  16333.                         &cmd
  16334.                 endif
  16335.         return
  16336.        
  16337.         procedure get (cPropName)
  16338.                 cPropName = '_'+cPropName
  16339.                 If type('this.'+cPropName)=='U'
  16340.                         return ''
  16341.                 Else
  16342.                         local cmd
  16343.                         cmd = 'return this.'+cPropName
  16344.                         &cmd
  16345.                 endif
  16346.         return ''
  16347. enddefine
  16348.  
  16349.  
  16350.  
  16351.  
  16352.  
  16353. function testJsonClass
  16354.         clear
  16355.         set decimal to 10
  16356.         oJson = newObject('json')
  16357.        
  16358.        
  16359.         ? 'Test Basic Types'
  16360.         ? '----------------'
  16361.         ? oJson.decode('null')
  16362.         ? oJson.decode('true')
  16363.         ? oJson.decode('false')
  16364.         ?
  16365.         ? oJson.decode('791123')
  16366.         ? oJson.decode('791123.45')
  16367.         ? oJson.decode('791123.45.')
  16368.         ? oJson.decode('"nacho gtz"')
  16369.         if not empty(oJson.cError)
  16370.                 ? oJson.cError
  16371.                 return
  16372.         endif
  16373.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  16374.         if not empty(oJson.cError)
  16375.                 ? oJson.cError
  16376.                 return
  16377.         endif
  16378.        
  16379.         ? 'Test Array'
  16380.         ? '----------'
  16381.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  16382.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  16383.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  16384.         nombres = arr.get(1)
  16385.         edades  = arr.get(2)
  16386.         ? nombres.get(1), edades.get(1)
  16387.         ? nombres.get(2), edades.get(2)
  16388.         ? nombres.get(3), edades.get(3)
  16389.         ?
  16390.         ? 'Test Object'
  16391.         ? '-----------'
  16392.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  16393.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  16394.         ? obj._Nombre, obj._Edad, obj._IsGood
  16395.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  16396.         ? obj.get('jsonrpc'), obj._jsonrpc
  16397.         ? obj.get('id'), obj._id
  16398.         ? obj.get('method'), obj._method
  16399.         ? obj._Params.array[1], obj._Params.get(1)
  16400.  
  16401.         ?
  16402.         ? 'Test nested object'
  16403.         ? '------------------'
  16404.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  16405.         obj = oJson.decode(cJson)
  16406.         if not empty(oJson.cError)
  16407.                 ? oJson.cError
  16408.                 return
  16409.         endif
  16410.         ? cJson
  16411.         ? 'method -->',obj._method
  16412.         ? 'usrkey -->',obj._params._data._usrkey
  16413.         ? 'sendto -->',obj._params._data._sendto
  16414.         ? 'name  --->',obj._params._data._name
  16415.         ? 'expires ->',obj._params._data._expires
  16416.  
  16417.         ?
  16418.         ? 'Test empty object'
  16419.         ? '-----------------'
  16420.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  16421.         obj = oJson.decode(cJson)
  16422.         if not empty(oJson.cError)     
  16423.                 ? oJson.cError
  16424.                 return
  16425.         endif
  16426.         ? cJson
  16427.         ? 'result -->',obj._result, obj.get('result')
  16428.         oError = obj.get('error')
  16429.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  16430.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  16431.         ? 'id  ----->',obj._id, obj.get('id')
  16432.         ?  type("oError._code")
  16433.  
  16434.         ?
  16435.         ? 'Probar decode-enconde-decode-encode'
  16436.         ? '------------------------------------'
  16437.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  16438.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  16439.         ? cJson
  16440.         oSmtp = json_decode(cJson)
  16441.         cJson =  json_encode(oSmtp)
  16442.         ? cJson
  16443.         oSmtp = json_decode(cJson)
  16444.         cJson =  json_encode(oSmtp)
  16445.         ? cJson
  16446.  
  16447.         * Probar falla
  16448.         ?
  16449.         ? 'Probar una falla en el json'
  16450.         ? '---------------------------'
  16451.         cJson = ' {"server":"", "user":"", "password":"" ,'
  16452.         oSmtp = json_decode(cJson)
  16453.         if not empty(json_getErrorMsg())
  16454.                 ? json_getErrorMsg()
  16455.         endif
  16456.  
  16457.         ?
  16458.         ? 'Pruebas Finalizadas'
  16459. retur
  16460. *       ? oJson.encode(oCliente)
  16461. *       ? oCliente.get('nombre')
  16462. *       ? oCliente.get('apellido')
  16463. *
  16464. *
  16465. * VFPJSON  Encode and Decode JSON for VFP
  16466. * Examples:
  16467. *       oJson = newobject('json','json.prg')
  16468. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  16469. *       ? oJson.encode(oCustomer)
  16470. *       ? oCustomer.get('name')
  16471. *       ? oCustomer.get('lastname')
  16472. *
  16473. *
  16474. lRunTest = .f.
  16475. if lRunTest
  16476.         testJsonClass()
  16477. endif
  16478. return
  16479.  
  16480.  
  16481. function json_encode(xExpr)
  16482.         if vartype(_json)<>'O'
  16483.                 public _json
  16484.                 _json = newobject('json')
  16485.         endif
  16486. return _json.encode(@xExpr)
  16487.  
  16488.  
  16489. function json_decode(cJson)
  16490. local retval
  16491.         if vartype(_json)<>'O'
  16492.                 public _json
  16493.                 _json = newobject('json')
  16494.         endif
  16495.         retval = _json.decode(cJson)
  16496.         if not empty(_json.cError)
  16497.                 return null
  16498.         endif
  16499. return retval
  16500.  
  16501. function json_getErrorMsg()
  16502. return _json.cError
  16503.        
  16504.  
  16505.  
  16506. *
  16507. * json class
  16508. *
  16509. *
  16510. define class json as custom
  16511.  
  16512.  
  16513.         nPos=0
  16514.         nLen=0
  16515.         cJson=''
  16516.         cError=''
  16517.  
  16518.  
  16519.         *
  16520.         * Genera el codigo cJson para parametro que se manda
  16521.         *
  16522.         function encode(xExpr)
  16523.         local cTipo
  16524.                 * Cuando se manda una arreglo,
  16525.                 if type('ALen(xExpr)')=='N'
  16526.                         cTipo = 'A'
  16527.                 Else
  16528.                         cTipo = VarType(xExpr)
  16529.                 Endif
  16530.                
  16531.                 Do Case
  16532.                 Case cTipo=='D'
  16533.                         return '"'+dtos(xExpr)+'"'
  16534.                 Case cTipo=='N'
  16535.                         return Transform(xExpr)
  16536.                 Case cTipo=='L'
  16537.                         return iif(xExpr,'true','false')
  16538.                 Case cTipo=='X'
  16539.                         return 'null'
  16540.                 Case cTipo=='C'
  16541.                         xExpr = allt(xExpr)
  16542.                         xExpr = StrTran(xExpr, '\', '\\' )
  16543.                         xExpr = StrTran(xExpr, '/', '\/' )
  16544.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  16545.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  16546.                         xExpr = StrTran(xExpr, '"', '\"' )
  16547.                         return '"'+xExpr+'"'
  16548.  
  16549.                 case cTipo=='O'
  16550.                         local cProp, cJsonValue, cRetVal, aProp[1]
  16551.                         =AMembers(aProp,xExpr)
  16552.                         cRetVal = ''
  16553.                         for each cProp in aProp
  16554.                                 *? cProp
  16555.                                 *? cRetVal
  16556.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  16557.                                         * algunas propiedades pueden no estar definidas
  16558.                                         * como: activecontrol, parent, etc
  16559.                                         loop
  16560.                                 endif
  16561.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  16562.                                         *
  16563.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  16564.                                         *
  16565.                                         Local i,nTotElem
  16566.                                         cJsonValue = ''
  16567.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  16568.                                         For i=1 to nTotElem
  16569.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  16570.                                         Next
  16571.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  16572.                                 else
  16573.                                         *
  16574.                                         * es otro tipo de dato normal C, N, L
  16575.                                         *
  16576.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  16577.                                 endif
  16578.                                 if left(cProp,1)=='_'
  16579.                                         cProp = substr(cProp,2)
  16580.                                 endif
  16581.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  16582.                         next
  16583.                         return '{' + substr(cRetVal,2) + '}'
  16584.  
  16585.                 case cTipo=='A'
  16586.                         local valor, cRetVal
  16587.                         cRetVal = ''   
  16588.                         for each valor in xExpr
  16589.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  16590.                         next
  16591.                         return  '[' + substr(cRetVal,2) + ']'
  16592.                        
  16593.                 endcase
  16594.  
  16595.         return ''
  16596.  
  16597.  
  16598.  
  16599.  
  16600.  
  16601.         *
  16602.         * regresa un elemento representado por la cadena json que se manda
  16603.         *
  16604.        
  16605.         function decode(cJson)
  16606.         local retValue
  16607.                 cJson = StrTran(cJson,chr(9),'')
  16608.                 cJson = StrTran(cJson,chr(10),'')
  16609.                 cJson = StrTran(cJson,chr(13),'')
  16610.                 cJson = this.fixUnicode(cJson)
  16611.                 this.nPos  = 1
  16612.                 this.cJson = cJson
  16613.                 this.nLen  = len(cJson)
  16614.                 this.cError = ''
  16615.                 retValue = this.parsevalue()
  16616.                 if not empty(this.cError)
  16617.                         return null
  16618.                 endif
  16619.                 if this.getToken()<>null
  16620.                         this.setError('Junk at the end of JSON input')
  16621.                         return null
  16622.                 endif
  16623.         return retValue
  16624.                
  16625.        
  16626.         function parseValue()
  16627.         local token
  16628.                 token = this.getToken()
  16629.                 if token==null
  16630.                         this.setError('Nothing to parse')
  16631.                         return null
  16632.                 endif
  16633.                 do case
  16634.                 case token=='"'
  16635.                         return this.parseString()
  16636.                 case isdigit(token) or token=='-'
  16637.                         return this.parseNumber()
  16638.                 case token=='n'
  16639.                         return this.expectedKeyword('null',null)
  16640.                 case token=='f'
  16641.                         return this.expectedKeyword('false',.f.)
  16642.                 case token=='t'
  16643.                         return this.expectedKeyword('true',.t.)
  16644.                 case token=='{'
  16645.                         return this.parseObject()
  16646.                 case token=='['
  16647.                         return this.parseArray()
  16648.                 otherwise
  16649.                         this.setError('Unexpected token')
  16650.                 endcase
  16651.         return
  16652.                
  16653.        
  16654.         function expectedKeyword(cWord,eValue)
  16655.                 for i=1 to len(cWord)
  16656.                         cChar = this.getChar()
  16657.                         if cChar <> substr(cWord,i,1)
  16658.                                 this.setError("Expected keyword '" + cWord + "'")
  16659.                                 return ''
  16660.                         endif
  16661.                         this.nPos = this.nPos + 1
  16662.                 next
  16663.         return eValue
  16664.        
  16665.  
  16666.         function parseObject()
  16667.         local retval, cPropName, xValue
  16668.                 retval = createObject('myObj')
  16669.                 this.nPos = this.nPos + 1 && Eat {
  16670.                 if this.getToken()<>'}'
  16671.                         do while .t.
  16672.                                 cPropName = this.parseString()
  16673.                                 if not empty(this.cError)
  16674.                                         return null
  16675.                                 endif
  16676.                                 if this.getToken()<>':'
  16677.                                         this.setError("Expected ':' when parsing object")
  16678.                                         return null
  16679.                                 endif
  16680.                                 this.nPos = this.nPos + 1
  16681.                                 xValue = this.parseValue()
  16682.                                 if not empty(this.cError)
  16683.                                         return null
  16684.                                 endif                          
  16685.                                 ** Debug ? cPropName, type('xValue')
  16686.                                 retval.set(cPropName, xValue)
  16687.                                 if this.getToken()<>','
  16688.                                         exit
  16689.                                 endif
  16690.                                 this.nPos = this.nPos + 1
  16691.                         enddo
  16692.                 endif
  16693.                 if this.getToken()<>'}'
  16694.                         this.setError("Expected '}' at the end of object")
  16695.                         return null
  16696.                 endif
  16697.                 this.nPos = this.nPos + 1
  16698.         return retval
  16699.  
  16700.  
  16701.         function parseArray()
  16702.         local retVal, xValue
  16703.                 retval = createObject('MyArray')
  16704.                 this.nPos = this.nPos + 1       && Eat [
  16705.                 if this.getToken() <> ']'
  16706.                         do while .t.
  16707.                                 xValue = this.parseValue()
  16708.                                 if not empty(this.cError)
  16709.                                         return null
  16710.                                 endif
  16711.                                 retval.add( xValue )
  16712.                                 if this.getToken()<>','
  16713.                                         exit
  16714.                                 endif
  16715.                                 this.nPos = this.nPos + 1
  16716.                         enddo
  16717.                         if this.getToken() <> ']'
  16718.                                 this.setError('Expected ] at the end of array')
  16719.                                 return null
  16720.                         endif
  16721.                 endif
  16722.                 this.nPos = this.nPos + 1
  16723.         return retval
  16724.        
  16725.  
  16726.         function parseString()
  16727.         local cRetVal, c
  16728.                 if this.getToken()<>'"'
  16729.                         this.setError('Expected "')
  16730.                         return ''
  16731.                 endif
  16732.                 this.nPos = this.nPos + 1       && Eat "
  16733.                 cRetVal = ''
  16734.                 do while .t.
  16735.                         c = this.getChar()
  16736.                         if c==''
  16737.                                 return ''
  16738.                         endif
  16739.                         if c == '"'
  16740.                                 this.nPos = this.nPos + 1
  16741.                                 exit
  16742.                         endif
  16743.                         if c == '\'
  16744.                                 this.nPos = this.nPos + 1
  16745.                                 if (this.nPos>this.nLen)
  16746.                                         this.setError('\\ at the end of input')
  16747.                                         return ''
  16748.                                 endif
  16749.                                 c = this.getChar()
  16750.                                 if c==''
  16751.                                         return ''
  16752.                                 endif
  16753.                                 do case
  16754.                                 case c=='"'
  16755.                                         c='"'
  16756.                                 case c=='\'
  16757.                                         c='\'
  16758.                                 case c=='/'
  16759.                                         c='/'
  16760.                                 case c=='b'
  16761.                                         c=chr(8)
  16762.                                 case c=='t'
  16763.                                         c=chr(9)
  16764.                                 case c=='n'
  16765.                                         c=chr(10)
  16766.                                 case c=='f'
  16767.                                         c=chr(12)
  16768.                                 case c=='r'
  16769.                                         c=chr(13)
  16770.                                 otherwise
  16771.                                         ******* FALTAN LOS UNICODE
  16772.                                         this.setError('Invalid escape sequence in string literal')
  16773.                                         return ''
  16774.                                 endcase
  16775.                         endif
  16776.                         cRetVal = cRetVal + c
  16777.                         this.nPos = this.nPos + 1
  16778.                 enddo
  16779.         return cRetVal
  16780.                                        
  16781.  
  16782.         **** Pendiente numeros con E
  16783.         function parseNumber()
  16784.         local nStartPos,c, isInt, cNumero
  16785.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  16786.                         this.setError('Expected number literal')
  16787.                         return 0
  16788.                 endif
  16789.                 nStartPos = this.nPos
  16790.                 c = this.getChar()
  16791.                 if c == '-'
  16792.                         c = this.nextChar()
  16793.                 endif
  16794.                 if c == '0'
  16795.                         c = this.nextChar()
  16796.                 else
  16797.                         if isdigit(c)
  16798.                                 c = this.nextChar()
  16799.                                 do while isdigit(c)
  16800.                                         c = this.nextChar()
  16801.                                 enddo
  16802.                         else
  16803.                                 this.setError('Expected digit when parsing number')
  16804.                                 return 0
  16805.                         endif
  16806.                 endif
  16807.                
  16808.                 isInt = .t.
  16809.                 if c=='.'
  16810.                         c = this.nextChar()
  16811.                         if isdigit(c)
  16812.                                 c = this.nextChar()
  16813.                                 isInt = .f.
  16814.                                 do while isDigit(c)
  16815.                                         c = this.nextChar()
  16816.                                 enddo
  16817.                         else
  16818.                                 this.setError('Expected digit following dot comma')
  16819.                                 return 0
  16820.                         endif
  16821.                 endif
  16822.                
  16823.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  16824.         return val(cNumero)
  16825.  
  16826.  
  16827.  
  16828.         function getToken()
  16829.         local char1
  16830.                 do while .t.
  16831.                         if this.nPos > this.nLen
  16832.                                 return null
  16833.                         endif
  16834.                         char1 = substr(this.cJson, this.nPos, 1)
  16835.                         if char1==' '
  16836.                                 this.nPos = this.nPos + 1
  16837.                                 loop
  16838.                         endif
  16839.                         return char1
  16840.                 enddo
  16841.         return
  16842.        
  16843.                
  16844.                
  16845.         function getChar()
  16846.                 if this.nPos > this.nLen
  16847.                         this.setError('Unexpected end of JSON stream')
  16848.                         return ''
  16849.                 endif
  16850.         return substr(this.cJson, this.nPos, 1)
  16851.        
  16852.         function nextChar()
  16853.                 this.nPos = this.nPos + 1
  16854.                 if this.nPos > this.nLen
  16855.                         return ''
  16856.                 endif
  16857.         return substr(this.cJson, this.nPos, 1)
  16858.        
  16859.         function setError(cMsg)
  16860.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  16861.         return
  16862.  
  16863.  
  16864.         function fixUnicode(cStr)
  16865.                 cStr = StrTran(cStr,'\u00e1','á')
  16866.                 cStr = StrTran(cStr,'\u00e9','é')
  16867.                 cStr = StrTran(cStr,'\u00ed','í')
  16868.                 cStr = StrTran(cStr,'\u00f3','ó')
  16869.                 cStr = StrTran(cStr,'\u00fa','ú')
  16870.                 cStr = StrTran(cStr,'\u00c1','Á')
  16871.                 cStr = StrTran(cStr,'\u00c9','É')
  16872.                 cStr = StrTran(cStr,'\u00cd','Í')
  16873.                 cStr = StrTran(cStr,'\u00d3','Ó')
  16874.                 cStr = StrTran(cStr,'\u00da','Ú')
  16875.                 cStr = StrTran(cStr,'\u00f1','ń')
  16876.                 cStr = StrTran(cStr,'\u00d1','Ń')
  16877.         return cStr
  16878.  
  16879.  
  16880.  
  16881. enddefine
  16882.  
  16883.  
  16884.  
  16885.  
  16886.  
  16887. *
  16888. * class used to return an array
  16889. *
  16890. define class myArray as custom
  16891.         nSize = 0
  16892.         dimension array[1]
  16893.  
  16894.         function add(xExpr)
  16895.                 this.nSize = this.nSize + 1
  16896.                 dimension this.array[this.nSize]
  16897.                 this.array[this.nSize] = xExpr
  16898.         return
  16899.  
  16900.         function get(n)
  16901.         return this.array[n]
  16902.  
  16903. enddefine
  16904.  
  16905.  
  16906.  
  16907. *
  16908. * class used to simulate an object
  16909. * all properties are prefixed with 'prop' to permit property names like: error, init
  16910. * that already exists like vfp methods
  16911. *
  16912. define class myObj as custom
  16913. Hidden ;
  16914.         ClassLibrary,Comment, ;
  16915.         BaseClass,ControlCount, ;
  16916.         Controls,Objects,Object,;
  16917.         Height,HelpContextID,Left,Name, ;
  16918.         Parent,ParentClass,Picture, ;
  16919.         Tag,Top,WhatsThisHelpID,Width
  16920.                
  16921.         function set(cPropName, xValue)
  16922.                 cPropName = '_'+cPropName
  16923.                 if type('this.'+cPropName)=='U'
  16924.                         this.addProperty(cPropName,xValue)
  16925.                 else
  16926.                         local cmd
  16927.                         cmd = 'this.'+cPropName+'=xValue'
  16928.                         &cmd
  16929.                 endif
  16930.         return
  16931.        
  16932.         procedure get (cPropName)
  16933.                 cPropName = '_'+cPropName
  16934.                 If type('this.'+cPropName)=='U'
  16935.                         return ''
  16936.                 Else
  16937.                         local cmd
  16938.                         cmd = 'return this.'+cPropName
  16939.                         &cmd
  16940.                 endif
  16941.         return ''
  16942. enddefine
  16943.  
  16944.  
  16945.  
  16946.  
  16947.  
  16948. function testJsonClass
  16949.         clear
  16950.         set decimal to 10
  16951.         oJson = newObject('json')
  16952.        
  16953.        
  16954.         ? 'Test Basic Types'
  16955.         ? '----------------'
  16956.         ? oJson.decode('null')
  16957.         ? oJson.decode('true')
  16958.         ? oJson.decode('false')
  16959.         ?
  16960.         ? oJson.decode('791123')
  16961.         ? oJson.decode('791123.45')
  16962.         ? oJson.decode('791123.45.')
  16963.         ? oJson.decode('"nacho gtz"')
  16964.         if not empty(oJson.cError)
  16965.                 ? oJson.cError
  16966.                 return
  16967.         endif
  16968.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  16969.         if not empty(oJson.cError)
  16970.                 ? oJson.cError
  16971.                 return
  16972.         endif
  16973.        
  16974.         ? 'Test Array'
  16975.         ? '----------'
  16976.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  16977.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  16978.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  16979.         nombres = arr.get(1)
  16980.         edades  = arr.get(2)
  16981.         ? nombres.get(1), edades.get(1)
  16982.         ? nombres.get(2), edades.get(2)
  16983.         ? nombres.get(3), edades.get(3)
  16984.         ?
  16985.         ? 'Test Object'
  16986.         ? '-----------'
  16987.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  16988.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  16989.         ? obj._Nombre, obj._Edad, obj._IsGood
  16990.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  16991.         ? obj.get('jsonrpc'), obj._jsonrpc
  16992.         ? obj.get('id'), obj._id
  16993.         ? obj.get('method'), obj._method
  16994.         ? obj._Params.array[1], obj._Params.get(1)
  16995.  
  16996.         ?
  16997.         ? 'Test nested object'
  16998.         ? '------------------'
  16999.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  17000.         obj = oJson.decode(cJson)
  17001.         if not empty(oJson.cError)
  17002.                 ? oJson.cError
  17003.                 return
  17004.         endif
  17005.         ? cJson
  17006.         ? 'method -->',obj._method
  17007.         ? 'usrkey -->',obj._params._data._usrkey
  17008.         ? 'sendto -->',obj._params._data._sendto
  17009.         ? 'name  --->',obj._params._data._name
  17010.         ? 'expires ->',obj._params._data._expires
  17011.  
  17012.         ?
  17013.         ? 'Test empty object'
  17014.         ? '-----------------'
  17015.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  17016.         obj = oJson.decode(cJson)
  17017.         if not empty(oJson.cError)     
  17018.                 ? oJson.cError
  17019.                 return
  17020.         endif
  17021.         ? cJson
  17022.         ? 'result -->',obj._result, obj.get('result')
  17023.         oError = obj.get('error')
  17024.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  17025.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  17026.         ? 'id  ----->',obj._id, obj.get('id')
  17027.         ?  type("oError._code")
  17028.  
  17029.         ?
  17030.         ? 'Probar decode-enconde-decode-encode'
  17031.         ? '------------------------------------'
  17032.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  17033.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  17034.         ? cJson
  17035.         oSmtp = json_decode(cJson)
  17036.         cJson =  json_encode(oSmtp)
  17037.         ? cJson
  17038.         oSmtp = json_decode(cJson)
  17039.         cJson =  json_encode(oSmtp)
  17040.         ? cJson
  17041.  
  17042.         * Probar falla
  17043.         ?
  17044.         ? 'Probar una falla en el json'
  17045.         ? '---------------------------'
  17046.         cJson = ' {"server":"", "user":"", "password":"" ,'
  17047.         oSmtp = json_decode(cJson)
  17048.         if not empty(json_getErrorMsg())
  17049.                 ? json_getErrorMsg()
  17050.         endif
  17051.  
  17052.         ?
  17053.         ? 'Pruebas Finalizadas'
  17054. retur
  17055. *       ? oCliente.get('nombre')
  17056. *       ? oCliente.get('apellido')
  17057. *
  17058. *
  17059. * VFPJSON  Encode and Decode JSON for VFP
  17060. * Examples:
  17061. *       oJson = newobject('json','json.prg')
  17062. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  17063. *       ? oJson.encode(oCustomer)
  17064. *       ? oCustomer.get('name')
  17065. *       ? oCustomer.get('lastname')
  17066. *
  17067. *
  17068. lRunTest = .f.
  17069. if lRunTest
  17070.         testJsonClass()
  17071. endif
  17072. return
  17073.  
  17074.  
  17075. function json_encode(xExpr)
  17076.         if vartype(_json)<>'O'
  17077.                 public _json
  17078.                 _json = newobject('json')
  17079.         endif
  17080. return _json.encode(@xExpr)
  17081.  
  17082.  
  17083. function json_decode(cJson)
  17084. local retval
  17085.         if vartype(_json)<>'O'
  17086.                 public _json
  17087.                 _json = newobject('json')
  17088.         endif
  17089.         retval = _json.decode(cJson)
  17090.         if not empty(_json.cError)
  17091.                 return null
  17092.         endif
  17093. return retval
  17094.  
  17095. function json_getErrorMsg()
  17096. return _json.cError
  17097.        
  17098.  
  17099.  
  17100. *
  17101. * json class
  17102. *
  17103. *
  17104. define class json as custom
  17105.  
  17106.  
  17107.         nPos=0
  17108.         nLen=0
  17109.         cJson=''
  17110.         cError=''
  17111.  
  17112.  
  17113.         *
  17114.         * Genera el codigo cJson para parametro que se manda
  17115.         *
  17116.         function encode(xExpr)
  17117.         local cTipo
  17118.                 * Cuando se manda una arreglo,
  17119.                 if type('ALen(xExpr)')=='N'
  17120.                         cTipo = 'A'
  17121.                 Else
  17122.                         cTipo = VarType(xExpr)
  17123.                 Endif
  17124.                
  17125.                 Do Case
  17126.                 Case cTipo=='D'
  17127.                         return '"'+dtos(xExpr)+'"'
  17128.                 Case cTipo=='N'
  17129.                         return Transform(xExpr)
  17130.                 Case cTipo=='L'
  17131.                         return iif(xExpr,'true','false')
  17132.                 Case cTipo=='X'
  17133.                         return 'null'
  17134.                 Case cTipo=='C'
  17135.                         xExpr = allt(xExpr)
  17136.                         xExpr = StrTran(xExpr, '\', '\\' )
  17137.                         xExpr = StrTran(xExpr, '/', '\/' )
  17138.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  17139.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  17140.                         xExpr = StrTran(xExpr, '"', '\"' )
  17141.                         return '"'+xExpr+'"'
  17142.  
  17143.                 case cTipo=='O'
  17144.                         local cProp, cJsonValue, cRetVal, aProp[1]
  17145.                         =AMembers(aProp,xExpr)
  17146.                         cRetVal = ''
  17147.                         for each cProp in aProp
  17148.                                 *? cProp
  17149.                                 *? cRetVal
  17150.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  17151.                                         * algunas propiedades pueden no estar definidas
  17152.                                         * como: activecontrol, parent, etc
  17153.                                         loop
  17154.                                 endif
  17155.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  17156.                                         *
  17157.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  17158.                                         *
  17159.                                         Local i,nTotElem
  17160.                                         cJsonValue = ''
  17161.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  17162.                                         For i=1 to nTotElem
  17163.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  17164.                                         Next
  17165.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  17166.                                 else
  17167.                                         *
  17168.                                         * es otro tipo de dato normal C, N, L
  17169.                                         *
  17170.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  17171.                                 endif
  17172.                                 if left(cProp,1)=='_'
  17173.                                         cProp = substr(cProp,2)
  17174.                                 endif
  17175.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  17176.                         next
  17177.                         return '{' + substr(cRetVal,2) + '}'
  17178.  
  17179.                 case cTipo=='A'
  17180.                         local valor, cRetVal
  17181.                         cRetVal = ''   
  17182.                         for each valor in xExpr
  17183.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  17184.                         next
  17185.                         return  '[' + substr(cRetVal,2) + ']'
  17186.                        
  17187.                 endcase
  17188.  
  17189.         return ''
  17190.  
  17191.  
  17192.  
  17193.  
  17194.  
  17195.         *
  17196.         * regresa un elemento representado por la cadena json que se manda
  17197.         *
  17198.        
  17199.         function decode(cJson)
  17200.         local retValue
  17201.                 cJson = StrTran(cJson,chr(9),'')
  17202.                 cJson = StrTran(cJson,chr(10),'')
  17203.                 cJson = StrTran(cJson,chr(13),'')
  17204.                 cJson = this.fixUnicode(cJson)
  17205.                 this.nPos  = 1
  17206.                 this.cJson = cJson
  17207.                 this.nLen  = len(cJson)
  17208.                 this.cError = ''
  17209.                 retValue = this.parsevalue()
  17210.                 if not empty(this.cError)
  17211.                         return null
  17212.                 endif
  17213.                 if this.getToken()<>null
  17214.                         this.setError('Junk at the end of JSON input')
  17215.                         return null
  17216.                 endif
  17217.         return retValue
  17218.                
  17219.        
  17220.         function parseValue()
  17221.         local token
  17222.                 token = this.getToken()
  17223.                 if token==null
  17224.                         this.setError('Nothing to parse')
  17225.                         return null
  17226.                 endif
  17227.                 do case
  17228.                 case token=='"'
  17229.                         return this.parseString()
  17230.                 case isdigit(token) or token=='-'
  17231.                         return this.parseNumber()
  17232.                 case token=='n'
  17233.                         return this.expectedKeyword('null',null)
  17234.                 case token=='f'
  17235.                         return this.expectedKeyword('false',.f.)
  17236.                 case token=='t'
  17237.                         return this.expectedKeyword('true',.t.)
  17238.                 case token=='{'
  17239.                         return this.parseObject()
  17240.                 case token=='['
  17241.                         return this.parseArray()
  17242.                 otherwise
  17243.                         this.setError('Unexpected token')
  17244.                 endcase
  17245.         return
  17246.                
  17247.        
  17248.         function expectedKeyword(cWord,eValue)
  17249.                 for i=1 to len(cWord)
  17250.                         cChar = this.getChar()
  17251.                         if cChar <> substr(cWord,i,1)
  17252.                                 this.setError("Expected keyword '" + cWord + "'")
  17253.                                 return ''
  17254.                         endif
  17255.                         this.nPos = this.nPos + 1
  17256.                 next
  17257.         return eValue
  17258.        
  17259.  
  17260.         function parseObject()
  17261.         local retval, cPropName, xValue
  17262.                 retval = createObject('myObj')
  17263.                 this.nPos = this.nPos + 1 && Eat {
  17264.                 if this.getToken()<>'}'
  17265.                         do while .t.
  17266.                                 cPropName = this.parseString()
  17267.                                 if not empty(this.cError)
  17268.                                         return null
  17269.                                 endif
  17270.                                 if this.getToken()<>':'
  17271.                                         this.setError("Expected ':' when parsing object")
  17272.                                         return null
  17273.                                 endif
  17274.                                 this.nPos = this.nPos + 1
  17275.                                 xValue = this.parseValue()
  17276.                                 if not empty(this.cError)
  17277.                                         return null
  17278.                                 endif                          
  17279.                                 ** Debug ? cPropName, type('xValue')
  17280.                                 retval.set(cPropName, xValue)
  17281.                                 if this.getToken()<>','
  17282.                                         exit
  17283.                                 endif
  17284.                                 this.nPos = this.nPos + 1
  17285.                         enddo
  17286.                 endif
  17287.                 if this.getToken()<>'}'
  17288.                         this.setError("Expected '}' at the end of object")
  17289.                         return null
  17290.                 endif
  17291.                 this.nPos = this.nPos + 1
  17292.         return retval
  17293.  
  17294.  
  17295.         function parseArray()
  17296.         local retVal, xValue
  17297.                 retval = createObject('MyArray')
  17298.                 this.nPos = this.nPos + 1       && Eat [
  17299.                 if this.getToken() <> ']'
  17300.                         do while .t.
  17301.                                 xValue = this.parseValue()
  17302.                                 if not empty(this.cError)
  17303.                                         return null
  17304.                                 endif
  17305.                                 retval.add( xValue )
  17306.                                 if this.getToken()<>','
  17307.                                         exit
  17308.                                 endif
  17309.                                 this.nPos = this.nPos + 1
  17310.                         enddo
  17311.                         if this.getToken() <> ']'
  17312.                                 this.setError('Expected ] at the end of array')
  17313.                                 return null
  17314.                         endif
  17315.                 endif
  17316.                 this.nPos = this.nPos + 1
  17317.         return retval
  17318.        
  17319.  
  17320.         function parseString()
  17321.         local cRetVal, c
  17322.                 if this.getToken()<>'"'
  17323.                         this.setError('Expected "')
  17324.                         return ''
  17325.                 endif
  17326.                 this.nPos = this.nPos + 1       && Eat "
  17327.                 cRetVal = ''
  17328.                 do while .t.
  17329.                         c = this.getChar()
  17330.                         if c==''
  17331.                                 return ''
  17332.                         endif
  17333.                         if c == '"'
  17334.                                 this.nPos = this.nPos + 1
  17335.                                 exit
  17336.                         endif
  17337.                         if c == '\'
  17338.                                 this.nPos = this.nPos + 1
  17339.                                 if (this.nPos>this.nLen)
  17340.                                         this.setError('\\ at the end of input')
  17341.                                         return ''
  17342.                                 endif
  17343.                                 c = this.getChar()
  17344.                                 if c==''
  17345.                                         return ''
  17346.                                 endif
  17347.                                 do case
  17348.                                 case c=='"'
  17349.                                         c='"'
  17350.                                 case c=='\'
  17351.                                         c='\'
  17352.                                 case c=='/'
  17353.                                         c='/'
  17354.                                 case c=='b'
  17355.                                         c=chr(8)
  17356.                                 case c=='t'
  17357.                                         c=chr(9)
  17358.                                 case c=='n'
  17359.                                         c=chr(10)
  17360.                                 case c=='f'
  17361.                                         c=chr(12)
  17362.                                 case c=='r'
  17363.                                         c=chr(13)
  17364.                                 otherwise
  17365.                                         ******* FALTAN LOS UNICODE
  17366.                                         this.setError('Invalid escape sequence in string literal')
  17367.                                         return ''
  17368.                                 endcase
  17369.                         endif
  17370.                         cRetVal = cRetVal + c
  17371.                         this.nPos = this.nPos + 1
  17372.                 enddo
  17373.         return cRetVal
  17374.                                        
  17375.  
  17376.         **** Pendiente numeros con E
  17377.         function parseNumber()
  17378.         local nStartPos,c, isInt, cNumero
  17379.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  17380.                         this.setError('Expected number literal')
  17381.                         return 0
  17382.                 endif
  17383.                 nStartPos = this.nPos
  17384.                 c = this.getChar()
  17385.                 if c == '-'
  17386.                         c = this.nextChar()
  17387.                 endif
  17388.                 if c == '0'
  17389.                         c = this.nextChar()
  17390.                 else
  17391.                         if isdigit(c)
  17392.                                 c = this.nextChar()
  17393.                                 do while isdigit(c)
  17394.                                         c = this.nextChar()
  17395.                                 enddo
  17396.                         else
  17397.                                 this.setError('Expected digit when parsing number')
  17398.                                 return 0
  17399.                         endif
  17400.                 endif
  17401.                
  17402.                 isInt = .t.
  17403.                 if c=='.'
  17404.                         c = this.nextChar()
  17405.                         if isdigit(c)
  17406.                                 c = this.nextChar()
  17407.                                 isInt = .f.
  17408.                                 do while isDigit(c)
  17409.                                         c = this.nextChar()
  17410.                                 enddo
  17411.                         else
  17412.                                 this.setError('Expected digit following dot comma')
  17413.                                 return 0
  17414.                         endif
  17415.                 endif
  17416.                
  17417.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  17418.         return val(cNumero)
  17419.  
  17420.  
  17421.  
  17422.         function getToken()
  17423.         local char1
  17424.                 do while .t.
  17425.                         if this.nPos > this.nLen
  17426.                                 return null
  17427.                         endif
  17428.                         char1 = substr(this.cJson, this.nPos, 1)
  17429.                         if char1==' '
  17430.                                 this.nPos = this.nPos + 1
  17431.                                 loop
  17432.                         endif
  17433.                         return char1
  17434.                 enddo
  17435.         return
  17436.        
  17437.                
  17438.                
  17439.         function getChar()
  17440.                 if this.nPos > this.nLen
  17441.                         this.setError('Unexpected end of JSON stream')
  17442.                         return ''
  17443.                 endif
  17444.         return substr(this.cJson, this.nPos, 1)
  17445.        
  17446.         function nextChar()
  17447.                 this.nPos = this.nPos + 1
  17448.                 if this.nPos > this.nLen
  17449.                         return ''
  17450.                 endif
  17451.         return substr(this.cJson, this.nPos, 1)
  17452.        
  17453.         function setError(cMsg)
  17454.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  17455.         return
  17456.  
  17457.  
  17458.         function fixUnicode(cStr)
  17459.                 cStr = StrTran(cStr,'\u00e1','á')
  17460.                 cStr = StrTran(cStr,'\u00e9','é')
  17461.                 cStr = StrTran(cStr,'\u00ed','í')
  17462.                 cStr = StrTran(cStr,'\u00f3','ó')
  17463.                 cStr = StrTran(cStr,'\u00fa','ú')
  17464.                 cStr = StrTran(cStr,'\u00c1','Á')
  17465.                 cStr = StrTran(cStr,'\u00c9','É')
  17466.                 cStr = StrTran(cStr,'\u00cd','Í')
  17467.                 cStr = StrTran(cStr,'\u00d3','Ó')
  17468.                 cStr = StrTran(cStr,'\u00da','Ú')
  17469.                 cStr = StrTran(cStr,'\u00f1','ń')
  17470.                 cStr = StrTran(cStr,'\u00d1','Ń')
  17471.         return cStr
  17472.  
  17473.  
  17474.  
  17475. enddefine
  17476.  
  17477.  
  17478.  
  17479.  
  17480.  
  17481. *
  17482. * class used to return an array
  17483. *
  17484. define class myArray as custom
  17485.         nSize = 0
  17486.         dimension array[1]
  17487.  
  17488.         function add(xExpr)
  17489.                 this.nSize = this.nSize + 1
  17490.                 dimension this.array[this.nSize]
  17491.                 this.array[this.nSize] = xExpr
  17492.         return
  17493.  
  17494.         function get(n)
  17495.         return this.array[n]
  17496.  
  17497. enddefine
  17498.  
  17499.  
  17500.  
  17501. *
  17502. * class used to simulate an object
  17503. * all properties are prefixed with 'prop' to permit property names like: error, init
  17504. * that already exists like vfp methods
  17505. *
  17506. define class myObj as custom
  17507. Hidden ;
  17508.         ClassLibrary,Comment, ;
  17509.         BaseClass,ControlCount, ;
  17510.         Controls,Objects,Object,;
  17511.         Height,HelpContextID,Left,Name, ;
  17512.         Parent,ParentClass,Picture, ;
  17513.         Tag,Top,WhatsThisHelpID,Width
  17514.                
  17515.         function set(cPropName, xValue)
  17516.                 cPropName = '_'+cPropName
  17517.                 if type('this.'+cPropName)=='U'
  17518.                         this.addProperty(cPropName,xValue)
  17519.                 else
  17520.                         local cmd
  17521.                         cmd = 'this.'+cPropName+'=xValue'
  17522.                         &cmd
  17523.                 endif
  17524.         return
  17525.        
  17526.         procedure get (cPropName)
  17527.                 cPropName = '_'+cPropName
  17528.                 If type('this.'+cPropName)=='U'
  17529.                         return ''
  17530.                 Else
  17531.                         local cmd
  17532.                         cmd = 'return this.'+cPropName
  17533.                         &cmd
  17534.                 endif
  17535.         return ''
  17536. enddefine
  17537.  
  17538.  
  17539.  
  17540.  
  17541.  
  17542. function testJsonClass
  17543.         clear
  17544.         set decimal to 10
  17545.         oJson = newObject('json')
  17546.        
  17547.        
  17548.         ? 'Test Basic Types'
  17549.         ? '----------------'
  17550.         ? oJson.decode('null')
  17551.         ? oJson.decode('true')
  17552.         ? oJson.decode('false')
  17553.         ?
  17554.         ? oJson.decode('791123')
  17555.         ? oJson.decode('791123.45')
  17556.         ? oJson.decode('791123.45.')
  17557.         ? oJson.decode('"nacho gtz"')
  17558.         if not empty(oJson.cError)
  17559.                 ? oJson.cError
  17560.                 return
  17561.         endif
  17562.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  17563.         if not empty(oJson.cError)
  17564.                 ? oJson.cError
  17565.                 return
  17566.         endif
  17567.        
  17568.         ? 'Test Array'
  17569.         ? '----------'
  17570.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  17571.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  17572.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  17573.         nombres = arr.get(1)
  17574.         edades  = arr.get(2)
  17575.         ? nombres.get(1), edades.get(1)
  17576.         ? nombres.get(2), edades.get(2)
  17577.         ? nombres.get(3), edades.get(3)
  17578.         ?
  17579.         ? 'Test Object'
  17580.         ? '-----------'
  17581.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  17582.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  17583.         ? obj._Nombre, obj._Edad, obj._IsGood
  17584.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  17585.         ? obj.get('jsonrpc'), obj._jsonrpc
  17586.         ? obj.get('id'), obj._id
  17587.         ? obj.get('method'), obj._method
  17588.         ? obj._Params.array[1], obj._Params.get(1)
  17589.  
  17590.         ?
  17591.         ? 'Test nested object'
  17592.         ? '------------------'
  17593.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  17594.         obj = oJson.decode(cJson)
  17595.         if not empty(oJson.cError)
  17596.                 ? oJson.cError
  17597.                 return
  17598.         endif
  17599.         ? cJson
  17600.         ? 'method -->',obj._method
  17601.         ? 'usrkey -->',obj._params._data._usrkey
  17602.         ? 'sendto -->',obj._params._data._sendto
  17603.         ? 'name  --->',obj._params._data._name
  17604.         ? 'expires ->',obj._params._data._expires
  17605.  
  17606.         ?
  17607.         ? 'Test empty object'
  17608.         ? '-----------------'
  17609.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  17610.         obj = oJson.decode(cJson)
  17611.         if not empty(oJson.cError)     
  17612.                 ? oJson.cError
  17613.                 return
  17614.         endif
  17615.         ? cJson
  17616.         ? 'result -->',obj._result, obj.get('result')
  17617.         oError = obj.get('error')
  17618.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  17619.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  17620.         ? 'id  ----->',obj._id, obj.get('id')
  17621.         ?  type("oError._code")
  17622.  
  17623.         ?
  17624.         ? 'Probar decode-enconde-decode-encode'
  17625.         ? '------------------------------------'
  17626.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  17627.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  17628.         ? cJson
  17629.         oSmtp = json_decode(cJson)
  17630.         cJson =  json_encode(oSmtp)
  17631.         ? cJson
  17632.         oSmtp = json_decode(cJson)
  17633.         cJson =  json_encode(oSmtp)
  17634.         ? cJson
  17635.  
  17636.         * Probar falla
  17637.         ?
  17638.         ? 'Probar una falla en el json'
  17639.         ? '---------------------------'
  17640.         cJson = ' {"server":"", "user":"", "password":"" ,'
  17641.         oSmtp = json_decode(cJson)
  17642.         if not empty(json_getErrorMsg())
  17643.                 ? json_getErrorMsg()
  17644.         endif
  17645.  
  17646.         ?
  17647.         ? 'Pruebas Finalizadas'
  17648. retur
  17649. *       ? oCliente.get('apellido')
  17650. *
  17651. *
  17652. * VFPJSON  Encode and Decode JSON for VFP
  17653. * Examples:
  17654. *       oJson = newobject('json','json.prg')
  17655. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  17656. *       ? oJson.encode(oCustomer)
  17657. *       ? oCustomer.get('name')
  17658. *       ? oCustomer.get('lastname')
  17659. *
  17660. *
  17661. lRunTest = .f.
  17662. if lRunTest
  17663.         testJsonClass()
  17664. endif
  17665. return
  17666.  
  17667.  
  17668. function json_encode(xExpr)
  17669.         if vartype(_json)<>'O'
  17670.                 public _json
  17671.                 _json = newobject('json')
  17672.         endif
  17673. return _json.encode(@xExpr)
  17674.  
  17675.  
  17676. function json_decode(cJson)
  17677. local retval
  17678.         if vartype(_json)<>'O'
  17679.                 public _json
  17680.                 _json = newobject('json')
  17681.         endif
  17682.         retval = _json.decode(cJson)
  17683.         if not empty(_json.cError)
  17684.                 return null
  17685.         endif
  17686. return retval
  17687.  
  17688. function json_getErrorMsg()
  17689. return _json.cError
  17690.        
  17691.  
  17692.  
  17693. *
  17694. * json class
  17695. *
  17696. *
  17697. define class json as custom
  17698.  
  17699.  
  17700.         nPos=0
  17701.         nLen=0
  17702.         cJson=''
  17703.         cError=''
  17704.  
  17705.  
  17706.         *
  17707.         * Genera el codigo cJson para parametro que se manda
  17708.         *
  17709.         function encode(xExpr)
  17710.         local cTipo
  17711.                 * Cuando se manda una arreglo,
  17712.                 if type('ALen(xExpr)')=='N'
  17713.                         cTipo = 'A'
  17714.                 Else
  17715.                         cTipo = VarType(xExpr)
  17716.                 Endif
  17717.                
  17718.                 Do Case
  17719.                 Case cTipo=='D'
  17720.                         return '"'+dtos(xExpr)+'"'
  17721.                 Case cTipo=='N'
  17722.                         return Transform(xExpr)
  17723.                 Case cTipo=='L'
  17724.                         return iif(xExpr,'true','false')
  17725.                 Case cTipo=='X'
  17726.                         return 'null'
  17727.                 Case cTipo=='C'
  17728.                         xExpr = allt(xExpr)
  17729.                         xExpr = StrTran(xExpr, '\', '\\' )
  17730.                         xExpr = StrTran(xExpr, '/', '\/' )
  17731.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  17732.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  17733.                         xExpr = StrTran(xExpr, '"', '\"' )
  17734.                         return '"'+xExpr+'"'
  17735.  
  17736.                 case cTipo=='O'
  17737.                         local cProp, cJsonValue, cRetVal, aProp[1]
  17738.                         =AMembers(aProp,xExpr)
  17739.                         cRetVal = ''
  17740.                         for each cProp in aProp
  17741.                                 *? cProp
  17742.                                 *? cRetVal
  17743.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  17744.                                         * algunas propiedades pueden no estar definidas
  17745.                                         * como: activecontrol, parent, etc
  17746.                                         loop
  17747.                                 endif
  17748.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  17749.                                         *
  17750.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  17751.                                         *
  17752.                                         Local i,nTotElem
  17753.                                         cJsonValue = ''
  17754.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  17755.                                         For i=1 to nTotElem
  17756.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  17757.                                         Next
  17758.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  17759.                                 else
  17760.                                         *
  17761.                                         * es otro tipo de dato normal C, N, L
  17762.                                         *
  17763.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  17764.                                 endif
  17765.                                 if left(cProp,1)=='_'
  17766.                                         cProp = substr(cProp,2)
  17767.                                 endif
  17768.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  17769.                         next
  17770.                         return '{' + substr(cRetVal,2) + '}'
  17771.  
  17772.                 case cTipo=='A'
  17773.                         local valor, cRetVal
  17774.                         cRetVal = ''   
  17775.                         for each valor in xExpr
  17776.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  17777.                         next
  17778.                         return  '[' + substr(cRetVal,2) + ']'
  17779.                        
  17780.                 endcase
  17781.  
  17782.         return ''
  17783.  
  17784.  
  17785.  
  17786.  
  17787.  
  17788.         *
  17789.         * regresa un elemento representado por la cadena json que se manda
  17790.         *
  17791.        
  17792.         function decode(cJson)
  17793.         local retValue
  17794.                 cJson = StrTran(cJson,chr(9),'')
  17795.                 cJson = StrTran(cJson,chr(10),'')
  17796.                 cJson = StrTran(cJson,chr(13),'')
  17797.                 cJson = this.fixUnicode(cJson)
  17798.                 this.nPos  = 1
  17799.                 this.cJson = cJson
  17800.                 this.nLen  = len(cJson)
  17801.                 this.cError = ''
  17802.                 retValue = this.parsevalue()
  17803.                 if not empty(this.cError)
  17804.                         return null
  17805.                 endif
  17806.                 if this.getToken()<>null
  17807.                         this.setError('Junk at the end of JSON input')
  17808.                         return null
  17809.                 endif
  17810.         return retValue
  17811.                
  17812.        
  17813.         function parseValue()
  17814.         local token
  17815.                 token = this.getToken()
  17816.                 if token==null
  17817.                         this.setError('Nothing to parse')
  17818.                         return null
  17819.                 endif
  17820.                 do case
  17821.                 case token=='"'
  17822.                         return this.parseString()
  17823.                 case isdigit(token) or token=='-'
  17824.                         return this.parseNumber()
  17825.                 case token=='n'
  17826.                         return this.expectedKeyword('null',null)
  17827.                 case token=='f'
  17828.                         return this.expectedKeyword('false',.f.)
  17829.                 case token=='t'
  17830.                         return this.expectedKeyword('true',.t.)
  17831.                 case token=='{'
  17832.                         return this.parseObject()
  17833.                 case token=='['
  17834.                         return this.parseArray()
  17835.                 otherwise
  17836.                         this.setError('Unexpected token')
  17837.                 endcase
  17838.         return
  17839.                
  17840.        
  17841.         function expectedKeyword(cWord,eValue)
  17842.                 for i=1 to len(cWord)
  17843.                         cChar = this.getChar()
  17844.                         if cChar <> substr(cWord,i,1)
  17845.                                 this.setError("Expected keyword '" + cWord + "'")
  17846.                                 return ''
  17847.                         endif
  17848.                         this.nPos = this.nPos + 1
  17849.                 next
  17850.         return eValue
  17851.        
  17852.  
  17853.         function parseObject()
  17854.         local retval, cPropName, xValue
  17855.                 retval = createObject('myObj')
  17856.                 this.nPos = this.nPos + 1 && Eat {
  17857.                 if this.getToken()<>'}'
  17858.                         do while .t.
  17859.                                 cPropName = this.parseString()
  17860.                                 if not empty(this.cError)
  17861.                                         return null
  17862.                                 endif
  17863.                                 if this.getToken()<>':'
  17864.                                         this.setError("Expected ':' when parsing object")
  17865.                                         return null
  17866.                                 endif
  17867.                                 this.nPos = this.nPos + 1
  17868.                                 xValue = this.parseValue()
  17869.                                 if not empty(this.cError)
  17870.                                         return null
  17871.                                 endif                          
  17872.                                 ** Debug ? cPropName, type('xValue')
  17873.                                 retval.set(cPropName, xValue)
  17874.                                 if this.getToken()<>','
  17875.                                         exit
  17876.                                 endif
  17877.                                 this.nPos = this.nPos + 1
  17878.                         enddo
  17879.                 endif
  17880.                 if this.getToken()<>'}'
  17881.                         this.setError("Expected '}' at the end of object")
  17882.                         return null
  17883.                 endif
  17884.                 this.nPos = this.nPos + 1
  17885.         return retval
  17886.  
  17887.  
  17888.         function parseArray()
  17889.         local retVal, xValue
  17890.                 retval = createObject('MyArray')
  17891.                 this.nPos = this.nPos + 1       && Eat [
  17892.                 if this.getToken() <> ']'
  17893.                         do while .t.
  17894.                                 xValue = this.parseValue()
  17895.                                 if not empty(this.cError)
  17896.                                         return null
  17897.                                 endif
  17898.                                 retval.add( xValue )
  17899.                                 if this.getToken()<>','
  17900.                                         exit
  17901.                                 endif
  17902.                                 this.nPos = this.nPos + 1
  17903.                         enddo
  17904.                         if this.getToken() <> ']'
  17905.                                 this.setError('Expected ] at the end of array')
  17906.                                 return null
  17907.                         endif
  17908.                 endif
  17909.                 this.nPos = this.nPos + 1
  17910.         return retval
  17911.        
  17912.  
  17913.         function parseString()
  17914.         local cRetVal, c
  17915.                 if this.getToken()<>'"'
  17916.                         this.setError('Expected "')
  17917.                         return ''
  17918.                 endif
  17919.                 this.nPos = this.nPos + 1       && Eat "
  17920.                 cRetVal = ''
  17921.                 do while .t.
  17922.                         c = this.getChar()
  17923.                         if c==''
  17924.                                 return ''
  17925.                         endif
  17926.                         if c == '"'
  17927.                                 this.nPos = this.nPos + 1
  17928.                                 exit
  17929.                         endif
  17930.                         if c == '\'
  17931.                                 this.nPos = this.nPos + 1
  17932.                                 if (this.nPos>this.nLen)
  17933.                                         this.setError('\\ at the end of input')
  17934.                                         return ''
  17935.                                 endif
  17936.                                 c = this.getChar()
  17937.                                 if c==''
  17938.                                         return ''
  17939.                                 endif
  17940.                                 do case
  17941.                                 case c=='"'
  17942.                                         c='"'
  17943.                                 case c=='\'
  17944.                                         c='\'
  17945.                                 case c=='/'
  17946.                                         c='/'
  17947.                                 case c=='b'
  17948.                                         c=chr(8)
  17949.                                 case c=='t'
  17950.                                         c=chr(9)
  17951.                                 case c=='n'
  17952.                                         c=chr(10)
  17953.                                 case c=='f'
  17954.                                         c=chr(12)
  17955.                                 case c=='r'
  17956.                                         c=chr(13)
  17957.                                 otherwise
  17958.                                         ******* FALTAN LOS UNICODE
  17959.                                         this.setError('Invalid escape sequence in string literal')
  17960.                                         return ''
  17961.                                 endcase
  17962.                         endif
  17963.                         cRetVal = cRetVal + c
  17964.                         this.nPos = this.nPos + 1
  17965.                 enddo
  17966.         return cRetVal
  17967.                                        
  17968.  
  17969.         **** Pendiente numeros con E
  17970.         function parseNumber()
  17971.         local nStartPos,c, isInt, cNumero
  17972.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  17973.                         this.setError('Expected number literal')
  17974.                         return 0
  17975.                 endif
  17976.                 nStartPos = this.nPos
  17977.                 c = this.getChar()
  17978.                 if c == '-'
  17979.                         c = this.nextChar()
  17980.                 endif
  17981.                 if c == '0'
  17982.                         c = this.nextChar()
  17983.                 else
  17984.                         if isdigit(c)
  17985.                                 c = this.nextChar()
  17986.                                 do while isdigit(c)
  17987.                                         c = this.nextChar()
  17988.                                 enddo
  17989.                         else
  17990.                                 this.setError('Expected digit when parsing number')
  17991.                                 return 0
  17992.                         endif
  17993.                 endif
  17994.                
  17995.                 isInt = .t.
  17996.                 if c=='.'
  17997.                         c = this.nextChar()
  17998.                         if isdigit(c)
  17999.                                 c = this.nextChar()
  18000.                                 isInt = .f.
  18001.                                 do while isDigit(c)
  18002.                                         c = this.nextChar()
  18003.                                 enddo
  18004.                         else
  18005.                                 this.setError('Expected digit following dot comma')
  18006.                                 return 0
  18007.                         endif
  18008.                 endif
  18009.                
  18010.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  18011.         return val(cNumero)
  18012.  
  18013.  
  18014.  
  18015.         function getToken()
  18016.         local char1
  18017.                 do while .t.
  18018.                         if this.nPos > this.nLen
  18019.                                 return null
  18020.                         endif
  18021.                         char1 = substr(this.cJson, this.nPos, 1)
  18022.                         if char1==' '
  18023.                                 this.nPos = this.nPos + 1
  18024.                                 loop
  18025.                         endif
  18026.                         return char1
  18027.                 enddo
  18028.         return
  18029.        
  18030.                
  18031.                
  18032.         function getChar()
  18033.                 if this.nPos > this.nLen
  18034.                         this.setError('Unexpected end of JSON stream')
  18035.                         return ''
  18036.                 endif
  18037.         return substr(this.cJson, this.nPos, 1)
  18038.        
  18039.         function nextChar()
  18040.                 this.nPos = this.nPos + 1
  18041.                 if this.nPos > this.nLen
  18042.                         return ''
  18043.                 endif
  18044.         return substr(this.cJson, this.nPos, 1)
  18045.        
  18046.         function setError(cMsg)
  18047.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  18048.         return
  18049.  
  18050.  
  18051.         function fixUnicode(cStr)
  18052.                 cStr = StrTran(cStr,'\u00e1','á')
  18053.                 cStr = StrTran(cStr,'\u00e9','é')
  18054.                 cStr = StrTran(cStr,'\u00ed','í')
  18055.                 cStr = StrTran(cStr,'\u00f3','ó')
  18056.                 cStr = StrTran(cStr,'\u00fa','ú')
  18057.                 cStr = StrTran(cStr,'\u00c1','Á')
  18058.                 cStr = StrTran(cStr,'\u00c9','É')
  18059.                 cStr = StrTran(cStr,'\u00cd','Í')
  18060.                 cStr = StrTran(cStr,'\u00d3','Ó')
  18061.                 cStr = StrTran(cStr,'\u00da','Ú')
  18062.                 cStr = StrTran(cStr,'\u00f1','ń')
  18063.                 cStr = StrTran(cStr,'\u00d1','Ń')
  18064.         return cStr
  18065.  
  18066.  
  18067.  
  18068. enddefine
  18069.  
  18070.  
  18071.  
  18072.  
  18073.  
  18074. *
  18075. * class used to return an array
  18076. *
  18077. define class myArray as custom
  18078.         nSize = 0
  18079.         dimension array[1]
  18080.  
  18081.         function add(xExpr)
  18082.                 this.nSize = this.nSize + 1
  18083.                 dimension this.array[this.nSize]
  18084.                 this.array[this.nSize] = xExpr
  18085.         return
  18086.  
  18087.         function get(n)
  18088.         return this.array[n]
  18089.  
  18090. enddefine
  18091.  
  18092.  
  18093.  
  18094. *
  18095. * class used to simulate an object
  18096. * all properties are prefixed with 'prop' to permit property names like: error, init
  18097. * that already exists like vfp methods
  18098. *
  18099. define class myObj as custom
  18100. Hidden ;
  18101.         ClassLibrary,Comment, ;
  18102.         BaseClass,ControlCount, ;
  18103.         Controls,Objects,Object,;
  18104.         Height,HelpContextID,Left,Name, ;
  18105.         Parent,ParentClass,Picture, ;
  18106.         Tag,Top,WhatsThisHelpID,Width
  18107.                
  18108.         function set(cPropName, xValue)
  18109.                 cPropName = '_'+cPropName
  18110.                 if type('this.'+cPropName)=='U'
  18111.                         this.addProperty(cPropName,xValue)
  18112.                 else
  18113.                         local cmd
  18114.                         cmd = 'this.'+cPropName+'=xValue'
  18115.                         &cmd
  18116.                 endif
  18117.         return
  18118.        
  18119.         procedure get (cPropName)
  18120.                 cPropName = '_'+cPropName
  18121.                 If type('this.'+cPropName)=='U'
  18122.                         return ''
  18123.                 Else
  18124.                         local cmd
  18125.                         cmd = 'return this.'+cPropName
  18126.                         &cmd
  18127.                 endif
  18128.         return ''
  18129. enddefine
  18130.  
  18131.  
  18132.  
  18133.  
  18134.  
  18135. function testJsonClass
  18136.         clear
  18137.         set decimal to 10
  18138.         oJson = newObject('json')
  18139.        
  18140.        
  18141.         ? 'Test Basic Types'
  18142.         ? '----------------'
  18143.         ? oJson.decode('null')
  18144.         ? oJson.decode('true')
  18145.         ? oJson.decode('false')
  18146.         ?
  18147.         ? oJson.decode('791123')
  18148.         ? oJson.decode('791123.45')
  18149.         ? oJson.decode('791123.45.')
  18150.         ? oJson.decode('"nacho gtz"')
  18151.         if not empty(oJson.cError)
  18152.                 ? oJson.cError
  18153.                 return
  18154.         endif
  18155.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  18156.         if not empty(oJson.cError)
  18157.                 ? oJson.cError
  18158.                 return
  18159.         endif
  18160.        
  18161.         ? 'Test Array'
  18162.         ? '----------'
  18163.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  18164.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  18165.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  18166.         nombres = arr.get(1)
  18167.         edades  = arr.get(2)
  18168.         ? nombres.get(1), edades.get(1)
  18169.         ? nombres.get(2), edades.get(2)
  18170.         ? nombres.get(3), edades.get(3)
  18171.         ?
  18172.         ? 'Test Object'
  18173.         ? '-----------'
  18174.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  18175.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  18176.         ? obj._Nombre, obj._Edad, obj._IsGood
  18177.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  18178.         ? obj.get('jsonrpc'), obj._jsonrpc
  18179.         ? obj.get('id'), obj._id
  18180.         ? obj.get('method'), obj._method
  18181.         ? obj._Params.array[1], obj._Params.get(1)
  18182.  
  18183.         ?
  18184.         ? 'Test nested object'
  18185.         ? '------------------'
  18186.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  18187.         obj = oJson.decode(cJson)
  18188.         if not empty(oJson.cError)
  18189.                 ? oJson.cError
  18190.                 return
  18191.         endif
  18192.         ? cJson
  18193.         ? 'method -->',obj._method
  18194.         ? 'usrkey -->',obj._params._data._usrkey
  18195.         ? 'sendto -->',obj._params._data._sendto
  18196.         ? 'name  --->',obj._params._data._name
  18197.         ? 'expires ->',obj._params._data._expires
  18198.  
  18199.         ?
  18200.         ? 'Test empty object'
  18201.         ? '-----------------'
  18202.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  18203.         obj = oJson.decode(cJson)
  18204.         if not empty(oJson.cError)     
  18205.                 ? oJson.cError
  18206.                 return
  18207.         endif
  18208.         ? cJson
  18209.         ? 'result -->',obj._result, obj.get('result')
  18210.         oError = obj.get('error')
  18211.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  18212.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  18213.         ? 'id  ----->',obj._id, obj.get('id')
  18214.         ?  type("oError._code")
  18215.  
  18216.         ?
  18217.         ? 'Probar decode-enconde-decode-encode'
  18218.         ? '------------------------------------'
  18219.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  18220.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  18221.         ? cJson
  18222.         oSmtp = json_decode(cJson)
  18223.         cJson =  json_encode(oSmtp)
  18224.         ? cJson
  18225.         oSmtp = json_decode(cJson)
  18226.         cJson =  json_encode(oSmtp)
  18227.         ? cJson
  18228.  
  18229.         * Probar falla
  18230.         ?
  18231.         ? 'Probar una falla en el json'
  18232.         ? '---------------------------'
  18233.         cJson = ' {"server":"", "user":"", "password":"" ,'
  18234.         oSmtp = json_decode(cJson)
  18235.         if not empty(json_getErrorMsg())
  18236.                 ? json_getErrorMsg()
  18237.         endif
  18238.  
  18239.         ?
  18240.         ? 'Pruebas Finalizadas'
  18241. retur
  18242. *
  18243. *
  18244. * VFPJSON  Encode and Decode JSON for VFP
  18245. * Examples:
  18246. *       oJson = newobject('json','json.prg')
  18247. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  18248. *       ? oJson.encode(oCustomer)
  18249. *       ? oCustomer.get('name')
  18250. *       ? oCustomer.get('lastname')
  18251. *
  18252. *
  18253. lRunTest = .f.
  18254. if lRunTest
  18255.         testJsonClass()
  18256. endif
  18257. return
  18258.  
  18259.  
  18260. function json_encode(xExpr)
  18261.         if vartype(_json)<>'O'
  18262.                 public _json
  18263.                 _json = newobject('json')
  18264.         endif
  18265. return _json.encode(@xExpr)
  18266.  
  18267.  
  18268. function json_decode(cJson)
  18269. local retval
  18270.         if vartype(_json)<>'O'
  18271.                 public _json
  18272.                 _json = newobject('json')
  18273.         endif
  18274.         retval = _json.decode(cJson)
  18275.         if not empty(_json.cError)
  18276.                 return null
  18277.         endif
  18278. return retval
  18279.  
  18280. function json_getErrorMsg()
  18281. return _json.cError
  18282.        
  18283.  
  18284.  
  18285. *
  18286. * json class
  18287. *
  18288. *
  18289. define class json as custom
  18290.  
  18291.  
  18292.         nPos=0
  18293.         nLen=0
  18294.         cJson=''
  18295.         cError=''
  18296.  
  18297.  
  18298.         *
  18299.         * Genera el codigo cJson para parametro que se manda
  18300.         *
  18301.         function encode(xExpr)
  18302.         local cTipo
  18303.                 * Cuando se manda una arreglo,
  18304.                 if type('ALen(xExpr)')=='N'
  18305.                         cTipo = 'A'
  18306.                 Else
  18307.                         cTipo = VarType(xExpr)
  18308.                 Endif
  18309.                
  18310.                 Do Case
  18311.                 Case cTipo=='D'
  18312.                         return '"'+dtos(xExpr)+'"'
  18313.                 Case cTipo=='N'
  18314.                         return Transform(xExpr)
  18315.                 Case cTipo=='L'
  18316.                         return iif(xExpr,'true','false')
  18317.                 Case cTipo=='X'
  18318.                         return 'null'
  18319.                 Case cTipo=='C'
  18320.                         xExpr = allt(xExpr)
  18321.                         xExpr = StrTran(xExpr, '\', '\\' )
  18322.                         xExpr = StrTran(xExpr, '/', '\/' )
  18323.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  18324.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  18325.                         xExpr = StrTran(xExpr, '"', '\"' )
  18326.                         return '"'+xExpr+'"'
  18327.  
  18328.                 case cTipo=='O'
  18329.                         local cProp, cJsonValue, cRetVal, aProp[1]
  18330.                         =AMembers(aProp,xExpr)
  18331.                         cRetVal = ''
  18332.                         for each cProp in aProp
  18333.                                 *? cProp
  18334.                                 *? cRetVal
  18335.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  18336.                                         * algunas propiedades pueden no estar definidas
  18337.                                         * como: activecontrol, parent, etc
  18338.                                         loop
  18339.                                 endif
  18340.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  18341.                                         *
  18342.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  18343.                                         *
  18344.                                         Local i,nTotElem
  18345.                                         cJsonValue = ''
  18346.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  18347.                                         For i=1 to nTotElem
  18348.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  18349.                                         Next
  18350.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  18351.                                 else
  18352.                                         *
  18353.                                         * es otro tipo de dato normal C, N, L
  18354.                                         *
  18355.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  18356.                                 endif
  18357.                                 if left(cProp,1)=='_'
  18358.                                         cProp = substr(cProp,2)
  18359.                                 endif
  18360.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  18361.                         next
  18362.                         return '{' + substr(cRetVal,2) + '}'
  18363.  
  18364.                 case cTipo=='A'
  18365.                         local valor, cRetVal
  18366.                         cRetVal = ''   
  18367.                         for each valor in xExpr
  18368.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  18369.                         next
  18370.                         return  '[' + substr(cRetVal,2) + ']'
  18371.                        
  18372.                 endcase
  18373.  
  18374.         return ''
  18375.  
  18376.  
  18377.  
  18378.  
  18379.  
  18380.         *
  18381.         * regresa un elemento representado por la cadena json que se manda
  18382.         *
  18383.        
  18384.         function decode(cJson)
  18385.         local retValue
  18386.                 cJson = StrTran(cJson,chr(9),'')
  18387.                 cJson = StrTran(cJson,chr(10),'')
  18388.                 cJson = StrTran(cJson,chr(13),'')
  18389.                 cJson = this.fixUnicode(cJson)
  18390.                 this.nPos  = 1
  18391.                 this.cJson = cJson
  18392.                 this.nLen  = len(cJson)
  18393.                 this.cError = ''
  18394.                 retValue = this.parsevalue()
  18395.                 if not empty(this.cError)
  18396.                         return null
  18397.                 endif
  18398.                 if this.getToken()<>null
  18399.                         this.setError('Junk at the end of JSON input')
  18400.                         return null
  18401.                 endif
  18402.         return retValue
  18403.                
  18404.        
  18405.         function parseValue()
  18406.         local token
  18407.                 token = this.getToken()
  18408.                 if token==null
  18409.                         this.setError('Nothing to parse')
  18410.                         return null
  18411.                 endif
  18412.                 do case
  18413.                 case token=='"'
  18414.                         return this.parseString()
  18415.                 case isdigit(token) or token=='-'
  18416.                         return this.parseNumber()
  18417.                 case token=='n'
  18418.                         return this.expectedKeyword('null',null)
  18419.                 case token=='f'
  18420.                         return this.expectedKeyword('false',.f.)
  18421.                 case token=='t'
  18422.                         return this.expectedKeyword('true',.t.)
  18423.                 case token=='{'
  18424.                         return this.parseObject()
  18425.                 case token=='['
  18426.                         return this.parseArray()
  18427.                 otherwise
  18428.                         this.setError('Unexpected token')
  18429.                 endcase
  18430.         return
  18431.                
  18432.        
  18433.         function expectedKeyword(cWord,eValue)
  18434.                 for i=1 to len(cWord)
  18435.                         cChar = this.getChar()
  18436.                         if cChar <> substr(cWord,i,1)
  18437.                                 this.setError("Expected keyword '" + cWord + "'")
  18438.                                 return ''
  18439.                         endif
  18440.                         this.nPos = this.nPos + 1
  18441.                 next
  18442.         return eValue
  18443.        
  18444.  
  18445.         function parseObject()
  18446.         local retval, cPropName, xValue
  18447.                 retval = createObject('myObj')
  18448.                 this.nPos = this.nPos + 1 && Eat {
  18449.                 if this.getToken()<>'}'
  18450.                         do while .t.
  18451.                                 cPropName = this.parseString()
  18452.                                 if not empty(this.cError)
  18453.                                         return null
  18454.                                 endif
  18455.                                 if this.getToken()<>':'
  18456.                                         this.setError("Expected ':' when parsing object")
  18457.                                         return null
  18458.                                 endif
  18459.                                 this.nPos = this.nPos + 1
  18460.                                 xValue = this.parseValue()
  18461.                                 if not empty(this.cError)
  18462.                                         return null
  18463.                                 endif                          
  18464.                                 ** Debug ? cPropName, type('xValue')
  18465.                                 retval.set(cPropName, xValue)
  18466.                                 if this.getToken()<>','
  18467.                                         exit
  18468.                                 endif
  18469.                                 this.nPos = this.nPos + 1
  18470.                         enddo
  18471.                 endif
  18472.                 if this.getToken()<>'}'
  18473.                         this.setError("Expected '}' at the end of object")
  18474.                         return null
  18475.                 endif
  18476.                 this.nPos = this.nPos + 1
  18477.         return retval
  18478.  
  18479.  
  18480.         function parseArray()
  18481.         local retVal, xValue
  18482.                 retval = createObject('MyArray')
  18483.                 this.nPos = this.nPos + 1       && Eat [
  18484.                 if this.getToken() <> ']'
  18485.                         do while .t.
  18486.                                 xValue = this.parseValue()
  18487.                                 if not empty(this.cError)
  18488.                                         return null
  18489.                                 endif
  18490.                                 retval.add( xValue )
  18491.                                 if this.getToken()<>','
  18492.                                         exit
  18493.                                 endif
  18494.                                 this.nPos = this.nPos + 1
  18495.                         enddo
  18496.                         if this.getToken() <> ']'
  18497.                                 this.setError('Expected ] at the end of array')
  18498.                                 return null
  18499.                         endif
  18500.                 endif
  18501.                 this.nPos = this.nPos + 1
  18502.         return retval
  18503.        
  18504.  
  18505.         function parseString()
  18506.         local cRetVal, c
  18507.                 if this.getToken()<>'"'
  18508.                         this.setError('Expected "')
  18509.                         return ''
  18510.                 endif
  18511.                 this.nPos = this.nPos + 1       && Eat "
  18512.                 cRetVal = ''
  18513.                 do while .t.
  18514.                         c = this.getChar()
  18515.                         if c==''
  18516.                                 return ''
  18517.                         endif
  18518.                         if c == '"'
  18519.                                 this.nPos = this.nPos + 1
  18520.                                 exit
  18521.                         endif
  18522.                         if c == '\'
  18523.                                 this.nPos = this.nPos + 1
  18524.                                 if (this.nPos>this.nLen)
  18525.                                         this.setError('\\ at the end of input')
  18526.                                         return ''
  18527.                                 endif
  18528.                                 c = this.getChar()
  18529.                                 if c==''
  18530.                                         return ''
  18531.                                 endif
  18532.                                 do case
  18533.                                 case c=='"'
  18534.                                         c='"'
  18535.                                 case c=='\'
  18536.                                         c='\'
  18537.                                 case c=='/'
  18538.                                         c='/'
  18539.                                 case c=='b'
  18540.                                         c=chr(8)
  18541.                                 case c=='t'
  18542.                                         c=chr(9)
  18543.                                 case c=='n'
  18544.                                         c=chr(10)
  18545.                                 case c=='f'
  18546.                                         c=chr(12)
  18547.                                 case c=='r'
  18548.                                         c=chr(13)
  18549.                                 otherwise
  18550.                                         ******* FALTAN LOS UNICODE
  18551.                                         this.setError('Invalid escape sequence in string literal')
  18552.                                         return ''
  18553.                                 endcase
  18554.                         endif
  18555.                         cRetVal = cRetVal + c
  18556.                         this.nPos = this.nPos + 1
  18557.                 enddo
  18558.         return cRetVal
  18559.                                        
  18560.  
  18561.         **** Pendiente numeros con E
  18562.         function parseNumber()
  18563.         local nStartPos,c, isInt, cNumero
  18564.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  18565.                         this.setError('Expected number literal')
  18566.                         return 0
  18567.                 endif
  18568.                 nStartPos = this.nPos
  18569.                 c = this.getChar()
  18570.                 if c == '-'
  18571.                         c = this.nextChar()
  18572.                 endif
  18573.                 if c == '0'
  18574.                         c = this.nextChar()
  18575.                 else
  18576.                         if isdigit(c)
  18577.                                 c = this.nextChar()
  18578.                                 do while isdigit(c)
  18579.                                         c = this.nextChar()
  18580.                                 enddo
  18581.                         else
  18582.                                 this.setError('Expected digit when parsing number')
  18583.                                 return 0
  18584.                         endif
  18585.                 endif
  18586.                
  18587.                 isInt = .t.
  18588.                 if c=='.'
  18589.                         c = this.nextChar()
  18590.                         if isdigit(c)
  18591.                                 c = this.nextChar()
  18592.                                 isInt = .f.
  18593.                                 do while isDigit(c)
  18594.                                         c = this.nextChar()
  18595.                                 enddo
  18596.                         else
  18597.                                 this.setError('Expected digit following dot comma')
  18598.                                 return 0
  18599.                         endif
  18600.                 endif
  18601.                
  18602.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  18603.         return val(cNumero)
  18604.  
  18605.  
  18606.  
  18607.         function getToken()
  18608.         local char1
  18609.                 do while .t.
  18610.                         if this.nPos > this.nLen
  18611.                                 return null
  18612.                         endif
  18613.                         char1 = substr(this.cJson, this.nPos, 1)
  18614.                         if char1==' '
  18615.                                 this.nPos = this.nPos + 1
  18616.                                 loop
  18617.                         endif
  18618.                         return char1
  18619.                 enddo
  18620.         return
  18621.        
  18622.                
  18623.                
  18624.         function getChar()
  18625.                 if this.nPos > this.nLen
  18626.                         this.setError('Unexpected end of JSON stream')
  18627.                         return ''
  18628.                 endif
  18629.         return substr(this.cJson, this.nPos, 1)
  18630.        
  18631.         function nextChar()
  18632.                 this.nPos = this.nPos + 1
  18633.                 if this.nPos > this.nLen
  18634.                         return ''
  18635.                 endif
  18636.         return substr(this.cJson, this.nPos, 1)
  18637.        
  18638.         function setError(cMsg)
  18639.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  18640.         return
  18641.  
  18642.  
  18643.         function fixUnicode(cStr)
  18644.                 cStr = StrTran(cStr,'\u00e1','á')
  18645.                 cStr = StrTran(cStr,'\u00e9','é')
  18646.                 cStr = StrTran(cStr,'\u00ed','í')
  18647.                 cStr = StrTran(cStr,'\u00f3','ó')
  18648.                 cStr = StrTran(cStr,'\u00fa','ú')
  18649.                 cStr = StrTran(cStr,'\u00c1','Á')
  18650.                 cStr = StrTran(cStr,'\u00c9','É')
  18651.                 cStr = StrTran(cStr,'\u00cd','Í')
  18652.                 cStr = StrTran(cStr,'\u00d3','Ó')
  18653.                 cStr = StrTran(cStr,'\u00da','Ú')
  18654.                 cStr = StrTran(cStr,'\u00f1','ń')
  18655.                 cStr = StrTran(cStr,'\u00d1','Ń')
  18656.         return cStr
  18657.  
  18658.  
  18659.  
  18660. enddefine
  18661.  
  18662.  
  18663.  
  18664.  
  18665.  
  18666. *
  18667. * class used to return an array
  18668. *
  18669. define class myArray as custom
  18670.         nSize = 0
  18671.         dimension array[1]
  18672.  
  18673.         function add(xExpr)
  18674.                 this.nSize = this.nSize + 1
  18675.                 dimension this.array[this.nSize]
  18676.                 this.array[this.nSize] = xExpr
  18677.         return
  18678.  
  18679.         function get(n)
  18680.         return this.array[n]
  18681.  
  18682. enddefine
  18683.  
  18684.  
  18685.  
  18686. *
  18687. * class used to simulate an object
  18688. * all properties are prefixed with 'prop' to permit property names like: error, init
  18689. * that already exists like vfp methods
  18690. *
  18691. define class myObj as custom
  18692. Hidden ;
  18693.         ClassLibrary,Comment, ;
  18694.         BaseClass,ControlCount, ;
  18695.         Controls,Objects,Object,;
  18696.         Height,HelpContextID,Left,Name, ;
  18697.         Parent,ParentClass,Picture, ;
  18698.         Tag,Top,WhatsThisHelpID,Width
  18699.                
  18700.         function set(cPropName, xValue)
  18701.                 cPropName = '_'+cPropName
  18702.                 if type('this.'+cPropName)=='U'
  18703.                         this.addProperty(cPropName,xValue)
  18704.                 else
  18705.                         local cmd
  18706.                         cmd = 'this.'+cPropName+'=xValue'
  18707.                         &cmd
  18708.                 endif
  18709.         return
  18710.        
  18711.         procedure get (cPropName)
  18712.                 cPropName = '_'+cPropName
  18713.                 If type('this.'+cPropName)=='U'
  18714.                         return ''
  18715.                 Else
  18716.                         local cmd
  18717.                         cmd = 'return this.'+cPropName
  18718.                         &cmd
  18719.                 endif
  18720.         return ''
  18721. enddefine
  18722.  
  18723.  
  18724.  
  18725.  
  18726.  
  18727. function testJsonClass
  18728.         clear
  18729.         set decimal to 10
  18730.         oJson = newObject('json')
  18731.        
  18732.        
  18733.         ? 'Test Basic Types'
  18734.         ? '----------------'
  18735.         ? oJson.decode('null')
  18736.         ? oJson.decode('true')
  18737.         ? oJson.decode('false')
  18738.         ?
  18739.         ? oJson.decode('791123')
  18740.         ? oJson.decode('791123.45')
  18741.         ? oJson.decode('791123.45.')
  18742.         ? oJson.decode('"nacho gtz"')
  18743.         if not empty(oJson.cError)
  18744.                 ? oJson.cError
  18745.                 return
  18746.         endif
  18747.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  18748.         if not empty(oJson.cError)
  18749.                 ? oJson.cError
  18750.                 return
  18751.         endif
  18752.        
  18753.         ? 'Test Array'
  18754.         ? '----------'
  18755.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  18756.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  18757.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  18758.         nombres = arr.get(1)
  18759.         edades  = arr.get(2)
  18760.         ? nombres.get(1), edades.get(1)
  18761.         ? nombres.get(2), edades.get(2)
  18762.         ? nombres.get(3), edades.get(3)
  18763.         ?
  18764.         ? 'Test Object'
  18765.         ? '-----------'
  18766.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  18767.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  18768.         ? obj._Nombre, obj._Edad, obj._IsGood
  18769.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  18770.         ? obj.get('jsonrpc'), obj._jsonrpc
  18771.         ? obj.get('id'), obj._id
  18772.         ? obj.get('method'), obj._method
  18773.         ? obj._Params.array[1], obj._Params.get(1)
  18774.  
  18775.         ?
  18776.         ? 'Test nested object'
  18777.         ? '------------------'
  18778.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  18779.         obj = oJson.decode(cJson)
  18780.         if not empty(oJson.cError)
  18781.                 ? oJson.cError
  18782.                 return
  18783.         endif
  18784.         ? cJson
  18785.         ? 'method -->',obj._method
  18786.         ? 'usrkey -->',obj._params._data._usrkey
  18787.         ? 'sendto -->',obj._params._data._sendto
  18788.         ? 'name  --->',obj._params._data._name
  18789.         ? 'expires ->',obj._params._data._expires
  18790.  
  18791.         ?
  18792.         ? 'Test empty object'
  18793.         ? '-----------------'
  18794.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  18795.         obj = oJson.decode(cJson)
  18796.         if not empty(oJson.cError)     
  18797.                 ? oJson.cError
  18798.                 return
  18799.         endif
  18800.         ? cJson
  18801.         ? 'result -->',obj._result, obj.get('result')
  18802.         oError = obj.get('error')
  18803.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  18804.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  18805.         ? 'id  ----->',obj._id, obj.get('id')
  18806.         ?  type("oError._code")
  18807.  
  18808.         ?
  18809.         ? 'Probar decode-enconde-decode-encode'
  18810.         ? '------------------------------------'
  18811.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  18812.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  18813.         ? cJson
  18814.         oSmtp = json_decode(cJson)
  18815.         cJson =  json_encode(oSmtp)
  18816.         ? cJson
  18817.         oSmtp = json_decode(cJson)
  18818.         cJson =  json_encode(oSmtp)
  18819.         ? cJson
  18820.  
  18821.         * Probar falla
  18822.         ?
  18823.         ? 'Probar una falla en el json'
  18824.         ? '---------------------------'
  18825.         cJson = ' {"server":"", "user":"", "password":"" ,'
  18826.         oSmtp = json_decode(cJson)
  18827.         if not empty(json_getErrorMsg())
  18828.                 ? json_getErrorMsg()
  18829.         endif
  18830.  
  18831.         ?
  18832.         ? 'Pruebas Finalizadas'
  18833. retur
  18834. *
  18835. * VFPJSON  Encode and Decode JSON for VFP
  18836. * Examples:
  18837. *       oJson = newobject('json','json.prg')
  18838. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  18839. *       ? oJson.encode(oCustomer)
  18840. *       ? oCustomer.get('name')
  18841. *       ? oCustomer.get('lastname')
  18842. *
  18843. *
  18844. lRunTest = .f.
  18845. if lRunTest
  18846.         testJsonClass()
  18847. endif
  18848. return
  18849.  
  18850.  
  18851. function json_encode(xExpr)
  18852.         if vartype(_json)<>'O'
  18853.                 public _json
  18854.                 _json = newobject('json')
  18855.         endif
  18856. return _json.encode(@xExpr)
  18857.  
  18858.  
  18859. function json_decode(cJson)
  18860. local retval
  18861.         if vartype(_json)<>'O'
  18862.                 public _json
  18863.                 _json = newobject('json')
  18864.         endif
  18865.         retval = _json.decode(cJson)
  18866.         if not empty(_json.cError)
  18867.                 return null
  18868.         endif
  18869. return retval
  18870.  
  18871. function json_getErrorMsg()
  18872. return _json.cError
  18873.        
  18874.  
  18875.  
  18876. *
  18877. * json class
  18878. *
  18879. *
  18880. define class json as custom
  18881.  
  18882.  
  18883.         nPos=0
  18884.         nLen=0
  18885.         cJson=''
  18886.         cError=''
  18887.  
  18888.  
  18889.         *
  18890.         * Genera el codigo cJson para parametro que se manda
  18891.         *
  18892.         function encode(xExpr)
  18893.         local cTipo
  18894.                 * Cuando se manda una arreglo,
  18895.                 if type('ALen(xExpr)')=='N'
  18896.                         cTipo = 'A'
  18897.                 Else
  18898.                         cTipo = VarType(xExpr)
  18899.                 Endif
  18900.                
  18901.                 Do Case
  18902.                 Case cTipo=='D'
  18903.                         return '"'+dtos(xExpr)+'"'
  18904.                 Case cTipo=='N'
  18905.                         return Transform(xExpr)
  18906.                 Case cTipo=='L'
  18907.                         return iif(xExpr,'true','false')
  18908.                 Case cTipo=='X'
  18909.                         return 'null'
  18910.                 Case cTipo=='C'
  18911.                         xExpr = allt(xExpr)
  18912.                         xExpr = StrTran(xExpr, '\', '\\' )
  18913.                         xExpr = StrTran(xExpr, '/', '\/' )
  18914.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  18915.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  18916.                         xExpr = StrTran(xExpr, '"', '\"' )
  18917.                         return '"'+xExpr+'"'
  18918.  
  18919.                 case cTipo=='O'
  18920.                         local cProp, cJsonValue, cRetVal, aProp[1]
  18921.                         =AMembers(aProp,xExpr)
  18922.                         cRetVal = ''
  18923.                         for each cProp in aProp
  18924.                                 *? cProp
  18925.                                 *? cRetVal
  18926.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  18927.                                         * algunas propiedades pueden no estar definidas
  18928.                                         * como: activecontrol, parent, etc
  18929.                                         loop
  18930.                                 endif
  18931.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  18932.                                         *
  18933.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  18934.                                         *
  18935.                                         Local i,nTotElem
  18936.                                         cJsonValue = ''
  18937.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  18938.                                         For i=1 to nTotElem
  18939.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  18940.                                         Next
  18941.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  18942.                                 else
  18943.                                         *
  18944.                                         * es otro tipo de dato normal C, N, L
  18945.                                         *
  18946.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  18947.                                 endif
  18948.                                 if left(cProp,1)=='_'
  18949.                                         cProp = substr(cProp,2)
  18950.                                 endif
  18951.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  18952.                         next
  18953.                         return '{' + substr(cRetVal,2) + '}'
  18954.  
  18955.                 case cTipo=='A'
  18956.                         local valor, cRetVal
  18957.                         cRetVal = ''   
  18958.                         for each valor in xExpr
  18959.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  18960.                         next
  18961.                         return  '[' + substr(cRetVal,2) + ']'
  18962.                        
  18963.                 endcase
  18964.  
  18965.         return ''
  18966.  
  18967.  
  18968.  
  18969.  
  18970.  
  18971.         *
  18972.         * regresa un elemento representado por la cadena json que se manda
  18973.         *
  18974.        
  18975.         function decode(cJson)
  18976.         local retValue
  18977.                 cJson = StrTran(cJson,chr(9),'')
  18978.                 cJson = StrTran(cJson,chr(10),'')
  18979.                 cJson = StrTran(cJson,chr(13),'')
  18980.                 cJson = this.fixUnicode(cJson)
  18981.                 this.nPos  = 1
  18982.                 this.cJson = cJson
  18983.                 this.nLen  = len(cJson)
  18984.                 this.cError = ''
  18985.                 retValue = this.parsevalue()
  18986.                 if not empty(this.cError)
  18987.                         return null
  18988.                 endif
  18989.                 if this.getToken()<>null
  18990.                         this.setError('Junk at the end of JSON input')
  18991.                         return null
  18992.                 endif
  18993.         return retValue
  18994.                
  18995.        
  18996.         function parseValue()
  18997.         local token
  18998.                 token = this.getToken()
  18999.                 if token==null
  19000.                         this.setError('Nothing to parse')
  19001.                         return null
  19002.                 endif
  19003.                 do case
  19004.                 case token=='"'
  19005.                         return this.parseString()
  19006.                 case isdigit(token) or token=='-'
  19007.                         return this.parseNumber()
  19008.                 case token=='n'
  19009.                         return this.expectedKeyword('null',null)
  19010.                 case token=='f'
  19011.                         return this.expectedKeyword('false',.f.)
  19012.                 case token=='t'
  19013.                         return this.expectedKeyword('true',.t.)
  19014.                 case token=='{'
  19015.                         return this.parseObject()
  19016.                 case token=='['
  19017.                         return this.parseArray()
  19018.                 otherwise
  19019.                         this.setError('Unexpected token')
  19020.                 endcase
  19021.         return
  19022.                
  19023.        
  19024.         function expectedKeyword(cWord,eValue)
  19025.                 for i=1 to len(cWord)
  19026.                         cChar = this.getChar()
  19027.                         if cChar <> substr(cWord,i,1)
  19028.                                 this.setError("Expected keyword '" + cWord + "'")
  19029.                                 return ''
  19030.                         endif
  19031.                         this.nPos = this.nPos + 1
  19032.                 next
  19033.         return eValue
  19034.        
  19035.  
  19036.         function parseObject()
  19037.         local retval, cPropName, xValue
  19038.                 retval = createObject('myObj')
  19039.                 this.nPos = this.nPos + 1 && Eat {
  19040.                 if this.getToken()<>'}'
  19041.                         do while .t.
  19042.                                 cPropName = this.parseString()
  19043.                                 if not empty(this.cError)
  19044.                                         return null
  19045.                                 endif
  19046.                                 if this.getToken()<>':'
  19047.                                         this.setError("Expected ':' when parsing object")
  19048.                                         return null
  19049.                                 endif
  19050.                                 this.nPos = this.nPos + 1
  19051.                                 xValue = this.parseValue()
  19052.                                 if not empty(this.cError)
  19053.                                         return null
  19054.                                 endif                          
  19055.                                 ** Debug ? cPropName, type('xValue')
  19056.                                 retval.set(cPropName, xValue)
  19057.                                 if this.getToken()<>','
  19058.                                         exit
  19059.                                 endif
  19060.                                 this.nPos = this.nPos + 1
  19061.                         enddo
  19062.                 endif
  19063.                 if this.getToken()<>'}'
  19064.                         this.setError("Expected '}' at the end of object")
  19065.                         return null
  19066.                 endif
  19067.                 this.nPos = this.nPos + 1
  19068.         return retval
  19069.  
  19070.  
  19071.         function parseArray()
  19072.         local retVal, xValue
  19073.                 retval = createObject('MyArray')
  19074.                 this.nPos = this.nPos + 1       && Eat [
  19075.                 if this.getToken() <> ']'
  19076.                         do while .t.
  19077.                                 xValue = this.parseValue()
  19078.                                 if not empty(this.cError)
  19079.                                         return null
  19080.                                 endif
  19081.                                 retval.add( xValue )
  19082.                                 if this.getToken()<>','
  19083.                                         exit
  19084.                                 endif
  19085.                                 this.nPos = this.nPos + 1
  19086.                         enddo
  19087.                         if this.getToken() <> ']'
  19088.                                 this.setError('Expected ] at the end of array')
  19089.                                 return null
  19090.                         endif
  19091.                 endif
  19092.                 this.nPos = this.nPos + 1
  19093.         return retval
  19094.        
  19095.  
  19096.         function parseString()
  19097.         local cRetVal, c
  19098.                 if this.getToken()<>'"'
  19099.                         this.setError('Expected "')
  19100.                         return ''
  19101.                 endif
  19102.                 this.nPos = this.nPos + 1       && Eat "
  19103.                 cRetVal = ''
  19104.                 do while .t.
  19105.                         c = this.getChar()
  19106.                         if c==''
  19107.                                 return ''
  19108.                         endif
  19109.                         if c == '"'
  19110.                                 this.nPos = this.nPos + 1
  19111.                                 exit
  19112.                         endif
  19113.                         if c == '\'
  19114.                                 this.nPos = this.nPos + 1
  19115.                                 if (this.nPos>this.nLen)
  19116.                                         this.setError('\\ at the end of input')
  19117.                                         return ''
  19118.                                 endif
  19119.                                 c = this.getChar()
  19120.                                 if c==''
  19121.                                         return ''
  19122.                                 endif
  19123.                                 do case
  19124.                                 case c=='"'
  19125.                                         c='"'
  19126.                                 case c=='\'
  19127.                                         c='\'
  19128.                                 case c=='/'
  19129.                                         c='/'
  19130.                                 case c=='b'
  19131.                                         c=chr(8)
  19132.                                 case c=='t'
  19133.                                         c=chr(9)
  19134.                                 case c=='n'
  19135.                                         c=chr(10)
  19136.                                 case c=='f'
  19137.                                         c=chr(12)
  19138.                                 case c=='r'
  19139.                                         c=chr(13)
  19140.                                 otherwise
  19141.                                         ******* FALTAN LOS UNICODE
  19142.                                         this.setError('Invalid escape sequence in string literal')
  19143.                                         return ''
  19144.                                 endcase
  19145.                         endif
  19146.                         cRetVal = cRetVal + c
  19147.                         this.nPos = this.nPos + 1
  19148.                 enddo
  19149.         return cRetVal
  19150.                                        
  19151.  
  19152.         **** Pendiente numeros con E
  19153.         function parseNumber()
  19154.         local nStartPos,c, isInt, cNumero
  19155.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  19156.                         this.setError('Expected number literal')
  19157.                         return 0
  19158.                 endif
  19159.                 nStartPos = this.nPos
  19160.                 c = this.getChar()
  19161.                 if c == '-'
  19162.                         c = this.nextChar()
  19163.                 endif
  19164.                 if c == '0'
  19165.                         c = this.nextChar()
  19166.                 else
  19167.                         if isdigit(c)
  19168.                                 c = this.nextChar()
  19169.                                 do while isdigit(c)
  19170.                                         c = this.nextChar()
  19171.                                 enddo
  19172.                         else
  19173.                                 this.setError('Expected digit when parsing number')
  19174.                                 return 0
  19175.                         endif
  19176.                 endif
  19177.                
  19178.                 isInt = .t.
  19179.                 if c=='.'
  19180.                         c = this.nextChar()
  19181.                         if isdigit(c)
  19182.                                 c = this.nextChar()
  19183.                                 isInt = .f.
  19184.                                 do while isDigit(c)
  19185.                                         c = this.nextChar()
  19186.                                 enddo
  19187.                         else
  19188.                                 this.setError('Expected digit following dot comma')
  19189.                                 return 0
  19190.                         endif
  19191.                 endif
  19192.                
  19193.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  19194.         return val(cNumero)
  19195.  
  19196.  
  19197.  
  19198.         function getToken()
  19199.         local char1
  19200.                 do while .t.
  19201.                         if this.nPos > this.nLen
  19202.                                 return null
  19203.                         endif
  19204.                         char1 = substr(this.cJson, this.nPos, 1)
  19205.                         if char1==' '
  19206.                                 this.nPos = this.nPos + 1
  19207.                                 loop
  19208.                         endif
  19209.                         return char1
  19210.                 enddo
  19211.         return
  19212.        
  19213.                
  19214.                
  19215.         function getChar()
  19216.                 if this.nPos > this.nLen
  19217.                         this.setError('Unexpected end of JSON stream')
  19218.                         return ''
  19219.                 endif
  19220.         return substr(this.cJson, this.nPos, 1)
  19221.        
  19222.         function nextChar()
  19223.                 this.nPos = this.nPos + 1
  19224.                 if this.nPos > this.nLen
  19225.                         return ''
  19226.                 endif
  19227.         return substr(this.cJson, this.nPos, 1)
  19228.        
  19229.         function setError(cMsg)
  19230.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  19231.         return
  19232.  
  19233.  
  19234.         function fixUnicode(cStr)
  19235.                 cStr = StrTran(cStr,'\u00e1','á')
  19236.                 cStr = StrTran(cStr,'\u00e9','é')
  19237.                 cStr = StrTran(cStr,'\u00ed','í')
  19238.                 cStr = StrTran(cStr,'\u00f3','ó')
  19239.                 cStr = StrTran(cStr,'\u00fa','ú')
  19240.                 cStr = StrTran(cStr,'\u00c1','Á')
  19241.                 cStr = StrTran(cStr,'\u00c9','É')
  19242.                 cStr = StrTran(cStr,'\u00cd','Í')
  19243.                 cStr = StrTran(cStr,'\u00d3','Ó')
  19244.                 cStr = StrTran(cStr,'\u00da','Ú')
  19245.                 cStr = StrTran(cStr,'\u00f1','ń')
  19246.                 cStr = StrTran(cStr,'\u00d1','Ń')
  19247.         return cStr
  19248.  
  19249.  
  19250.  
  19251. enddefine
  19252.  
  19253.  
  19254.  
  19255.  
  19256.  
  19257. *
  19258. * class used to return an array
  19259. *
  19260. define class myArray as custom
  19261.         nSize = 0
  19262.         dimension array[1]
  19263.  
  19264.         function add(xExpr)
  19265.                 this.nSize = this.nSize + 1
  19266.                 dimension this.array[this.nSize]
  19267.                 this.array[this.nSize] = xExpr
  19268.         return
  19269.  
  19270.         function get(n)
  19271.         return this.array[n]
  19272.  
  19273. enddefine
  19274.  
  19275.  
  19276.  
  19277. *
  19278. * class used to simulate an object
  19279. * all properties are prefixed with 'prop' to permit property names like: error, init
  19280. * that already exists like vfp methods
  19281. *
  19282. define class myObj as custom
  19283. Hidden ;
  19284.         ClassLibrary,Comment, ;
  19285.         BaseClass,ControlCount, ;
  19286.         Controls,Objects,Object,;
  19287.         Height,HelpContextID,Left,Name, ;
  19288.         Parent,ParentClass,Picture, ;
  19289.         Tag,Top,WhatsThisHelpID,Width
  19290.                
  19291.         function set(cPropName, xValue)
  19292.                 cPropName = '_'+cPropName
  19293.                 if type('this.'+cPropName)=='U'
  19294.                         this.addProperty(cPropName,xValue)
  19295.                 else
  19296.                         local cmd
  19297.                         cmd = 'this.'+cPropName+'=xValue'
  19298.                         &cmd
  19299.                 endif
  19300.         return
  19301.        
  19302.         procedure get (cPropName)
  19303.                 cPropName = '_'+cPropName
  19304.                 If type('this.'+cPropName)=='U'
  19305.                         return ''
  19306.                 Else
  19307.                         local cmd
  19308.                         cmd = 'return this.'+cPropName
  19309.                         &cmd
  19310.                 endif
  19311.         return ''
  19312. enddefine
  19313.  
  19314.  
  19315.  
  19316.  
  19317.  
  19318. function testJsonClass
  19319.         clear
  19320.         set decimal to 10
  19321.         oJson = newObject('json')
  19322.        
  19323.        
  19324.         ? 'Test Basic Types'
  19325.         ? '----------------'
  19326.         ? oJson.decode('null')
  19327.         ? oJson.decode('true')
  19328.         ? oJson.decode('false')
  19329.         ?
  19330.         ? oJson.decode('791123')
  19331.         ? oJson.decode('791123.45')
  19332.         ? oJson.decode('791123.45.')
  19333.         ? oJson.decode('"nacho gtz"')
  19334.         if not empty(oJson.cError)
  19335.                 ? oJson.cError
  19336.                 return
  19337.         endif
  19338.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  19339.         if not empty(oJson.cError)
  19340.                 ? oJson.cError
  19341.                 return
  19342.         endif
  19343.        
  19344.         ? 'Test Array'
  19345.         ? '----------'
  19346.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  19347.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  19348.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  19349.         nombres = arr.get(1)
  19350.         edades  = arr.get(2)
  19351.         ? nombres.get(1), edades.get(1)
  19352.         ? nombres.get(2), edades.get(2)
  19353.         ? nombres.get(3), edades.get(3)
  19354.         ?
  19355.         ? 'Test Object'
  19356.         ? '-----------'
  19357.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  19358.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  19359.         ? obj._Nombre, obj._Edad, obj._IsGood
  19360.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  19361.         ? obj.get('jsonrpc'), obj._jsonrpc
  19362.         ? obj.get('id'), obj._id
  19363.         ? obj.get('method'), obj._method
  19364.         ? obj._Params.array[1], obj._Params.get(1)
  19365.  
  19366.         ?
  19367.         ? 'Test nested object'
  19368.         ? '------------------'
  19369.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  19370.         obj = oJson.decode(cJson)
  19371.         if not empty(oJson.cError)
  19372.                 ? oJson.cError
  19373.                 return
  19374.         endif
  19375.         ? cJson
  19376.         ? 'method -->',obj._method
  19377.         ? 'usrkey -->',obj._params._data._usrkey
  19378.         ? 'sendto -->',obj._params._data._sendto
  19379.         ? 'name  --->',obj._params._data._name
  19380.         ? 'expires ->',obj._params._data._expires
  19381.  
  19382.         ?
  19383.         ? 'Test empty object'
  19384.         ? '-----------------'
  19385.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  19386.         obj = oJson.decode(cJson)
  19387.         if not empty(oJson.cError)     
  19388.                 ? oJson.cError
  19389.                 return
  19390.         endif
  19391.         ? cJson
  19392.         ? 'result -->',obj._result, obj.get('result')
  19393.         oError = obj.get('error')
  19394.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  19395.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  19396.         ? 'id  ----->',obj._id, obj.get('id')
  19397.         ?  type("oError._code")
  19398.  
  19399.         ?
  19400.         ? 'Probar decode-enconde-decode-encode'
  19401.         ? '------------------------------------'
  19402.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  19403.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  19404.         ? cJson
  19405.         oSmtp = json_decode(cJson)
  19406.         cJson =  json_encode(oSmtp)
  19407.         ? cJson
  19408.         oSmtp = json_decode(cJson)
  19409.         cJson =  json_encode(oSmtp)
  19410.         ? cJson
  19411.  
  19412.         * Probar falla
  19413.         ?
  19414.         ? 'Probar una falla en el json'
  19415.         ? '---------------------------'
  19416.         cJson = ' {"server":"", "user":"", "password":"" ,'
  19417.         oSmtp = json_decode(cJson)
  19418.         if not empty(json_getErrorMsg())
  19419.                 ? json_getErrorMsg()
  19420.         endif
  19421.  
  19422.         ?
  19423.         ? 'Pruebas Finalizadas'
  19424. retur
  19425. * VFPJSON  Encode and Decode JSON for VFP
  19426. * Examples:
  19427. *       oJson = newobject('json','json.prg')
  19428. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  19429. *       ? oJson.encode(oCustomer)
  19430. *       ? oCustomer.get('name')
  19431. *       ? oCustomer.get('lastname')
  19432. *
  19433. *
  19434. lRunTest = .f.
  19435. if lRunTest
  19436.         testJsonClass()
  19437. endif
  19438. return
  19439.  
  19440.  
  19441. function json_encode(xExpr)
  19442.         if vartype(_json)<>'O'
  19443.                 public _json
  19444.                 _json = newobject('json')
  19445.         endif
  19446. return _json.encode(@xExpr)
  19447.  
  19448.  
  19449. function json_decode(cJson)
  19450. local retval
  19451.         if vartype(_json)<>'O'
  19452.                 public _json
  19453.                 _json = newobject('json')
  19454.         endif
  19455.         retval = _json.decode(cJson)
  19456.         if not empty(_json.cError)
  19457.                 return null
  19458.         endif
  19459. return retval
  19460.  
  19461. function json_getErrorMsg()
  19462. return _json.cError
  19463.        
  19464.  
  19465.  
  19466. *
  19467. * json class
  19468. *
  19469. *
  19470. define class json as custom
  19471.  
  19472.  
  19473.         nPos=0
  19474.         nLen=0
  19475.         cJson=''
  19476.         cError=''
  19477.  
  19478.  
  19479.         *
  19480.         * Genera el codigo cJson para parametro que se manda
  19481.         *
  19482.         function encode(xExpr)
  19483.         local cTipo
  19484.                 * Cuando se manda una arreglo,
  19485.                 if type('ALen(xExpr)')=='N'
  19486.                         cTipo = 'A'
  19487.                 Else
  19488.                         cTipo = VarType(xExpr)
  19489.                 Endif
  19490.                
  19491.                 Do Case
  19492.                 Case cTipo=='D'
  19493.                         return '"'+dtos(xExpr)+'"'
  19494.                 Case cTipo=='N'
  19495.                         return Transform(xExpr)
  19496.                 Case cTipo=='L'
  19497.                         return iif(xExpr,'true','false')
  19498.                 Case cTipo=='X'
  19499.                         return 'null'
  19500.                 Case cTipo=='C'
  19501.                         xExpr = allt(xExpr)
  19502.                         xExpr = StrTran(xExpr, '\', '\\' )
  19503.                         xExpr = StrTran(xExpr, '/', '\/' )
  19504.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  19505.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  19506.                         xExpr = StrTran(xExpr, '"', '\"' )
  19507.                         return '"'+xExpr+'"'
  19508.  
  19509.                 case cTipo=='O'
  19510.                         local cProp, cJsonValue, cRetVal, aProp[1]
  19511.                         =AMembers(aProp,xExpr)
  19512.                         cRetVal = ''
  19513.                         for each cProp in aProp
  19514.                                 *? cProp
  19515.                                 *? cRetVal
  19516.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  19517.                                         * algunas propiedades pueden no estar definidas
  19518.                                         * como: activecontrol, parent, etc
  19519.                                         loop
  19520.                                 endif
  19521.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  19522.                                         *
  19523.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  19524.                                         *
  19525.                                         Local i,nTotElem
  19526.                                         cJsonValue = ''
  19527.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  19528.                                         For i=1 to nTotElem
  19529.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  19530.                                         Next
  19531.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  19532.                                 else
  19533.                                         *
  19534.                                         * es otro tipo de dato normal C, N, L
  19535.                                         *
  19536.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  19537.                                 endif
  19538.                                 if left(cProp,1)=='_'
  19539.                                         cProp = substr(cProp,2)
  19540.                                 endif
  19541.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  19542.                         next
  19543.                         return '{' + substr(cRetVal,2) + '}'
  19544.  
  19545.                 case cTipo=='A'
  19546.                         local valor, cRetVal
  19547.                         cRetVal = ''   
  19548.                         for each valor in xExpr
  19549.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  19550.                         next
  19551.                         return  '[' + substr(cRetVal,2) + ']'
  19552.                        
  19553.                 endcase
  19554.  
  19555.         return ''
  19556.  
  19557.  
  19558.  
  19559.  
  19560.  
  19561.         *
  19562.         * regresa un elemento representado por la cadena json que se manda
  19563.         *
  19564.        
  19565.         function decode(cJson)
  19566.         local retValue
  19567.                 cJson = StrTran(cJson,chr(9),'')
  19568.                 cJson = StrTran(cJson,chr(10),'')
  19569.                 cJson = StrTran(cJson,chr(13),'')
  19570.                 cJson = this.fixUnicode(cJson)
  19571.                 this.nPos  = 1
  19572.                 this.cJson = cJson
  19573.                 this.nLen  = len(cJson)
  19574.                 this.cError = ''
  19575.                 retValue = this.parsevalue()
  19576.                 if not empty(this.cError)
  19577.                         return null
  19578.                 endif
  19579.                 if this.getToken()<>null
  19580.                         this.setError('Junk at the end of JSON input')
  19581.                         return null
  19582.                 endif
  19583.         return retValue
  19584.                
  19585.        
  19586.         function parseValue()
  19587.         local token
  19588.                 token = this.getToken()
  19589.                 if token==null
  19590.                         this.setError('Nothing to parse')
  19591.                         return null
  19592.                 endif
  19593.                 do case
  19594.                 case token=='"'
  19595.                         return this.parseString()
  19596.                 case isdigit(token) or token=='-'
  19597.                         return this.parseNumber()
  19598.                 case token=='n'
  19599.                         return this.expectedKeyword('null',null)
  19600.                 case token=='f'
  19601.                         return this.expectedKeyword('false',.f.)
  19602.                 case token=='t'
  19603.                         return this.expectedKeyword('true',.t.)
  19604.                 case token=='{'
  19605.                         return this.parseObject()
  19606.                 case token=='['
  19607.                         return this.parseArray()
  19608.                 otherwise
  19609.                         this.setError('Unexpected token')
  19610.                 endcase
  19611.         return
  19612.                
  19613.        
  19614.         function expectedKeyword(cWord,eValue)
  19615.                 for i=1 to len(cWord)
  19616.                         cChar = this.getChar()
  19617.                         if cChar <> substr(cWord,i,1)
  19618.                                 this.setError("Expected keyword '" + cWord + "'")
  19619.                                 return ''
  19620.                         endif
  19621.                         this.nPos = this.nPos + 1
  19622.                 next
  19623.         return eValue
  19624.        
  19625.  
  19626.         function parseObject()
  19627.         local retval, cPropName, xValue
  19628.                 retval = createObject('myObj')
  19629.                 this.nPos = this.nPos + 1 && Eat {
  19630.                 if this.getToken()<>'}'
  19631.                         do while .t.
  19632.                                 cPropName = this.parseString()
  19633.                                 if not empty(this.cError)
  19634.                                         return null
  19635.                                 endif
  19636.                                 if this.getToken()<>':'
  19637.                                         this.setError("Expected ':' when parsing object")
  19638.                                         return null
  19639.                                 endif
  19640.                                 this.nPos = this.nPos + 1
  19641.                                 xValue = this.parseValue()
  19642.                                 if not empty(this.cError)
  19643.                                         return null
  19644.                                 endif                          
  19645.                                 ** Debug ? cPropName, type('xValue')
  19646.                                 retval.set(cPropName, xValue)
  19647.                                 if this.getToken()<>','
  19648.                                         exit
  19649.                                 endif
  19650.                                 this.nPos = this.nPos + 1
  19651.                         enddo
  19652.                 endif
  19653.                 if this.getToken()<>'}'
  19654.                         this.setError("Expected '}' at the end of object")
  19655.                         return null
  19656.                 endif
  19657.                 this.nPos = this.nPos + 1
  19658.         return retval
  19659.  
  19660.  
  19661.         function parseArray()
  19662.         local retVal, xValue
  19663.                 retval = createObject('MyArray')
  19664.                 this.nPos = this.nPos + 1       && Eat [
  19665.                 if this.getToken() <> ']'
  19666.                         do while .t.
  19667.                                 xValue = this.parseValue()
  19668.                                 if not empty(this.cError)
  19669.                                         return null
  19670.                                 endif
  19671.                                 retval.add( xValue )
  19672.                                 if this.getToken()<>','
  19673.                                         exit
  19674.                                 endif
  19675.                                 this.nPos = this.nPos + 1
  19676.                         enddo
  19677.                         if this.getToken() <> ']'
  19678.                                 this.setError('Expected ] at the end of array')
  19679.                                 return null
  19680.                         endif
  19681.                 endif
  19682.                 this.nPos = this.nPos + 1
  19683.         return retval
  19684.        
  19685.  
  19686.         function parseString()
  19687.         local cRetVal, c
  19688.                 if this.getToken()<>'"'
  19689.                         this.setError('Expected "')
  19690.                         return ''
  19691.                 endif
  19692.                 this.nPos = this.nPos + 1       && Eat "
  19693.                 cRetVal = ''
  19694.                 do while .t.
  19695.                         c = this.getChar()
  19696.                         if c==''
  19697.                                 return ''
  19698.                         endif
  19699.                         if c == '"'
  19700.                                 this.nPos = this.nPos + 1
  19701.                                 exit
  19702.                         endif
  19703.                         if c == '\'
  19704.                                 this.nPos = this.nPos + 1
  19705.                                 if (this.nPos>this.nLen)
  19706.                                         this.setError('\\ at the end of input')
  19707.                                         return ''
  19708.                                 endif
  19709.                                 c = this.getChar()
  19710.                                 if c==''
  19711.                                         return ''
  19712.                                 endif
  19713.                                 do case
  19714.                                 case c=='"'
  19715.                                         c='"'
  19716.                                 case c=='\'
  19717.                                         c='\'
  19718.                                 case c=='/'
  19719.                                         c='/'
  19720.                                 case c=='b'
  19721.                                         c=chr(8)
  19722.                                 case c=='t'
  19723.                                         c=chr(9)
  19724.                                 case c=='n'
  19725.                                         c=chr(10)
  19726.                                 case c=='f'
  19727.                                         c=chr(12)
  19728.                                 case c=='r'
  19729.                                         c=chr(13)
  19730.                                 otherwise
  19731.                                         ******* FALTAN LOS UNICODE
  19732.                                         this.setError('Invalid escape sequence in string literal')
  19733.                                         return ''
  19734.                                 endcase
  19735.                         endif
  19736.                         cRetVal = cRetVal + c
  19737.                         this.nPos = this.nPos + 1
  19738.                 enddo
  19739.         return cRetVal
  19740.                                        
  19741.  
  19742.         **** Pendiente numeros con E
  19743.         function parseNumber()
  19744.         local nStartPos,c, isInt, cNumero
  19745.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  19746.                         this.setError('Expected number literal')
  19747.                         return 0
  19748.                 endif
  19749.                 nStartPos = this.nPos
  19750.                 c = this.getChar()
  19751.                 if c == '-'
  19752.                         c = this.nextChar()
  19753.                 endif
  19754.                 if c == '0'
  19755.                         c = this.nextChar()
  19756.                 else
  19757.                         if isdigit(c)
  19758.                                 c = this.nextChar()
  19759.                                 do while isdigit(c)
  19760.                                         c = this.nextChar()
  19761.                                 enddo
  19762.                         else
  19763.                                 this.setError('Expected digit when parsing number')
  19764.                                 return 0
  19765.                         endif
  19766.                 endif
  19767.                
  19768.                 isInt = .t.
  19769.                 if c=='.'
  19770.                         c = this.nextChar()
  19771.                         if isdigit(c)
  19772.                                 c = this.nextChar()
  19773.                                 isInt = .f.
  19774.                                 do while isDigit(c)
  19775.                                         c = this.nextChar()
  19776.                                 enddo
  19777.                         else
  19778.                                 this.setError('Expected digit following dot comma')
  19779.                                 return 0
  19780.                         endif
  19781.                 endif
  19782.                
  19783.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  19784.         return val(cNumero)
  19785.  
  19786.  
  19787.  
  19788.         function getToken()
  19789.         local char1
  19790.                 do while .t.
  19791.                         if this.nPos > this.nLen
  19792.                                 return null
  19793.                         endif
  19794.                         char1 = substr(this.cJson, this.nPos, 1)
  19795.                         if char1==' '
  19796.                                 this.nPos = this.nPos + 1
  19797.                                 loop
  19798.                         endif
  19799.                         return char1
  19800.                 enddo
  19801.         return
  19802.        
  19803.                
  19804.                
  19805.         function getChar()
  19806.                 if this.nPos > this.nLen
  19807.                         this.setError('Unexpected end of JSON stream')
  19808.                         return ''
  19809.                 endif
  19810.         return substr(this.cJson, this.nPos, 1)
  19811.        
  19812.         function nextChar()
  19813.                 this.nPos = this.nPos + 1
  19814.                 if this.nPos > this.nLen
  19815.                         return ''
  19816.                 endif
  19817.         return substr(this.cJson, this.nPos, 1)
  19818.        
  19819.         function setError(cMsg)
  19820.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  19821.         return
  19822.  
  19823.  
  19824.         function fixUnicode(cStr)
  19825.                 cStr = StrTran(cStr,'\u00e1','á')
  19826.                 cStr = StrTran(cStr,'\u00e9','é')
  19827.                 cStr = StrTran(cStr,'\u00ed','í')
  19828.                 cStr = StrTran(cStr,'\u00f3','ó')
  19829.                 cStr = StrTran(cStr,'\u00fa','ú')
  19830.                 cStr = StrTran(cStr,'\u00c1','Á')
  19831.                 cStr = StrTran(cStr,'\u00c9','É')
  19832.                 cStr = StrTran(cStr,'\u00cd','Í')
  19833.                 cStr = StrTran(cStr,'\u00d3','Ó')
  19834.                 cStr = StrTran(cStr,'\u00da','Ú')
  19835.                 cStr = StrTran(cStr,'\u00f1','ń')
  19836.                 cStr = StrTran(cStr,'\u00d1','Ń')
  19837.         return cStr
  19838.  
  19839.  
  19840.  
  19841. enddefine
  19842.  
  19843.  
  19844.  
  19845.  
  19846.  
  19847. *
  19848. * class used to return an array
  19849. *
  19850. define class myArray as custom
  19851.         nSize = 0
  19852.         dimension array[1]
  19853.  
  19854.         function add(xExpr)
  19855.                 this.nSize = this.nSize + 1
  19856.                 dimension this.array[this.nSize]
  19857.                 this.array[this.nSize] = xExpr
  19858.         return
  19859.  
  19860.         function get(n)
  19861.         return this.array[n]
  19862.  
  19863. enddefine
  19864.  
  19865.  
  19866.  
  19867. *
  19868. * class used to simulate an object
  19869. * all properties are prefixed with 'prop' to permit property names like: error, init
  19870. * that already exists like vfp methods
  19871. *
  19872. define class myObj as custom
  19873. Hidden ;
  19874.         ClassLibrary,Comment, ;
  19875.         BaseClass,ControlCount, ;
  19876.         Controls,Objects,Object,;
  19877.         Height,HelpContextID,Left,Name, ;
  19878.         Parent,ParentClass,Picture, ;
  19879.         Tag,Top,WhatsThisHelpID,Width
  19880.                
  19881.         function set(cPropName, xValue)
  19882.                 cPropName = '_'+cPropName
  19883.                 if type('this.'+cPropName)=='U'
  19884.                         this.addProperty(cPropName,xValue)
  19885.                 else
  19886.                         local cmd
  19887.                         cmd = 'this.'+cPropName+'=xValue'
  19888.                         &cmd
  19889.                 endif
  19890.         return
  19891.        
  19892.         procedure get (cPropName)
  19893.                 cPropName = '_'+cPropName
  19894.                 If type('this.'+cPropName)=='U'
  19895.                         return ''
  19896.                 Else
  19897.                         local cmd
  19898.                         cmd = 'return this.'+cPropName
  19899.                         &cmd
  19900.                 endif
  19901.         return ''
  19902. enddefine
  19903.  
  19904.  
  19905.  
  19906.  
  19907.  
  19908. function testJsonClass
  19909.         clear
  19910.         set decimal to 10
  19911.         oJson = newObject('json')
  19912.        
  19913.        
  19914.         ? 'Test Basic Types'
  19915.         ? '----------------'
  19916.         ? oJson.decode('null')
  19917.         ? oJson.decode('true')
  19918.         ? oJson.decode('false')
  19919.         ?
  19920.         ? oJson.decode('791123')
  19921.         ? oJson.decode('791123.45')
  19922.         ? oJson.decode('791123.45.')
  19923.         ? oJson.decode('"nacho gtz"')
  19924.         if not empty(oJson.cError)
  19925.                 ? oJson.cError
  19926.                 return
  19927.         endif
  19928.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  19929.         if not empty(oJson.cError)
  19930.                 ? oJson.cError
  19931.                 return
  19932.         endif
  19933.        
  19934.         ? 'Test Array'
  19935.         ? '----------'
  19936.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  19937.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  19938.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  19939.         nombres = arr.get(1)
  19940.         edades  = arr.get(2)
  19941.         ? nombres.get(1), edades.get(1)
  19942.         ? nombres.get(2), edades.get(2)
  19943.         ? nombres.get(3), edades.get(3)
  19944.         ?
  19945.         ? 'Test Object'
  19946.         ? '-----------'
  19947.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  19948.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  19949.         ? obj._Nombre, obj._Edad, obj._IsGood
  19950.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  19951.         ? obj.get('jsonrpc'), obj._jsonrpc
  19952.         ? obj.get('id'), obj._id
  19953.         ? obj.get('method'), obj._method
  19954.         ? obj._Params.array[1], obj._Params.get(1)
  19955.  
  19956.         ?
  19957.         ? 'Test nested object'
  19958.         ? '------------------'
  19959.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  19960.         obj = oJson.decode(cJson)
  19961.         if not empty(oJson.cError)
  19962.                 ? oJson.cError
  19963.                 return
  19964.         endif
  19965.         ? cJson
  19966.         ? 'method -->',obj._method
  19967.         ? 'usrkey -->',obj._params._data._usrkey
  19968.         ? 'sendto -->',obj._params._data._sendto
  19969.         ? 'name  --->',obj._params._data._name
  19970.         ? 'expires ->',obj._params._data._expires
  19971.  
  19972.         ?
  19973.         ? 'Test empty object'
  19974.         ? '-----------------'
  19975.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  19976.         obj = oJson.decode(cJson)
  19977.         if not empty(oJson.cError)     
  19978.                 ? oJson.cError
  19979.                 return
  19980.         endif
  19981.         ? cJson
  19982.         ? 'result -->',obj._result, obj.get('result')
  19983.         oError = obj.get('error')
  19984.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  19985.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  19986.         ? 'id  ----->',obj._id, obj.get('id')
  19987.         ?  type("oError._code")
  19988.  
  19989.         ?
  19990.         ? 'Probar decode-enconde-decode-encode'
  19991.         ? '------------------------------------'
  19992.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  19993.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  19994.         ? cJson
  19995.         oSmtp = json_decode(cJson)
  19996.         cJson =  json_encode(oSmtp)
  19997.         ? cJson
  19998.         oSmtp = json_decode(cJson)
  19999.         cJson =  json_encode(oSmtp)
  20000.         ? cJson
  20001.  
  20002.         * Probar falla
  20003.         ?
  20004.         ? 'Probar una falla en el json'
  20005.         ? '---------------------------'
  20006.         cJson = ' {"server":"", "user":"", "password":"" ,'
  20007.         oSmtp = json_decode(cJson)
  20008.         if not empty(json_getErrorMsg())
  20009.                 ? json_getErrorMsg()
  20010.         endif
  20011.  
  20012.         ?
  20013.         ? 'Pruebas Finalizadas'
  20014. retur
  20015. * Examples:
  20016. *       oJson = newobject('json','json.prg')
  20017. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  20018. *       ? oJson.encode(oCustomer)
  20019. *       ? oCustomer.get('name')
  20020. *       ? oCustomer.get('lastname')
  20021. *
  20022. *
  20023. lRunTest = .f.
  20024. if lRunTest
  20025.         testJsonClass()
  20026. endif
  20027. return
  20028.  
  20029.  
  20030. function json_encode(xExpr)
  20031.         if vartype(_json)<>'O'
  20032.                 public _json
  20033.                 _json = newobject('json')
  20034.         endif
  20035. return _json.encode(@xExpr)
  20036.  
  20037.  
  20038. function json_decode(cJson)
  20039. local retval
  20040.         if vartype(_json)<>'O'
  20041.                 public _json
  20042.                 _json = newobject('json')
  20043.         endif
  20044.         retval = _json.decode(cJson)
  20045.         if not empty(_json.cError)
  20046.                 return null
  20047.         endif
  20048. return retval
  20049.  
  20050. function json_getErrorMsg()
  20051. return _json.cError
  20052.        
  20053.  
  20054.  
  20055. *
  20056. * json class
  20057. *
  20058. *
  20059. define class json as custom
  20060.  
  20061.  
  20062.         nPos=0
  20063.         nLen=0
  20064.         cJson=''
  20065.         cError=''
  20066.  
  20067.  
  20068.         *
  20069.         * Genera el codigo cJson para parametro que se manda
  20070.         *
  20071.         function encode(xExpr)
  20072.         local cTipo
  20073.                 * Cuando se manda una arreglo,
  20074.                 if type('ALen(xExpr)')=='N'
  20075.                         cTipo = 'A'
  20076.                 Else
  20077.                         cTipo = VarType(xExpr)
  20078.                 Endif
  20079.                
  20080.                 Do Case
  20081.                 Case cTipo=='D'
  20082.                         return '"'+dtos(xExpr)+'"'
  20083.                 Case cTipo=='N'
  20084.                         return Transform(xExpr)
  20085.                 Case cTipo=='L'
  20086.                         return iif(xExpr,'true','false')
  20087.                 Case cTipo=='X'
  20088.                         return 'null'
  20089.                 Case cTipo=='C'
  20090.                         xExpr = allt(xExpr)
  20091.                         xExpr = StrTran(xExpr, '\', '\\' )
  20092.                         xExpr = StrTran(xExpr, '/', '\/' )
  20093.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  20094.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  20095.                         xExpr = StrTran(xExpr, '"', '\"' )
  20096.                         return '"'+xExpr+'"'
  20097.  
  20098.                 case cTipo=='O'
  20099.                         local cProp, cJsonValue, cRetVal, aProp[1]
  20100.                         =AMembers(aProp,xExpr)
  20101.                         cRetVal = ''
  20102.                         for each cProp in aProp
  20103.                                 *? cProp
  20104.                                 *? cRetVal
  20105.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  20106.                                         * algunas propiedades pueden no estar definidas
  20107.                                         * como: activecontrol, parent, etc
  20108.                                         loop
  20109.                                 endif
  20110.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  20111.                                         *
  20112.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  20113.                                         *
  20114.                                         Local i,nTotElem
  20115.                                         cJsonValue = ''
  20116.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  20117.                                         For i=1 to nTotElem
  20118.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  20119.                                         Next
  20120.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  20121.                                 else
  20122.                                         *
  20123.                                         * es otro tipo de dato normal C, N, L
  20124.                                         *
  20125.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  20126.                                 endif
  20127.                                 if left(cProp,1)=='_'
  20128.                                         cProp = substr(cProp,2)
  20129.                                 endif
  20130.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  20131.                         next
  20132.                         return '{' + substr(cRetVal,2) + '}'
  20133.  
  20134.                 case cTipo=='A'
  20135.                         local valor, cRetVal
  20136.                         cRetVal = ''   
  20137.                         for each valor in xExpr
  20138.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  20139.                         next
  20140.                         return  '[' + substr(cRetVal,2) + ']'
  20141.                        
  20142.                 endcase
  20143.  
  20144.         return ''
  20145.  
  20146.  
  20147.  
  20148.  
  20149.  
  20150.         *
  20151.         * regresa un elemento representado por la cadena json que se manda
  20152.         *
  20153.        
  20154.         function decode(cJson)
  20155.         local retValue
  20156.                 cJson = StrTran(cJson,chr(9),'')
  20157.                 cJson = StrTran(cJson,chr(10),'')
  20158.                 cJson = StrTran(cJson,chr(13),'')
  20159.                 cJson = this.fixUnicode(cJson)
  20160.                 this.nPos  = 1
  20161.                 this.cJson = cJson
  20162.                 this.nLen  = len(cJson)
  20163.                 this.cError = ''
  20164.                 retValue = this.parsevalue()
  20165.                 if not empty(this.cError)
  20166.                         return null
  20167.                 endif
  20168.                 if this.getToken()<>null
  20169.                         this.setError('Junk at the end of JSON input')
  20170.                         return null
  20171.                 endif
  20172.         return retValue
  20173.                
  20174.        
  20175.         function parseValue()
  20176.         local token
  20177.                 token = this.getToken()
  20178.                 if token==null
  20179.                         this.setError('Nothing to parse')
  20180.                         return null
  20181.                 endif
  20182.                 do case
  20183.                 case token=='"'
  20184.                         return this.parseString()
  20185.                 case isdigit(token) or token=='-'
  20186.                         return this.parseNumber()
  20187.                 case token=='n'
  20188.                         return this.expectedKeyword('null',null)
  20189.                 case token=='f'
  20190.                         return this.expectedKeyword('false',.f.)
  20191.                 case token=='t'
  20192.                         return this.expectedKeyword('true',.t.)
  20193.                 case token=='{'
  20194.                         return this.parseObject()
  20195.                 case token=='['
  20196.                         return this.parseArray()
  20197.                 otherwise
  20198.                         this.setError('Unexpected token')
  20199.                 endcase
  20200.         return
  20201.                
  20202.        
  20203.         function expectedKeyword(cWord,eValue)
  20204.                 for i=1 to len(cWord)
  20205.                         cChar = this.getChar()
  20206.                         if cChar <> substr(cWord,i,1)
  20207.                                 this.setError("Expected keyword '" + cWord + "'")
  20208.                                 return ''
  20209.                         endif
  20210.                         this.nPos = this.nPos + 1
  20211.                 next
  20212.         return eValue
  20213.        
  20214.  
  20215.         function parseObject()
  20216.         local retval, cPropName, xValue
  20217.                 retval = createObject('myObj')
  20218.                 this.nPos = this.nPos + 1 && Eat {
  20219.                 if this.getToken()<>'}'
  20220.                         do while .t.
  20221.                                 cPropName = this.parseString()
  20222.                                 if not empty(this.cError)
  20223.                                         return null
  20224.                                 endif
  20225.                                 if this.getToken()<>':'
  20226.                                         this.setError("Expected ':' when parsing object")
  20227.                                         return null
  20228.                                 endif
  20229.                                 this.nPos = this.nPos + 1
  20230.                                 xValue = this.parseValue()
  20231.                                 if not empty(this.cError)
  20232.                                         return null
  20233.                                 endif                          
  20234.                                 ** Debug ? cPropName, type('xValue')
  20235.                                 retval.set(cPropName, xValue)
  20236.                                 if this.getToken()<>','
  20237.                                         exit
  20238.                                 endif
  20239.                                 this.nPos = this.nPos + 1
  20240.                         enddo
  20241.                 endif
  20242.                 if this.getToken()<>'}'
  20243.                         this.setError("Expected '}' at the end of object")
  20244.                         return null
  20245.                 endif
  20246.                 this.nPos = this.nPos + 1
  20247.         return retval
  20248.  
  20249.  
  20250.         function parseArray()
  20251.         local retVal, xValue
  20252.                 retval = createObject('MyArray')
  20253.                 this.nPos = this.nPos + 1       && Eat [
  20254.                 if this.getToken() <> ']'
  20255.                         do while .t.
  20256.                                 xValue = this.parseValue()
  20257.                                 if not empty(this.cError)
  20258.                                         return null
  20259.                                 endif
  20260.                                 retval.add( xValue )
  20261.                                 if this.getToken()<>','
  20262.                                         exit
  20263.                                 endif
  20264.                                 this.nPos = this.nPos + 1
  20265.                         enddo
  20266.                         if this.getToken() <> ']'
  20267.                                 this.setError('Expected ] at the end of array')
  20268.                                 return null
  20269.                         endif
  20270.                 endif
  20271.                 this.nPos = this.nPos + 1
  20272.         return retval
  20273.        
  20274.  
  20275.         function parseString()
  20276.         local cRetVal, c
  20277.                 if this.getToken()<>'"'
  20278.                         this.setError('Expected "')
  20279.                         return ''
  20280.                 endif
  20281.                 this.nPos = this.nPos + 1       && Eat "
  20282.                 cRetVal = ''
  20283.                 do while .t.
  20284.                         c = this.getChar()
  20285.                         if c==''
  20286.                                 return ''
  20287.                         endif
  20288.                         if c == '"'
  20289.                                 this.nPos = this.nPos + 1
  20290.                                 exit
  20291.                         endif
  20292.                         if c == '\'
  20293.                                 this.nPos = this.nPos + 1
  20294.                                 if (this.nPos>this.nLen)
  20295.                                         this.setError('\\ at the end of input')
  20296.                                         return ''
  20297.                                 endif
  20298.                                 c = this.getChar()
  20299.                                 if c==''
  20300.                                         return ''
  20301.                                 endif
  20302.                                 do case
  20303.                                 case c=='"'
  20304.                                         c='"'
  20305.                                 case c=='\'
  20306.                                         c='\'
  20307.                                 case c=='/'
  20308.                                         c='/'
  20309.                                 case c=='b'
  20310.                                         c=chr(8)
  20311.                                 case c=='t'
  20312.                                         c=chr(9)
  20313.                                 case c=='n'
  20314.                                         c=chr(10)
  20315.                                 case c=='f'
  20316.                                         c=chr(12)
  20317.                                 case c=='r'
  20318.                                         c=chr(13)
  20319.                                 otherwise
  20320.                                         ******* FALTAN LOS UNICODE
  20321.                                         this.setError('Invalid escape sequence in string literal')
  20322.                                         return ''
  20323.                                 endcase
  20324.                         endif
  20325.                         cRetVal = cRetVal + c
  20326.                         this.nPos = this.nPos + 1
  20327.                 enddo
  20328.         return cRetVal
  20329.                                        
  20330.  
  20331.         **** Pendiente numeros con E
  20332.         function parseNumber()
  20333.         local nStartPos,c, isInt, cNumero
  20334.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  20335.                         this.setError('Expected number literal')
  20336.                         return 0
  20337.                 endif
  20338.                 nStartPos = this.nPos
  20339.                 c = this.getChar()
  20340.                 if c == '-'
  20341.                         c = this.nextChar()
  20342.                 endif
  20343.                 if c == '0'
  20344.                         c = this.nextChar()
  20345.                 else
  20346.                         if isdigit(c)
  20347.                                 c = this.nextChar()
  20348.                                 do while isdigit(c)
  20349.                                         c = this.nextChar()
  20350.                                 enddo
  20351.                         else
  20352.                                 this.setError('Expected digit when parsing number')
  20353.                                 return 0
  20354.                         endif
  20355.                 endif
  20356.                
  20357.                 isInt = .t.
  20358.                 if c=='.'
  20359.                         c = this.nextChar()
  20360.                         if isdigit(c)
  20361.                                 c = this.nextChar()
  20362.                                 isInt = .f.
  20363.                                 do while isDigit(c)
  20364.                                         c = this.nextChar()
  20365.                                 enddo
  20366.                         else
  20367.                                 this.setError('Expected digit following dot comma')
  20368.                                 return 0
  20369.                         endif
  20370.                 endif
  20371.                
  20372.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  20373.         return val(cNumero)
  20374.  
  20375.  
  20376.  
  20377.         function getToken()
  20378.         local char1
  20379.                 do while .t.
  20380.                         if this.nPos > this.nLen
  20381.                                 return null
  20382.                         endif
  20383.                         char1 = substr(this.cJson, this.nPos, 1)
  20384.                         if char1==' '
  20385.                                 this.nPos = this.nPos + 1
  20386.                                 loop
  20387.                         endif
  20388.                         return char1
  20389.                 enddo
  20390.         return
  20391.        
  20392.                
  20393.                
  20394.         function getChar()
  20395.                 if this.nPos > this.nLen
  20396.                         this.setError('Unexpected end of JSON stream')
  20397.                         return ''
  20398.                 endif
  20399.         return substr(this.cJson, this.nPos, 1)
  20400.        
  20401.         function nextChar()
  20402.                 this.nPos = this.nPos + 1
  20403.                 if this.nPos > this.nLen
  20404.                         return ''
  20405.                 endif
  20406.         return substr(this.cJson, this.nPos, 1)
  20407.        
  20408.         function setError(cMsg)
  20409.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  20410.         return
  20411.  
  20412.  
  20413.         function fixUnicode(cStr)
  20414.                 cStr = StrTran(cStr,'\u00e1','á')
  20415.                 cStr = StrTran(cStr,'\u00e9','é')
  20416.                 cStr = StrTran(cStr,'\u00ed','í')
  20417.                 cStr = StrTran(cStr,'\u00f3','ó')
  20418.                 cStr = StrTran(cStr,'\u00fa','ú')
  20419.                 cStr = StrTran(cStr,'\u00c1','Á')
  20420.                 cStr = StrTran(cStr,'\u00c9','É')
  20421.                 cStr = StrTran(cStr,'\u00cd','Í')
  20422.                 cStr = StrTran(cStr,'\u00d3','Ó')
  20423.                 cStr = StrTran(cStr,'\u00da','Ú')
  20424.                 cStr = StrTran(cStr,'\u00f1','ń')
  20425.                 cStr = StrTran(cStr,'\u00d1','Ń')
  20426.         return cStr
  20427.  
  20428.  
  20429.  
  20430. enddefine
  20431.  
  20432.  
  20433.  
  20434.  
  20435.  
  20436. *
  20437. * class used to return an array
  20438. *
  20439. define class myArray as custom
  20440.         nSize = 0
  20441.         dimension array[1]
  20442.  
  20443.         function add(xExpr)
  20444.                 this.nSize = this.nSize + 1
  20445.                 dimension this.array[this.nSize]
  20446.                 this.array[this.nSize] = xExpr
  20447.         return
  20448.  
  20449.         function get(n)
  20450.         return this.array[n]
  20451.  
  20452. enddefine
  20453.  
  20454.  
  20455.  
  20456. *
  20457. * class used to simulate an object
  20458. * all properties are prefixed with 'prop' to permit property names like: error, init
  20459. * that already exists like vfp methods
  20460. *
  20461. define class myObj as custom
  20462. Hidden ;
  20463.         ClassLibrary,Comment, ;
  20464.         BaseClass,ControlCount, ;
  20465.         Controls,Objects,Object,;
  20466.         Height,HelpContextID,Left,Name, ;
  20467.         Parent,ParentClass,Picture, ;
  20468.         Tag,Top,WhatsThisHelpID,Width
  20469.                
  20470.         function set(cPropName, xValue)
  20471.                 cPropName = '_'+cPropName
  20472.                 if type('this.'+cPropName)=='U'
  20473.                         this.addProperty(cPropName,xValue)
  20474.                 else
  20475.                         local cmd
  20476.                         cmd = 'this.'+cPropName+'=xValue'
  20477.                         &cmd
  20478.                 endif
  20479.         return
  20480.        
  20481.         procedure get (cPropName)
  20482.                 cPropName = '_'+cPropName
  20483.                 If type('this.'+cPropName)=='U'
  20484.                         return ''
  20485.                 Else
  20486.                         local cmd
  20487.                         cmd = 'return this.'+cPropName
  20488.                         &cmd
  20489.                 endif
  20490.         return ''
  20491. enddefine
  20492.  
  20493.  
  20494.  
  20495.  
  20496.  
  20497. function testJsonClass
  20498.         clear
  20499.         set decimal to 10
  20500.         oJson = newObject('json')
  20501.        
  20502.        
  20503.         ? 'Test Basic Types'
  20504.         ? '----------------'
  20505.         ? oJson.decode('null')
  20506.         ? oJson.decode('true')
  20507.         ? oJson.decode('false')
  20508.         ?
  20509.         ? oJson.decode('791123')
  20510.         ? oJson.decode('791123.45')
  20511.         ? oJson.decode('791123.45.')
  20512.         ? oJson.decode('"nacho gtz"')
  20513.         if not empty(oJson.cError)
  20514.                 ? oJson.cError
  20515.                 return
  20516.         endif
  20517.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  20518.         if not empty(oJson.cError)
  20519.                 ? oJson.cError
  20520.                 return
  20521.         endif
  20522.        
  20523.         ? 'Test Array'
  20524.         ? '----------'
  20525.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  20526.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  20527.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  20528.         nombres = arr.get(1)
  20529.         edades  = arr.get(2)
  20530.         ? nombres.get(1), edades.get(1)
  20531.         ? nombres.get(2), edades.get(2)
  20532.         ? nombres.get(3), edades.get(3)
  20533.         ?
  20534.         ? 'Test Object'
  20535.         ? '-----------'
  20536.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  20537.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  20538.         ? obj._Nombre, obj._Edad, obj._IsGood
  20539.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  20540.         ? obj.get('jsonrpc'), obj._jsonrpc
  20541.         ? obj.get('id'), obj._id
  20542.         ? obj.get('method'), obj._method
  20543.         ? obj._Params.array[1], obj._Params.get(1)
  20544.  
  20545.         ?
  20546.         ? 'Test nested object'
  20547.         ? '------------------'
  20548.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  20549.         obj = oJson.decode(cJson)
  20550.         if not empty(oJson.cError)
  20551.                 ? oJson.cError
  20552.                 return
  20553.         endif
  20554.         ? cJson
  20555.         ? 'method -->',obj._method
  20556.         ? 'usrkey -->',obj._params._data._usrkey
  20557.         ? 'sendto -->',obj._params._data._sendto
  20558.         ? 'name  --->',obj._params._data._name
  20559.         ? 'expires ->',obj._params._data._expires
  20560.  
  20561.         ?
  20562.         ? 'Test empty object'
  20563.         ? '-----------------'
  20564.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  20565.         obj = oJson.decode(cJson)
  20566.         if not empty(oJson.cError)     
  20567.                 ? oJson.cError
  20568.                 return
  20569.         endif
  20570.         ? cJson
  20571.         ? 'result -->',obj._result, obj.get('result')
  20572.         oError = obj.get('error')
  20573.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  20574.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  20575.         ? 'id  ----->',obj._id, obj.get('id')
  20576.         ?  type("oError._code")
  20577.  
  20578.         ?
  20579.         ? 'Probar decode-enconde-decode-encode'
  20580.         ? '------------------------------------'
  20581.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  20582.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  20583.         ? cJson
  20584.         oSmtp = json_decode(cJson)
  20585.         cJson =  json_encode(oSmtp)
  20586.         ? cJson
  20587.         oSmtp = json_decode(cJson)
  20588.         cJson =  json_encode(oSmtp)
  20589.         ? cJson
  20590.  
  20591.         * Probar falla
  20592.         ?
  20593.         ? 'Probar una falla en el json'
  20594.         ? '---------------------------'
  20595.         cJson = ' {"server":"", "user":"", "password":"" ,'
  20596.         oSmtp = json_decode(cJson)
  20597.         if not empty(json_getErrorMsg())
  20598.                 ? json_getErrorMsg()
  20599.         endif
  20600.  
  20601.         ?
  20602.         ? 'Pruebas Finalizadas'
  20603. retur
  20604. *       oJson = newobject('json','json.prg')
  20605. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  20606. *       ? oJson.encode(oCustomer)
  20607. *       ? oCustomer.get('name')
  20608. *       ? oCustomer.get('lastname')
  20609. *
  20610. *
  20611. lRunTest = .f.
  20612. if lRunTest
  20613.         testJsonClass()
  20614. endif
  20615. return
  20616.  
  20617.  
  20618. function json_encode(xExpr)
  20619.         if vartype(_json)<>'O'
  20620.                 public _json
  20621.                 _json = newobject('json')
  20622.         endif
  20623. return _json.encode(@xExpr)
  20624.  
  20625.  
  20626. function json_decode(cJson)
  20627. local retval
  20628.         if vartype(_json)<>'O'
  20629.                 public _json
  20630.                 _json = newobject('json')
  20631.         endif
  20632.         retval = _json.decode(cJson)
  20633.         if not empty(_json.cError)
  20634.                 return null
  20635.         endif
  20636. return retval
  20637.  
  20638. function json_getErrorMsg()
  20639. return _json.cError
  20640.        
  20641.  
  20642.  
  20643. *
  20644. * json class
  20645. *
  20646. *
  20647. define class json as custom
  20648.  
  20649.  
  20650.         nPos=0
  20651.         nLen=0
  20652.         cJson=''
  20653.         cError=''
  20654.  
  20655.  
  20656.         *
  20657.         * Genera el codigo cJson para parametro que se manda
  20658.         *
  20659.         function encode(xExpr)
  20660.         local cTipo
  20661.                 * Cuando se manda una arreglo,
  20662.                 if type('ALen(xExpr)')=='N'
  20663.                         cTipo = 'A'
  20664.                 Else
  20665.                         cTipo = VarType(xExpr)
  20666.                 Endif
  20667.                
  20668.                 Do Case
  20669.                 Case cTipo=='D'
  20670.                         return '"'+dtos(xExpr)+'"'
  20671.                 Case cTipo=='N'
  20672.                         return Transform(xExpr)
  20673.                 Case cTipo=='L'
  20674.                         return iif(xExpr,'true','false')
  20675.                 Case cTipo=='X'
  20676.                         return 'null'
  20677.                 Case cTipo=='C'
  20678.                         xExpr = allt(xExpr)
  20679.                         xExpr = StrTran(xExpr, '\', '\\' )
  20680.                         xExpr = StrTran(xExpr, '/', '\/' )
  20681.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  20682.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  20683.                         xExpr = StrTran(xExpr, '"', '\"' )
  20684.                         return '"'+xExpr+'"'
  20685.  
  20686.                 case cTipo=='O'
  20687.                         local cProp, cJsonValue, cRetVal, aProp[1]
  20688.                         =AMembers(aProp,xExpr)
  20689.                         cRetVal = ''
  20690.                         for each cProp in aProp
  20691.                                 *? cProp
  20692.                                 *? cRetVal
  20693.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  20694.                                         * algunas propiedades pueden no estar definidas
  20695.                                         * como: activecontrol, parent, etc
  20696.                                         loop
  20697.                                 endif
  20698.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  20699.                                         *
  20700.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  20701.                                         *
  20702.                                         Local i,nTotElem
  20703.                                         cJsonValue = ''
  20704.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  20705.                                         For i=1 to nTotElem
  20706.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  20707.                                         Next
  20708.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  20709.                                 else
  20710.                                         *
  20711.                                         * es otro tipo de dato normal C, N, L
  20712.                                         *
  20713.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  20714.                                 endif
  20715.                                 if left(cProp,1)=='_'
  20716.                                         cProp = substr(cProp,2)
  20717.                                 endif
  20718.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  20719.                         next
  20720.                         return '{' + substr(cRetVal,2) + '}'
  20721.  
  20722.                 case cTipo=='A'
  20723.                         local valor, cRetVal
  20724.                         cRetVal = ''   
  20725.                         for each valor in xExpr
  20726.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  20727.                         next
  20728.                         return  '[' + substr(cRetVal,2) + ']'
  20729.                        
  20730.                 endcase
  20731.  
  20732.         return ''
  20733.  
  20734.  
  20735.  
  20736.  
  20737.  
  20738.         *
  20739.         * regresa un elemento representado por la cadena json que se manda
  20740.         *
  20741.        
  20742.         function decode(cJson)
  20743.         local retValue
  20744.                 cJson = StrTran(cJson,chr(9),'')
  20745.                 cJson = StrTran(cJson,chr(10),'')
  20746.                 cJson = StrTran(cJson,chr(13),'')
  20747.                 cJson = this.fixUnicode(cJson)
  20748.                 this.nPos  = 1
  20749.                 this.cJson = cJson
  20750.                 this.nLen  = len(cJson)
  20751.                 this.cError = ''
  20752.                 retValue = this.parsevalue()
  20753.                 if not empty(this.cError)
  20754.                         return null
  20755.                 endif
  20756.                 if this.getToken()<>null
  20757.                         this.setError('Junk at the end of JSON input')
  20758.                         return null
  20759.                 endif
  20760.         return retValue
  20761.                
  20762.        
  20763.         function parseValue()
  20764.         local token
  20765.                 token = this.getToken()
  20766.                 if token==null
  20767.                         this.setError('Nothing to parse')
  20768.                         return null
  20769.                 endif
  20770.                 do case
  20771.                 case token=='"'
  20772.                         return this.parseString()
  20773.                 case isdigit(token) or token=='-'
  20774.                         return this.parseNumber()
  20775.                 case token=='n'
  20776.                         return this.expectedKeyword('null',null)
  20777.                 case token=='f'
  20778.                         return this.expectedKeyword('false',.f.)
  20779.                 case token=='t'
  20780.                         return this.expectedKeyword('true',.t.)
  20781.                 case token=='{'
  20782.                         return this.parseObject()
  20783.                 case token=='['
  20784.                         return this.parseArray()
  20785.                 otherwise
  20786.                         this.setError('Unexpected token')
  20787.                 endcase
  20788.         return
  20789.                
  20790.        
  20791.         function expectedKeyword(cWord,eValue)
  20792.                 for i=1 to len(cWord)
  20793.                         cChar = this.getChar()
  20794.                         if cChar <> substr(cWord,i,1)
  20795.                                 this.setError("Expected keyword '" + cWord + "'")
  20796.                                 return ''
  20797.                         endif
  20798.                         this.nPos = this.nPos + 1
  20799.                 next
  20800.         return eValue
  20801.        
  20802.  
  20803.         function parseObject()
  20804.         local retval, cPropName, xValue
  20805.                 retval = createObject('myObj')
  20806.                 this.nPos = this.nPos + 1 && Eat {
  20807.                 if this.getToken()<>'}'
  20808.                         do while .t.
  20809.                                 cPropName = this.parseString()
  20810.                                 if not empty(this.cError)
  20811.                                         return null
  20812.                                 endif
  20813.                                 if this.getToken()<>':'
  20814.                                         this.setError("Expected ':' when parsing object")
  20815.                                         return null
  20816.                                 endif
  20817.                                 this.nPos = this.nPos + 1
  20818.                                 xValue = this.parseValue()
  20819.                                 if not empty(this.cError)
  20820.                                         return null
  20821.                                 endif                          
  20822.                                 ** Debug ? cPropName, type('xValue')
  20823.                                 retval.set(cPropName, xValue)
  20824.                                 if this.getToken()<>','
  20825.                                         exit
  20826.                                 endif
  20827.                                 this.nPos = this.nPos + 1
  20828.                         enddo
  20829.                 endif
  20830.                 if this.getToken()<>'}'
  20831.                         this.setError("Expected '}' at the end of object")
  20832.                         return null
  20833.                 endif
  20834.                 this.nPos = this.nPos + 1
  20835.         return retval
  20836.  
  20837.  
  20838.         function parseArray()
  20839.         local retVal, xValue
  20840.                 retval = createObject('MyArray')
  20841.                 this.nPos = this.nPos + 1       && Eat [
  20842.                 if this.getToken() <> ']'
  20843.                         do while .t.
  20844.                                 xValue = this.parseValue()
  20845.                                 if not empty(this.cError)
  20846.                                         return null
  20847.                                 endif
  20848.                                 retval.add( xValue )
  20849.                                 if this.getToken()<>','
  20850.                                         exit
  20851.                                 endif
  20852.                                 this.nPos = this.nPos + 1
  20853.                         enddo
  20854.                         if this.getToken() <> ']'
  20855.                                 this.setError('Expected ] at the end of array')
  20856.                                 return null
  20857.                         endif
  20858.                 endif
  20859.                 this.nPos = this.nPos + 1
  20860.         return retval
  20861.        
  20862.  
  20863.         function parseString()
  20864.         local cRetVal, c
  20865.                 if this.getToken()<>'"'
  20866.                         this.setError('Expected "')
  20867.                         return ''
  20868.                 endif
  20869.                 this.nPos = this.nPos + 1       && Eat "
  20870.                 cRetVal = ''
  20871.                 do while .t.
  20872.                         c = this.getChar()
  20873.                         if c==''
  20874.                                 return ''
  20875.                         endif
  20876.                         if c == '"'
  20877.                                 this.nPos = this.nPos + 1
  20878.                                 exit
  20879.                         endif
  20880.                         if c == '\'
  20881.                                 this.nPos = this.nPos + 1
  20882.                                 if (this.nPos>this.nLen)
  20883.                                         this.setError('\\ at the end of input')
  20884.                                         return ''
  20885.                                 endif
  20886.                                 c = this.getChar()
  20887.                                 if c==''
  20888.                                         return ''
  20889.                                 endif
  20890.                                 do case
  20891.                                 case c=='"'
  20892.                                         c='"'
  20893.                                 case c=='\'
  20894.                                         c='\'
  20895.                                 case c=='/'
  20896.                                         c='/'
  20897.                                 case c=='b'
  20898.                                         c=chr(8)
  20899.                                 case c=='t'
  20900.                                         c=chr(9)
  20901.                                 case c=='n'
  20902.                                         c=chr(10)
  20903.                                 case c=='f'
  20904.                                         c=chr(12)
  20905.                                 case c=='r'
  20906.                                         c=chr(13)
  20907.                                 otherwise
  20908.                                         ******* FALTAN LOS UNICODE
  20909.                                         this.setError('Invalid escape sequence in string literal')
  20910.                                         return ''
  20911.                                 endcase
  20912.                         endif
  20913.                         cRetVal = cRetVal + c
  20914.                         this.nPos = this.nPos + 1
  20915.                 enddo
  20916.         return cRetVal
  20917.                                        
  20918.  
  20919.         **** Pendiente numeros con E
  20920.         function parseNumber()
  20921.         local nStartPos,c, isInt, cNumero
  20922.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  20923.                         this.setError('Expected number literal')
  20924.                         return 0
  20925.                 endif
  20926.                 nStartPos = this.nPos
  20927.                 c = this.getChar()
  20928.                 if c == '-'
  20929.                         c = this.nextChar()
  20930.                 endif
  20931.                 if c == '0'
  20932.                         c = this.nextChar()
  20933.                 else
  20934.                         if isdigit(c)
  20935.                                 c = this.nextChar()
  20936.                                 do while isdigit(c)
  20937.                                         c = this.nextChar()
  20938.                                 enddo
  20939.                         else
  20940.                                 this.setError('Expected digit when parsing number')
  20941.                                 return 0
  20942.                         endif
  20943.                 endif
  20944.                
  20945.                 isInt = .t.
  20946.                 if c=='.'
  20947.                         c = this.nextChar()
  20948.                         if isdigit(c)
  20949.                                 c = this.nextChar()
  20950.                                 isInt = .f.
  20951.                                 do while isDigit(c)
  20952.                                         c = this.nextChar()
  20953.                                 enddo
  20954.                         else
  20955.                                 this.setError('Expected digit following dot comma')
  20956.                                 return 0
  20957.                         endif
  20958.                 endif
  20959.                
  20960.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  20961.         return val(cNumero)
  20962.  
  20963.  
  20964.  
  20965.         function getToken()
  20966.         local char1
  20967.                 do while .t.
  20968.                         if this.nPos > this.nLen
  20969.                                 return null
  20970.                         endif
  20971.                         char1 = substr(this.cJson, this.nPos, 1)
  20972.                         if char1==' '
  20973.                                 this.nPos = this.nPos + 1
  20974.                                 loop
  20975.                         endif
  20976.                         return char1
  20977.                 enddo
  20978.         return
  20979.        
  20980.                
  20981.                
  20982.         function getChar()
  20983.                 if this.nPos > this.nLen
  20984.                         this.setError('Unexpected end of JSON stream')
  20985.                         return ''
  20986.                 endif
  20987.         return substr(this.cJson, this.nPos, 1)
  20988.        
  20989.         function nextChar()
  20990.                 this.nPos = this.nPos + 1
  20991.                 if this.nPos > this.nLen
  20992.                         return ''
  20993.                 endif
  20994.         return substr(this.cJson, this.nPos, 1)
  20995.        
  20996.         function setError(cMsg)
  20997.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  20998.         return
  20999.  
  21000.  
  21001.         function fixUnicode(cStr)
  21002.                 cStr = StrTran(cStr,'\u00e1','á')
  21003.                 cStr = StrTran(cStr,'\u00e9','é')
  21004.                 cStr = StrTran(cStr,'\u00ed','í')
  21005.                 cStr = StrTran(cStr,'\u00f3','ó')
  21006.                 cStr = StrTran(cStr,'\u00fa','ú')
  21007.                 cStr = StrTran(cStr,'\u00c1','Á')
  21008.                 cStr = StrTran(cStr,'\u00c9','É')
  21009.                 cStr = StrTran(cStr,'\u00cd','Í')
  21010.                 cStr = StrTran(cStr,'\u00d3','Ó')
  21011.                 cStr = StrTran(cStr,'\u00da','Ú')
  21012.                 cStr = StrTran(cStr,'\u00f1','ń')
  21013.                 cStr = StrTran(cStr,'\u00d1','Ń')
  21014.         return cStr
  21015.  
  21016.  
  21017.  
  21018. enddefine
  21019.  
  21020.  
  21021.  
  21022.  
  21023.  
  21024. *
  21025. * class used to return an array
  21026. *
  21027. define class myArray as custom
  21028.         nSize = 0
  21029.         dimension array[1]
  21030.  
  21031.         function add(xExpr)
  21032.                 this.nSize = this.nSize + 1
  21033.                 dimension this.array[this.nSize]
  21034.                 this.array[this.nSize] = xExpr
  21035.         return
  21036.  
  21037.         function get(n)
  21038.         return this.array[n]
  21039.  
  21040. enddefine
  21041.  
  21042.  
  21043.  
  21044. *
  21045. * class used to simulate an object
  21046. * all properties are prefixed with 'prop' to permit property names like: error, init
  21047. * that already exists like vfp methods
  21048. *
  21049. define class myObj as custom
  21050. Hidden ;
  21051.         ClassLibrary,Comment, ;
  21052.         BaseClass,ControlCount, ;
  21053.         Controls,Objects,Object,;
  21054.         Height,HelpContextID,Left,Name, ;
  21055.         Parent,ParentClass,Picture, ;
  21056.         Tag,Top,WhatsThisHelpID,Width
  21057.                
  21058.         function set(cPropName, xValue)
  21059.                 cPropName = '_'+cPropName
  21060.                 if type('this.'+cPropName)=='U'
  21061.                         this.addProperty(cPropName,xValue)
  21062.                 else
  21063.                         local cmd
  21064.                         cmd = 'this.'+cPropName+'=xValue'
  21065.                         &cmd
  21066.                 endif
  21067.         return
  21068.        
  21069.         procedure get (cPropName)
  21070.                 cPropName = '_'+cPropName
  21071.                 If type('this.'+cPropName)=='U'
  21072.                         return ''
  21073.                 Else
  21074.                         local cmd
  21075.                         cmd = 'return this.'+cPropName
  21076.                         &cmd
  21077.                 endif
  21078.         return ''
  21079. enddefine
  21080.  
  21081.  
  21082.  
  21083.  
  21084.  
  21085. function testJsonClass
  21086.         clear
  21087.         set decimal to 10
  21088.         oJson = newObject('json')
  21089.        
  21090.        
  21091.         ? 'Test Basic Types'
  21092.         ? '----------------'
  21093.         ? oJson.decode('null')
  21094.         ? oJson.decode('true')
  21095.         ? oJson.decode('false')
  21096.         ?
  21097.         ? oJson.decode('791123')
  21098.         ? oJson.decode('791123.45')
  21099.         ? oJson.decode('791123.45.')
  21100.         ? oJson.decode('"nacho gtz"')
  21101.         if not empty(oJson.cError)
  21102.                 ? oJson.cError
  21103.                 return
  21104.         endif
  21105.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  21106.         if not empty(oJson.cError)
  21107.                 ? oJson.cError
  21108.                 return
  21109.         endif
  21110.        
  21111.         ? 'Test Array'
  21112.         ? '----------'
  21113.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  21114.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  21115.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  21116.         nombres = arr.get(1)
  21117.         edades  = arr.get(2)
  21118.         ? nombres.get(1), edades.get(1)
  21119.         ? nombres.get(2), edades.get(2)
  21120.         ? nombres.get(3), edades.get(3)
  21121.         ?
  21122.         ? 'Test Object'
  21123.         ? '-----------'
  21124.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  21125.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  21126.         ? obj._Nombre, obj._Edad, obj._IsGood
  21127.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  21128.         ? obj.get('jsonrpc'), obj._jsonrpc
  21129.         ? obj.get('id'), obj._id
  21130.         ? obj.get('method'), obj._method
  21131.         ? obj._Params.array[1], obj._Params.get(1)
  21132.  
  21133.         ?
  21134.         ? 'Test nested object'
  21135.         ? '------------------'
  21136.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  21137.         obj = oJson.decode(cJson)
  21138.         if not empty(oJson.cError)
  21139.                 ? oJson.cError
  21140.                 return
  21141.         endif
  21142.         ? cJson
  21143.         ? 'method -->',obj._method
  21144.         ? 'usrkey -->',obj._params._data._usrkey
  21145.         ? 'sendto -->',obj._params._data._sendto
  21146.         ? 'name  --->',obj._params._data._name
  21147.         ? 'expires ->',obj._params._data._expires
  21148.  
  21149.         ?
  21150.         ? 'Test empty object'
  21151.         ? '-----------------'
  21152.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  21153.         obj = oJson.decode(cJson)
  21154.         if not empty(oJson.cError)     
  21155.                 ? oJson.cError
  21156.                 return
  21157.         endif
  21158.         ? cJson
  21159.         ? 'result -->',obj._result, obj.get('result')
  21160.         oError = obj.get('error')
  21161.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  21162.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  21163.         ? 'id  ----->',obj._id, obj.get('id')
  21164.         ?  type("oError._code")
  21165.  
  21166.         ?
  21167.         ? 'Probar decode-enconde-decode-encode'
  21168.         ? '------------------------------------'
  21169.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  21170.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  21171.         ? cJson
  21172.         oSmtp = json_decode(cJson)
  21173.         cJson =  json_encode(oSmtp)
  21174.         ? cJson
  21175.         oSmtp = json_decode(cJson)
  21176.         cJson =  json_encode(oSmtp)
  21177.         ? cJson
  21178.  
  21179.         * Probar falla
  21180.         ?
  21181.         ? 'Probar una falla en el json'
  21182.         ? '---------------------------'
  21183.         cJson = ' {"server":"", "user":"", "password":"" ,'
  21184.         oSmtp = json_decode(cJson)
  21185.         if not empty(json_getErrorMsg())
  21186.                 ? json_getErrorMsg()
  21187.         endif
  21188.  
  21189.         ?
  21190.         ? 'Pruebas Finalizadas'
  21191. retur
  21192. *       oCustomer = oJson.decode( ' { "name":"Ignacio" , "lastname":"Gutierrez", "age":33 } ')
  21193. *       ? oJson.encode(oCustomer)
  21194. *       ? oCustomer.get('name')
  21195. *       ? oCustomer.get('lastname')
  21196. *
  21197. *
  21198. lRunTest = .f.
  21199. if lRunTest
  21200.         testJsonClass()
  21201. endif
  21202. return
  21203.  
  21204.  
  21205. function json_encode(xExpr)
  21206.         if vartype(_json)<>'O'
  21207.                 public _json
  21208.                 _json = newobject('json')
  21209.         endif
  21210. return _json.encode(@xExpr)
  21211.  
  21212.  
  21213. function json_decode(cJson)
  21214. local retval
  21215.         if vartype(_json)<>'O'
  21216.                 public _json
  21217.                 _json = newobject('json')
  21218.         endif
  21219.         retval = _json.decode(cJson)
  21220.         if not empty(_json.cError)
  21221.                 return null
  21222.         endif
  21223. return retval
  21224.  
  21225. function json_getErrorMsg()
  21226. return _json.cError
  21227.        
  21228.  
  21229.  
  21230. *
  21231. * json class
  21232. *
  21233. *
  21234. define class json as custom
  21235.  
  21236.  
  21237.         nPos=0
  21238.         nLen=0
  21239.         cJson=''
  21240.         cError=''
  21241.  
  21242.  
  21243.         *
  21244.         * Genera el codigo cJson para parametro que se manda
  21245.         *
  21246.         function encode(xExpr)
  21247.         local cTipo
  21248.                 * Cuando se manda una arreglo,
  21249.                 if type('ALen(xExpr)')=='N'
  21250.                         cTipo = 'A'
  21251.                 Else
  21252.                         cTipo = VarType(xExpr)
  21253.                 Endif
  21254.                
  21255.                 Do Case
  21256.                 Case cTipo=='D'
  21257.                         return '"'+dtos(xExpr)+'"'
  21258.                 Case cTipo=='N'
  21259.                         return Transform(xExpr)
  21260.                 Case cTipo=='L'
  21261.                         return iif(xExpr,'true','false')
  21262.                 Case cTipo=='X'
  21263.                         return 'null'
  21264.                 Case cTipo=='C'
  21265.                         xExpr = allt(xExpr)
  21266.                         xExpr = StrTran(xExpr, '\', '\\' )
  21267.                         xExpr = StrTran(xExpr, '/', '\/' )
  21268.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  21269.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  21270.                         xExpr = StrTran(xExpr, '"', '\"' )
  21271.                         return '"'+xExpr+'"'
  21272.  
  21273.                 case cTipo=='O'
  21274.                         local cProp, cJsonValue, cRetVal, aProp[1]
  21275.                         =AMembers(aProp,xExpr)
  21276.                         cRetVal = ''
  21277.                         for each cProp in aProp
  21278.                                 *? cProp
  21279.                                 *? cRetVal
  21280.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  21281.                                         * algunas propiedades pueden no estar definidas
  21282.                                         * como: activecontrol, parent, etc
  21283.                                         loop
  21284.                                 endif
  21285.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  21286.                                         *
  21287.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  21288.                                         *
  21289.                                         Local i,nTotElem
  21290.                                         cJsonValue = ''
  21291.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  21292.                                         For i=1 to nTotElem
  21293.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  21294.                                         Next
  21295.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  21296.                                 else
  21297.                                         *
  21298.                                         * es otro tipo de dato normal C, N, L
  21299.                                         *
  21300.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  21301.                                 endif
  21302.                                 if left(cProp,1)=='_'
  21303.                                         cProp = substr(cProp,2)
  21304.                                 endif
  21305.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  21306.                         next
  21307.                         return '{' + substr(cRetVal,2) + '}'
  21308.  
  21309.                 case cTipo=='A'
  21310.                         local valor, cRetVal
  21311.                         cRetVal = ''   
  21312.                         for each valor in xExpr
  21313.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  21314.                         next
  21315.                         return  '[' + substr(cRetVal,2) + ']'
  21316.                        
  21317.                 endcase
  21318.  
  21319.         return ''
  21320.  
  21321.  
  21322.  
  21323.  
  21324.  
  21325.         *
  21326.         * regresa un elemento representado por la cadena json que se manda
  21327.         *
  21328.        
  21329.         function decode(cJson)
  21330.         local retValue
  21331.                 cJson = StrTran(cJson,chr(9),'')
  21332.                 cJson = StrTran(cJson,chr(10),'')
  21333.                 cJson = StrTran(cJson,chr(13),'')
  21334.                 cJson = this.fixUnicode(cJson)
  21335.                 this.nPos  = 1
  21336.                 this.cJson = cJson
  21337.                 this.nLen  = len(cJson)
  21338.                 this.cError = ''
  21339.                 retValue = this.parsevalue()
  21340.                 if not empty(this.cError)
  21341.                         return null
  21342.                 endif
  21343.                 if this.getToken()<>null
  21344.                         this.setError('Junk at the end of JSON input')
  21345.                         return null
  21346.                 endif
  21347.         return retValue
  21348.                
  21349.        
  21350.         function parseValue()
  21351.         local token
  21352.                 token = this.getToken()
  21353.                 if token==null
  21354.                         this.setError('Nothing to parse')
  21355.                         return null
  21356.                 endif
  21357.                 do case
  21358.                 case token=='"'
  21359.                         return this.parseString()
  21360.                 case isdigit(token) or token=='-'
  21361.                         return this.parseNumber()
  21362.                 case token=='n'
  21363.                         return this.expectedKeyword('null',null)
  21364.                 case token=='f'
  21365.                         return this.expectedKeyword('false',.f.)
  21366.                 case token=='t'
  21367.                         return this.expectedKeyword('true',.t.)
  21368.                 case token=='{'
  21369.                         return this.parseObject()
  21370.                 case token=='['
  21371.                         return this.parseArray()
  21372.                 otherwise
  21373.                         this.setError('Unexpected token')
  21374.                 endcase
  21375.         return
  21376.                
  21377.        
  21378.         function expectedKeyword(cWord,eValue)
  21379.                 for i=1 to len(cWord)
  21380.                         cChar = this.getChar()
  21381.                         if cChar <> substr(cWord,i,1)
  21382.                                 this.setError("Expected keyword '" + cWord + "'")
  21383.                                 return ''
  21384.                         endif
  21385.                         this.nPos = this.nPos + 1
  21386.                 next
  21387.         return eValue
  21388.        
  21389.  
  21390.         function parseObject()
  21391.         local retval, cPropName, xValue
  21392.                 retval = createObject('myObj')
  21393.                 this.nPos = this.nPos + 1 && Eat {
  21394.                 if this.getToken()<>'}'
  21395.                         do while .t.
  21396.                                 cPropName = this.parseString()
  21397.                                 if not empty(this.cError)
  21398.                                         return null
  21399.                                 endif
  21400.                                 if this.getToken()<>':'
  21401.                                         this.setError("Expected ':' when parsing object")
  21402.                                         return null
  21403.                                 endif
  21404.                                 this.nPos = this.nPos + 1
  21405.                                 xValue = this.parseValue()
  21406.                                 if not empty(this.cError)
  21407.                                         return null
  21408.                                 endif                          
  21409.                                 ** Debug ? cPropName, type('xValue')
  21410.                                 retval.set(cPropName, xValue)
  21411.                                 if this.getToken()<>','
  21412.                                         exit
  21413.                                 endif
  21414.                                 this.nPos = this.nPos + 1
  21415.                         enddo
  21416.                 endif
  21417.                 if this.getToken()<>'}'
  21418.                         this.setError("Expected '}' at the end of object")
  21419.                         return null
  21420.                 endif
  21421.                 this.nPos = this.nPos + 1
  21422.         return retval
  21423.  
  21424.  
  21425.         function parseArray()
  21426.         local retVal, xValue
  21427.                 retval = createObject('MyArray')
  21428.                 this.nPos = this.nPos + 1       && Eat [
  21429.                 if this.getToken() <> ']'
  21430.                         do while .t.
  21431.                                 xValue = this.parseValue()
  21432.                                 if not empty(this.cError)
  21433.                                         return null
  21434.                                 endif
  21435.                                 retval.add( xValue )
  21436.                                 if this.getToken()<>','
  21437.                                         exit
  21438.                                 endif
  21439.                                 this.nPos = this.nPos + 1
  21440.                         enddo
  21441.                         if this.getToken() <> ']'
  21442.                                 this.setError('Expected ] at the end of array')
  21443.                                 return null
  21444.                         endif
  21445.                 endif
  21446.                 this.nPos = this.nPos + 1
  21447.         return retval
  21448.        
  21449.  
  21450.         function parseString()
  21451.         local cRetVal, c
  21452.                 if this.getToken()<>'"'
  21453.                         this.setError('Expected "')
  21454.                         return ''
  21455.                 endif
  21456.                 this.nPos = this.nPos + 1       && Eat "
  21457.                 cRetVal = ''
  21458.                 do while .t.
  21459.                         c = this.getChar()
  21460.                         if c==''
  21461.                                 return ''
  21462.                         endif
  21463.                         if c == '"'
  21464.                                 this.nPos = this.nPos + 1
  21465.                                 exit
  21466.                         endif
  21467.                         if c == '\'
  21468.                                 this.nPos = this.nPos + 1
  21469.                                 if (this.nPos>this.nLen)
  21470.                                         this.setError('\\ at the end of input')
  21471.                                         return ''
  21472.                                 endif
  21473.                                 c = this.getChar()
  21474.                                 if c==''
  21475.                                         return ''
  21476.                                 endif
  21477.                                 do case
  21478.                                 case c=='"'
  21479.                                         c='"'
  21480.                                 case c=='\'
  21481.                                         c='\'
  21482.                                 case c=='/'
  21483.                                         c='/'
  21484.                                 case c=='b'
  21485.                                         c=chr(8)
  21486.                                 case c=='t'
  21487.                                         c=chr(9)
  21488.                                 case c=='n'
  21489.                                         c=chr(10)
  21490.                                 case c=='f'
  21491.                                         c=chr(12)
  21492.                                 case c=='r'
  21493.                                         c=chr(13)
  21494.                                 otherwise
  21495.                                         ******* FALTAN LOS UNICODE
  21496.                                         this.setError('Invalid escape sequence in string literal')
  21497.                                         return ''
  21498.                                 endcase
  21499.                         endif
  21500.                         cRetVal = cRetVal + c
  21501.                         this.nPos = this.nPos + 1
  21502.                 enddo
  21503.         return cRetVal
  21504.                                        
  21505.  
  21506.         **** Pendiente numeros con E
  21507.         function parseNumber()
  21508.         local nStartPos,c, isInt, cNumero
  21509.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  21510.                         this.setError('Expected number literal')
  21511.                         return 0
  21512.                 endif
  21513.                 nStartPos = this.nPos
  21514.                 c = this.getChar()
  21515.                 if c == '-'
  21516.                         c = this.nextChar()
  21517.                 endif
  21518.                 if c == '0'
  21519.                         c = this.nextChar()
  21520.                 else
  21521.                         if isdigit(c)
  21522.                                 c = this.nextChar()
  21523.                                 do while isdigit(c)
  21524.                                         c = this.nextChar()
  21525.                                 enddo
  21526.                         else
  21527.                                 this.setError('Expected digit when parsing number')
  21528.                                 return 0
  21529.                         endif
  21530.                 endif
  21531.                
  21532.                 isInt = .t.
  21533.                 if c=='.'
  21534.                         c = this.nextChar()
  21535.                         if isdigit(c)
  21536.                                 c = this.nextChar()
  21537.                                 isInt = .f.
  21538.                                 do while isDigit(c)
  21539.                                         c = this.nextChar()
  21540.                                 enddo
  21541.                         else
  21542.                                 this.setError('Expected digit following dot comma')
  21543.                                 return 0
  21544.                         endif
  21545.                 endif
  21546.                
  21547.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  21548.         return val(cNumero)
  21549.  
  21550.  
  21551.  
  21552.         function getToken()
  21553.         local char1
  21554.                 do while .t.
  21555.                         if this.nPos > this.nLen
  21556.                                 return null
  21557.                         endif
  21558.                         char1 = substr(this.cJson, this.nPos, 1)
  21559.                         if char1==' '
  21560.                                 this.nPos = this.nPos + 1
  21561.                                 loop
  21562.                         endif
  21563.                         return char1
  21564.                 enddo
  21565.         return
  21566.        
  21567.                
  21568.                
  21569.         function getChar()
  21570.                 if this.nPos > this.nLen
  21571.                         this.setError('Unexpected end of JSON stream')
  21572.                         return ''
  21573.                 endif
  21574.         return substr(this.cJson, this.nPos, 1)
  21575.        
  21576.         function nextChar()
  21577.                 this.nPos = this.nPos + 1
  21578.                 if this.nPos > this.nLen
  21579.                         return ''
  21580.                 endif
  21581.         return substr(this.cJson, this.nPos, 1)
  21582.        
  21583.         function setError(cMsg)
  21584.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  21585.         return
  21586.  
  21587.  
  21588.         function fixUnicode(cStr)
  21589.                 cStr = StrTran(cStr,'\u00e1','á')
  21590.                 cStr = StrTran(cStr,'\u00e9','é')
  21591.                 cStr = StrTran(cStr,'\u00ed','í')
  21592.                 cStr = StrTran(cStr,'\u00f3','ó')
  21593.                 cStr = StrTran(cStr,'\u00fa','ú')
  21594.                 cStr = StrTran(cStr,'\u00c1','Á')
  21595.                 cStr = StrTran(cStr,'\u00c9','É')
  21596.                 cStr = StrTran(cStr,'\u00cd','Í')
  21597.                 cStr = StrTran(cStr,'\u00d3','Ó')
  21598.                 cStr = StrTran(cStr,'\u00da','Ú')
  21599.                 cStr = StrTran(cStr,'\u00f1','ń')
  21600.                 cStr = StrTran(cStr,'\u00d1','Ń')
  21601.         return cStr
  21602.  
  21603.  
  21604.  
  21605. enddefine
  21606.  
  21607.  
  21608.  
  21609.  
  21610.  
  21611. *
  21612. * class used to return an array
  21613. *
  21614. define class myArray as custom
  21615.         nSize = 0
  21616.         dimension array[1]
  21617.  
  21618.         function add(xExpr)
  21619.                 this.nSize = this.nSize + 1
  21620.                 dimension this.array[this.nSize]
  21621.                 this.array[this.nSize] = xExpr
  21622.         return
  21623.  
  21624.         function get(n)
  21625.         return this.array[n]
  21626.  
  21627. enddefine
  21628.  
  21629.  
  21630.  
  21631. *
  21632. * class used to simulate an object
  21633. * all properties are prefixed with 'prop' to permit property names like: error, init
  21634. * that already exists like vfp methods
  21635. *
  21636. define class myObj as custom
  21637. Hidden ;
  21638.         ClassLibrary,Comment, ;
  21639.         BaseClass,ControlCount, ;
  21640.         Controls,Objects,Object,;
  21641.         Height,HelpContextID,Left,Name, ;
  21642.         Parent,ParentClass,Picture, ;
  21643.         Tag,Top,WhatsThisHelpID,Width
  21644.                
  21645.         function set(cPropName, xValue)
  21646.                 cPropName = '_'+cPropName
  21647.                 if type('this.'+cPropName)=='U'
  21648.                         this.addProperty(cPropName,xValue)
  21649.                 else
  21650.                         local cmd
  21651.                         cmd = 'this.'+cPropName+'=xValue'
  21652.                         &cmd
  21653.                 endif
  21654.         return
  21655.        
  21656.         procedure get (cPropName)
  21657.                 cPropName = '_'+cPropName
  21658.                 If type('this.'+cPropName)=='U'
  21659.                         return ''
  21660.                 Else
  21661.                         local cmd
  21662.                         cmd = 'return this.'+cPropName
  21663.                         &cmd
  21664.                 endif
  21665.         return ''
  21666. enddefine
  21667.  
  21668.  
  21669.  
  21670.  
  21671.  
  21672. function testJsonClass
  21673.         clear
  21674.         set decimal to 10
  21675.         oJson = newObject('json')
  21676.        
  21677.        
  21678.         ? 'Test Basic Types'
  21679.         ? '----------------'
  21680.         ? oJson.decode('null')
  21681.         ? oJson.decode('true')
  21682.         ? oJson.decode('false')
  21683.         ?
  21684.         ? oJson.decode('791123')
  21685.         ? oJson.decode('791123.45')
  21686.         ? oJson.decode('791123.45.')
  21687.         ? oJson.decode('"nacho gtz"')
  21688.         if not empty(oJson.cError)
  21689.                 ? oJson.cError
  21690.                 return
  21691.         endif
  21692.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  21693.         if not empty(oJson.cError)
  21694.                 ? oJson.cError
  21695.                 return
  21696.         endif
  21697.        
  21698.         ? 'Test Array'
  21699.         ? '----------'
  21700.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  21701.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  21702.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  21703.         nombres = arr.get(1)
  21704.         edades  = arr.get(2)
  21705.         ? nombres.get(1), edades.get(1)
  21706.         ? nombres.get(2), edades.get(2)
  21707.         ? nombres.get(3), edades.get(3)
  21708.         ?
  21709.         ? 'Test Object'
  21710.         ? '-----------'
  21711.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  21712.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  21713.         ? obj._Nombre, obj._Edad, obj._IsGood
  21714.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  21715.         ? obj.get('jsonrpc'), obj._jsonrpc
  21716.         ? obj.get('id'), obj._id
  21717.         ? obj.get('method'), obj._method
  21718.         ? obj._Params.array[1], obj._Params.get(1)
  21719.  
  21720.         ?
  21721.         ? 'Test nested object'
  21722.         ? '------------------'
  21723.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  21724.         obj = oJson.decode(cJson)
  21725.         if not empty(oJson.cError)
  21726.                 ? oJson.cError
  21727.                 return
  21728.         endif
  21729.         ? cJson
  21730.         ? 'method -->',obj._method
  21731.         ? 'usrkey -->',obj._params._data._usrkey
  21732.         ? 'sendto -->',obj._params._data._sendto
  21733.         ? 'name  --->',obj._params._data._name
  21734.         ? 'expires ->',obj._params._data._expires
  21735.  
  21736.         ?
  21737.         ? 'Test empty object'
  21738.         ? '-----------------'
  21739.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  21740.         obj = oJson.decode(cJson)
  21741.         if not empty(oJson.cError)     
  21742.                 ? oJson.cError
  21743.                 return
  21744.         endif
  21745.         ? cJson
  21746.         ? 'result -->',obj._result, obj.get('result')
  21747.         oError = obj.get('error')
  21748.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  21749.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  21750.         ? 'id  ----->',obj._id, obj.get('id')
  21751.         ?  type("oError._code")
  21752.  
  21753.         ?
  21754.         ? 'Probar decode-enconde-decode-encode'
  21755.         ? '------------------------------------'
  21756.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  21757.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  21758.         ? cJson
  21759.         oSmtp = json_decode(cJson)
  21760.         cJson =  json_encode(oSmtp)
  21761.         ? cJson
  21762.         oSmtp = json_decode(cJson)
  21763.         cJson =  json_encode(oSmtp)
  21764.         ? cJson
  21765.  
  21766.         * Probar falla
  21767.         ?
  21768.         ? 'Probar una falla en el json'
  21769.         ? '---------------------------'
  21770.         cJson = ' {"server":"", "user":"", "password":"" ,'
  21771.         oSmtp = json_decode(cJson)
  21772.         if not empty(json_getErrorMsg())
  21773.                 ? json_getErrorMsg()
  21774.         endif
  21775.  
  21776.         ?
  21777.         ? 'Pruebas Finalizadas'
  21778. retur
  21779. *       ? oJson.encode(oCustomer)
  21780. *       ? oCustomer.get('name')
  21781. *       ? oCustomer.get('lastname')
  21782. *
  21783. *
  21784. lRunTest = .f.
  21785. if lRunTest
  21786.         testJsonClass()
  21787. endif
  21788. return
  21789.  
  21790.  
  21791. function json_encode(xExpr)
  21792.         if vartype(_json)<>'O'
  21793.                 public _json
  21794.                 _json = newobject('json')
  21795.         endif
  21796. return _json.encode(@xExpr)
  21797.  
  21798.  
  21799. function json_decode(cJson)
  21800. local retval
  21801.         if vartype(_json)<>'O'
  21802.                 public _json
  21803.                 _json = newobject('json')
  21804.         endif
  21805.         retval = _json.decode(cJson)
  21806.         if not empty(_json.cError)
  21807.                 return null
  21808.         endif
  21809. return retval
  21810.  
  21811. function json_getErrorMsg()
  21812. return _json.cError
  21813.        
  21814.  
  21815.  
  21816. *
  21817. * json class
  21818. *
  21819. *
  21820. define class json as custom
  21821.  
  21822.  
  21823.         nPos=0
  21824.         nLen=0
  21825.         cJson=''
  21826.         cError=''
  21827.  
  21828.  
  21829.         *
  21830.         * Genera el codigo cJson para parametro que se manda
  21831.         *
  21832.         function encode(xExpr)
  21833.         local cTipo
  21834.                 * Cuando se manda una arreglo,
  21835.                 if type('ALen(xExpr)')=='N'
  21836.                         cTipo = 'A'
  21837.                 Else
  21838.                         cTipo = VarType(xExpr)
  21839.                 Endif
  21840.                
  21841.                 Do Case
  21842.                 Case cTipo=='D'
  21843.                         return '"'+dtos(xExpr)+'"'
  21844.                 Case cTipo=='N'
  21845.                         return Transform(xExpr)
  21846.                 Case cTipo=='L'
  21847.                         return iif(xExpr,'true','false')
  21848.                 Case cTipo=='X'
  21849.                         return 'null'
  21850.                 Case cTipo=='C'
  21851.                         xExpr = allt(xExpr)
  21852.                         xExpr = StrTran(xExpr, '\', '\\' )
  21853.                         xExpr = StrTran(xExpr, '/', '\/' )
  21854.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  21855.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  21856.                         xExpr = StrTran(xExpr, '"', '\"' )
  21857.                         return '"'+xExpr+'"'
  21858.  
  21859.                 case cTipo=='O'
  21860.                         local cProp, cJsonValue, cRetVal, aProp[1]
  21861.                         =AMembers(aProp,xExpr)
  21862.                         cRetVal = ''
  21863.                         for each cProp in aProp
  21864.                                 *? cProp
  21865.                                 *? cRetVal
  21866.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  21867.                                         * algunas propiedades pueden no estar definidas
  21868.                                         * como: activecontrol, parent, etc
  21869.                                         loop
  21870.                                 endif
  21871.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  21872.                                         *
  21873.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  21874.                                         *
  21875.                                         Local i,nTotElem
  21876.                                         cJsonValue = ''
  21877.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  21878.                                         For i=1 to nTotElem
  21879.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  21880.                                         Next
  21881.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  21882.                                 else
  21883.                                         *
  21884.                                         * es otro tipo de dato normal C, N, L
  21885.                                         *
  21886.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  21887.                                 endif
  21888.                                 if left(cProp,1)=='_'
  21889.                                         cProp = substr(cProp,2)
  21890.                                 endif
  21891.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  21892.                         next
  21893.                         return '{' + substr(cRetVal,2) + '}'
  21894.  
  21895.                 case cTipo=='A'
  21896.                         local valor, cRetVal
  21897.                         cRetVal = ''   
  21898.                         for each valor in xExpr
  21899.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  21900.                         next
  21901.                         return  '[' + substr(cRetVal,2) + ']'
  21902.                        
  21903.                 endcase
  21904.  
  21905.         return ''
  21906.  
  21907.  
  21908.  
  21909.  
  21910.  
  21911.         *
  21912.         * regresa un elemento representado por la cadena json que se manda
  21913.         *
  21914.        
  21915.         function decode(cJson)
  21916.         local retValue
  21917.                 cJson = StrTran(cJson,chr(9),'')
  21918.                 cJson = StrTran(cJson,chr(10),'')
  21919.                 cJson = StrTran(cJson,chr(13),'')
  21920.                 cJson = this.fixUnicode(cJson)
  21921.                 this.nPos  = 1
  21922.                 this.cJson = cJson
  21923.                 this.nLen  = len(cJson)
  21924.                 this.cError = ''
  21925.                 retValue = this.parsevalue()
  21926.                 if not empty(this.cError)
  21927.                         return null
  21928.                 endif
  21929.                 if this.getToken()<>null
  21930.                         this.setError('Junk at the end of JSON input')
  21931.                         return null
  21932.                 endif
  21933.         return retValue
  21934.                
  21935.        
  21936.         function parseValue()
  21937.         local token
  21938.                 token = this.getToken()
  21939.                 if token==null
  21940.                         this.setError('Nothing to parse')
  21941.                         return null
  21942.                 endif
  21943.                 do case
  21944.                 case token=='"'
  21945.                         return this.parseString()
  21946.                 case isdigit(token) or token=='-'
  21947.                         return this.parseNumber()
  21948.                 case token=='n'
  21949.                         return this.expectedKeyword('null',null)
  21950.                 case token=='f'
  21951.                         return this.expectedKeyword('false',.f.)
  21952.                 case token=='t'
  21953.                         return this.expectedKeyword('true',.t.)
  21954.                 case token=='{'
  21955.                         return this.parseObject()
  21956.                 case token=='['
  21957.                         return this.parseArray()
  21958.                 otherwise
  21959.                         this.setError('Unexpected token')
  21960.                 endcase
  21961.         return
  21962.                
  21963.        
  21964.         function expectedKeyword(cWord,eValue)
  21965.                 for i=1 to len(cWord)
  21966.                         cChar = this.getChar()
  21967.                         if cChar <> substr(cWord,i,1)
  21968.                                 this.setError("Expected keyword '" + cWord + "'")
  21969.                                 return ''
  21970.                         endif
  21971.                         this.nPos = this.nPos + 1
  21972.                 next
  21973.         return eValue
  21974.        
  21975.  
  21976.         function parseObject()
  21977.         local retval, cPropName, xValue
  21978.                 retval = createObject('myObj')
  21979.                 this.nPos = this.nPos + 1 && Eat {
  21980.                 if this.getToken()<>'}'
  21981.                         do while .t.
  21982.                                 cPropName = this.parseString()
  21983.                                 if not empty(this.cError)
  21984.                                         return null
  21985.                                 endif
  21986.                                 if this.getToken()<>':'
  21987.                                         this.setError("Expected ':' when parsing object")
  21988.                                         return null
  21989.                                 endif
  21990.                                 this.nPos = this.nPos + 1
  21991.                                 xValue = this.parseValue()
  21992.                                 if not empty(this.cError)
  21993.                                         return null
  21994.                                 endif                          
  21995.                                 ** Debug ? cPropName, type('xValue')
  21996.                                 retval.set(cPropName, xValue)
  21997.                                 if this.getToken()<>','
  21998.                                         exit
  21999.                                 endif
  22000.                                 this.nPos = this.nPos + 1
  22001.                         enddo
  22002.                 endif
  22003.                 if this.getToken()<>'}'
  22004.                         this.setError("Expected '}' at the end of object")
  22005.                         return null
  22006.                 endif
  22007.                 this.nPos = this.nPos + 1
  22008.         return retval
  22009.  
  22010.  
  22011.         function parseArray()
  22012.         local retVal, xValue
  22013.                 retval = createObject('MyArray')
  22014.                 this.nPos = this.nPos + 1       && Eat [
  22015.                 if this.getToken() <> ']'
  22016.                         do while .t.
  22017.                                 xValue = this.parseValue()
  22018.                                 if not empty(this.cError)
  22019.                                         return null
  22020.                                 endif
  22021.                                 retval.add( xValue )
  22022.                                 if this.getToken()<>','
  22023.                                         exit
  22024.                                 endif
  22025.                                 this.nPos = this.nPos + 1
  22026.                         enddo
  22027.                         if this.getToken() <> ']'
  22028.                                 this.setError('Expected ] at the end of array')
  22029.                                 return null
  22030.                         endif
  22031.                 endif
  22032.                 this.nPos = this.nPos + 1
  22033.         return retval
  22034.        
  22035.  
  22036.         function parseString()
  22037.         local cRetVal, c
  22038.                 if this.getToken()<>'"'
  22039.                         this.setError('Expected "')
  22040.                         return ''
  22041.                 endif
  22042.                 this.nPos = this.nPos + 1       && Eat "
  22043.                 cRetVal = ''
  22044.                 do while .t.
  22045.                         c = this.getChar()
  22046.                         if c==''
  22047.                                 return ''
  22048.                         endif
  22049.                         if c == '"'
  22050.                                 this.nPos = this.nPos + 1
  22051.                                 exit
  22052.                         endif
  22053.                         if c == '\'
  22054.                                 this.nPos = this.nPos + 1
  22055.                                 if (this.nPos>this.nLen)
  22056.                                         this.setError('\\ at the end of input')
  22057.                                         return ''
  22058.                                 endif
  22059.                                 c = this.getChar()
  22060.                                 if c==''
  22061.                                         return ''
  22062.                                 endif
  22063.                                 do case
  22064.                                 case c=='"'
  22065.                                         c='"'
  22066.                                 case c=='\'
  22067.                                         c='\'
  22068.                                 case c=='/'
  22069.                                         c='/'
  22070.                                 case c=='b'
  22071.                                         c=chr(8)
  22072.                                 case c=='t'
  22073.                                         c=chr(9)
  22074.                                 case c=='n'
  22075.                                         c=chr(10)
  22076.                                 case c=='f'
  22077.                                         c=chr(12)
  22078.                                 case c=='r'
  22079.                                         c=chr(13)
  22080.                                 otherwise
  22081.                                         ******* FALTAN LOS UNICODE
  22082.                                         this.setError('Invalid escape sequence in string literal')
  22083.                                         return ''
  22084.                                 endcase
  22085.                         endif
  22086.                         cRetVal = cRetVal + c
  22087.                         this.nPos = this.nPos + 1
  22088.                 enddo
  22089.         return cRetVal
  22090.                                        
  22091.  
  22092.         **** Pendiente numeros con E
  22093.         function parseNumber()
  22094.         local nStartPos,c, isInt, cNumero
  22095.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  22096.                         this.setError('Expected number literal')
  22097.                         return 0
  22098.                 endif
  22099.                 nStartPos = this.nPos
  22100.                 c = this.getChar()
  22101.                 if c == '-'
  22102.                         c = this.nextChar()
  22103.                 endif
  22104.                 if c == '0'
  22105.                         c = this.nextChar()
  22106.                 else
  22107.                         if isdigit(c)
  22108.                                 c = this.nextChar()
  22109.                                 do while isdigit(c)
  22110.                                         c = this.nextChar()
  22111.                                 enddo
  22112.                         else
  22113.                                 this.setError('Expected digit when parsing number')
  22114.                                 return 0
  22115.                         endif
  22116.                 endif
  22117.                
  22118.                 isInt = .t.
  22119.                 if c=='.'
  22120.                         c = this.nextChar()
  22121.                         if isdigit(c)
  22122.                                 c = this.nextChar()
  22123.                                 isInt = .f.
  22124.                                 do while isDigit(c)
  22125.                                         c = this.nextChar()
  22126.                                 enddo
  22127.                         else
  22128.                                 this.setError('Expected digit following dot comma')
  22129.                                 return 0
  22130.                         endif
  22131.                 endif
  22132.                
  22133.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  22134.         return val(cNumero)
  22135.  
  22136.  
  22137.  
  22138.         function getToken()
  22139.         local char1
  22140.                 do while .t.
  22141.                         if this.nPos > this.nLen
  22142.                                 return null
  22143.                         endif
  22144.                         char1 = substr(this.cJson, this.nPos, 1)
  22145.                         if char1==' '
  22146.                                 this.nPos = this.nPos + 1
  22147.                                 loop
  22148.                         endif
  22149.                         return char1
  22150.                 enddo
  22151.         return
  22152.        
  22153.                
  22154.                
  22155.         function getChar()
  22156.                 if this.nPos > this.nLen
  22157.                         this.setError('Unexpected end of JSON stream')
  22158.                         return ''
  22159.                 endif
  22160.         return substr(this.cJson, this.nPos, 1)
  22161.        
  22162.         function nextChar()
  22163.                 this.nPos = this.nPos + 1
  22164.                 if this.nPos > this.nLen
  22165.                         return ''
  22166.                 endif
  22167.         return substr(this.cJson, this.nPos, 1)
  22168.        
  22169.         function setError(cMsg)
  22170.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  22171.         return
  22172.  
  22173.  
  22174.         function fixUnicode(cStr)
  22175.                 cStr = StrTran(cStr,'\u00e1','á')
  22176.                 cStr = StrTran(cStr,'\u00e9','é')
  22177.                 cStr = StrTran(cStr,'\u00ed','í')
  22178.                 cStr = StrTran(cStr,'\u00f3','ó')
  22179.                 cStr = StrTran(cStr,'\u00fa','ú')
  22180.                 cStr = StrTran(cStr,'\u00c1','Á')
  22181.                 cStr = StrTran(cStr,'\u00c9','É')
  22182.                 cStr = StrTran(cStr,'\u00cd','Í')
  22183.                 cStr = StrTran(cStr,'\u00d3','Ó')
  22184.                 cStr = StrTran(cStr,'\u00da','Ú')
  22185.                 cStr = StrTran(cStr,'\u00f1','ń')
  22186.                 cStr = StrTran(cStr,'\u00d1','Ń')
  22187.         return cStr
  22188.  
  22189.  
  22190.  
  22191. enddefine
  22192.  
  22193.  
  22194.  
  22195.  
  22196.  
  22197. *
  22198. * class used to return an array
  22199. *
  22200. define class myArray as custom
  22201.         nSize = 0
  22202.         dimension array[1]
  22203.  
  22204.         function add(xExpr)
  22205.                 this.nSize = this.nSize + 1
  22206.                 dimension this.array[this.nSize]
  22207.                 this.array[this.nSize] = xExpr
  22208.         return
  22209.  
  22210.         function get(n)
  22211.         return this.array[n]
  22212.  
  22213. enddefine
  22214.  
  22215.  
  22216.  
  22217. *
  22218. * class used to simulate an object
  22219. * all properties are prefixed with 'prop' to permit property names like: error, init
  22220. * that already exists like vfp methods
  22221. *
  22222. define class myObj as custom
  22223. Hidden ;
  22224.         ClassLibrary,Comment, ;
  22225.         BaseClass,ControlCount, ;
  22226.         Controls,Objects,Object,;
  22227.         Height,HelpContextID,Left,Name, ;
  22228.         Parent,ParentClass,Picture, ;
  22229.         Tag,Top,WhatsThisHelpID,Width
  22230.                
  22231.         function set(cPropName, xValue)
  22232.                 cPropName = '_'+cPropName
  22233.                 if type('this.'+cPropName)=='U'
  22234.                         this.addProperty(cPropName,xValue)
  22235.                 else
  22236.                         local cmd
  22237.                         cmd = 'this.'+cPropName+'=xValue'
  22238.                         &cmd
  22239.                 endif
  22240.         return
  22241.        
  22242.         procedure get (cPropName)
  22243.                 cPropName = '_'+cPropName
  22244.                 If type('this.'+cPropName)=='U'
  22245.                         return ''
  22246.                 Else
  22247.                         local cmd
  22248.                         cmd = 'return this.'+cPropName
  22249.                         &cmd
  22250.                 endif
  22251.         return ''
  22252. enddefine
  22253.  
  22254.  
  22255.  
  22256.  
  22257.  
  22258. function testJsonClass
  22259.         clear
  22260.         set decimal to 10
  22261.         oJson = newObject('json')
  22262.        
  22263.        
  22264.         ? 'Test Basic Types'
  22265.         ? '----------------'
  22266.         ? oJson.decode('null')
  22267.         ? oJson.decode('true')
  22268.         ? oJson.decode('false')
  22269.         ?
  22270.         ? oJson.decode('791123')
  22271.         ? oJson.decode('791123.45')
  22272.         ? oJson.decode('791123.45.')
  22273.         ? oJson.decode('"nacho gtz"')
  22274.         if not empty(oJson.cError)
  22275.                 ? oJson.cError
  22276.                 return
  22277.         endif
  22278.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  22279.         if not empty(oJson.cError)
  22280.                 ? oJson.cError
  22281.                 return
  22282.         endif
  22283.        
  22284.         ? 'Test Array'
  22285.         ? '----------'
  22286.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  22287.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  22288.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  22289.         nombres = arr.get(1)
  22290.         edades  = arr.get(2)
  22291.         ? nombres.get(1), edades.get(1)
  22292.         ? nombres.get(2), edades.get(2)
  22293.         ? nombres.get(3), edades.get(3)
  22294.         ?
  22295.         ? 'Test Object'
  22296.         ? '-----------'
  22297.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  22298.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  22299.         ? obj._Nombre, obj._Edad, obj._IsGood
  22300.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  22301.         ? obj.get('jsonrpc'), obj._jsonrpc
  22302.         ? obj.get('id'), obj._id
  22303.         ? obj.get('method'), obj._method
  22304.         ? obj._Params.array[1], obj._Params.get(1)
  22305.  
  22306.         ?
  22307.         ? 'Test nested object'
  22308.         ? '------------------'
  22309.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  22310.         obj = oJson.decode(cJson)
  22311.         if not empty(oJson.cError)
  22312.                 ? oJson.cError
  22313.                 return
  22314.         endif
  22315.         ? cJson
  22316.         ? 'method -->',obj._method
  22317.         ? 'usrkey -->',obj._params._data._usrkey
  22318.         ? 'sendto -->',obj._params._data._sendto
  22319.         ? 'name  --->',obj._params._data._name
  22320.         ? 'expires ->',obj._params._data._expires
  22321.  
  22322.         ?
  22323.         ? 'Test empty object'
  22324.         ? '-----------------'
  22325.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  22326.         obj = oJson.decode(cJson)
  22327.         if not empty(oJson.cError)     
  22328.                 ? oJson.cError
  22329.                 return
  22330.         endif
  22331.         ? cJson
  22332.         ? 'result -->',obj._result, obj.get('result')
  22333.         oError = obj.get('error')
  22334.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  22335.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  22336.         ? 'id  ----->',obj._id, obj.get('id')
  22337.         ?  type("oError._code")
  22338.  
  22339.         ?
  22340.         ? 'Probar decode-enconde-decode-encode'
  22341.         ? '------------------------------------'
  22342.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  22343.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  22344.         ? cJson
  22345.         oSmtp = json_decode(cJson)
  22346.         cJson =  json_encode(oSmtp)
  22347.         ? cJson
  22348.         oSmtp = json_decode(cJson)
  22349.         cJson =  json_encode(oSmtp)
  22350.         ? cJson
  22351.  
  22352.         * Probar falla
  22353.         ?
  22354.         ? 'Probar una falla en el json'
  22355.         ? '---------------------------'
  22356.         cJson = ' {"server":"", "user":"", "password":"" ,'
  22357.         oSmtp = json_decode(cJson)
  22358.         if not empty(json_getErrorMsg())
  22359.                 ? json_getErrorMsg()
  22360.         endif
  22361.  
  22362.         ?
  22363.         ? 'Pruebas Finalizadas'
  22364. retur
  22365. *       ? oCustomer.get('name')
  22366. *       ? oCustomer.get('lastname')
  22367. *
  22368. *
  22369. lRunTest = .f.
  22370. if lRunTest
  22371.         testJsonClass()
  22372. endif
  22373. return
  22374.  
  22375.  
  22376. function json_encode(xExpr)
  22377.         if vartype(_json)<>'O'
  22378.                 public _json
  22379.                 _json = newobject('json')
  22380.         endif
  22381. return _json.encode(@xExpr)
  22382.  
  22383.  
  22384. function json_decode(cJson)
  22385. local retval
  22386.         if vartype(_json)<>'O'
  22387.                 public _json
  22388.                 _json = newobject('json')
  22389.         endif
  22390.         retval = _json.decode(cJson)
  22391.         if not empty(_json.cError)
  22392.                 return null
  22393.         endif
  22394. return retval
  22395.  
  22396. function json_getErrorMsg()
  22397. return _json.cError
  22398.        
  22399.  
  22400.  
  22401. *
  22402. * json class
  22403. *
  22404. *
  22405. define class json as custom
  22406.  
  22407.  
  22408.         nPos=0
  22409.         nLen=0
  22410.         cJson=''
  22411.         cError=''
  22412.  
  22413.  
  22414.         *
  22415.         * Genera el codigo cJson para parametro que se manda
  22416.         *
  22417.         function encode(xExpr)
  22418.         local cTipo
  22419.                 * Cuando se manda una arreglo,
  22420.                 if type('ALen(xExpr)')=='N'
  22421.                         cTipo = 'A'
  22422.                 Else
  22423.                         cTipo = VarType(xExpr)
  22424.                 Endif
  22425.                
  22426.                 Do Case
  22427.                 Case cTipo=='D'
  22428.                         return '"'+dtos(xExpr)+'"'
  22429.                 Case cTipo=='N'
  22430.                         return Transform(xExpr)
  22431.                 Case cTipo=='L'
  22432.                         return iif(xExpr,'true','false')
  22433.                 Case cTipo=='X'
  22434.                         return 'null'
  22435.                 Case cTipo=='C'
  22436.                         xExpr = allt(xExpr)
  22437.                         xExpr = StrTran(xExpr, '\', '\\' )
  22438.                         xExpr = StrTran(xExpr, '/', '\/' )
  22439.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  22440.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  22441.                         xExpr = StrTran(xExpr, '"', '\"' )
  22442.                         return '"'+xExpr+'"'
  22443.  
  22444.                 case cTipo=='O'
  22445.                         local cProp, cJsonValue, cRetVal, aProp[1]
  22446.                         =AMembers(aProp,xExpr)
  22447.                         cRetVal = ''
  22448.                         for each cProp in aProp
  22449.                                 *? cProp
  22450.                                 *? cRetVal
  22451.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  22452.                                         * algunas propiedades pueden no estar definidas
  22453.                                         * como: activecontrol, parent, etc
  22454.                                         loop
  22455.                                 endif
  22456.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  22457.                                         *
  22458.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  22459.                                         *
  22460.                                         Local i,nTotElem
  22461.                                         cJsonValue = ''
  22462.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  22463.                                         For i=1 to nTotElem
  22464.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  22465.                                         Next
  22466.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  22467.                                 else
  22468.                                         *
  22469.                                         * es otro tipo de dato normal C, N, L
  22470.                                         *
  22471.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  22472.                                 endif
  22473.                                 if left(cProp,1)=='_'
  22474.                                         cProp = substr(cProp,2)
  22475.                                 endif
  22476.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  22477.                         next
  22478.                         return '{' + substr(cRetVal,2) + '}'
  22479.  
  22480.                 case cTipo=='A'
  22481.                         local valor, cRetVal
  22482.                         cRetVal = ''   
  22483.                         for each valor in xExpr
  22484.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  22485.                         next
  22486.                         return  '[' + substr(cRetVal,2) + ']'
  22487.                        
  22488.                 endcase
  22489.  
  22490.         return ''
  22491.  
  22492.  
  22493.  
  22494.  
  22495.  
  22496.         *
  22497.         * regresa un elemento representado por la cadena json que se manda
  22498.         *
  22499.        
  22500.         function decode(cJson)
  22501.         local retValue
  22502.                 cJson = StrTran(cJson,chr(9),'')
  22503.                 cJson = StrTran(cJson,chr(10),'')
  22504.                 cJson = StrTran(cJson,chr(13),'')
  22505.                 cJson = this.fixUnicode(cJson)
  22506.                 this.nPos  = 1
  22507.                 this.cJson = cJson
  22508.                 this.nLen  = len(cJson)
  22509.                 this.cError = ''
  22510.                 retValue = this.parsevalue()
  22511.                 if not empty(this.cError)
  22512.                         return null
  22513.                 endif
  22514.                 if this.getToken()<>null
  22515.                         this.setError('Junk at the end of JSON input')
  22516.                         return null
  22517.                 endif
  22518.         return retValue
  22519.                
  22520.        
  22521.         function parseValue()
  22522.         local token
  22523.                 token = this.getToken()
  22524.                 if token==null
  22525.                         this.setError('Nothing to parse')
  22526.                         return null
  22527.                 endif
  22528.                 do case
  22529.                 case token=='"'
  22530.                         return this.parseString()
  22531.                 case isdigit(token) or token=='-'
  22532.                         return this.parseNumber()
  22533.                 case token=='n'
  22534.                         return this.expectedKeyword('null',null)
  22535.                 case token=='f'
  22536.                         return this.expectedKeyword('false',.f.)
  22537.                 case token=='t'
  22538.                         return this.expectedKeyword('true',.t.)
  22539.                 case token=='{'
  22540.                         return this.parseObject()
  22541.                 case token=='['
  22542.                         return this.parseArray()
  22543.                 otherwise
  22544.                         this.setError('Unexpected token')
  22545.                 endcase
  22546.         return
  22547.                
  22548.        
  22549.         function expectedKeyword(cWord,eValue)
  22550.                 for i=1 to len(cWord)
  22551.                         cChar = this.getChar()
  22552.                         if cChar <> substr(cWord,i,1)
  22553.                                 this.setError("Expected keyword '" + cWord + "'")
  22554.                                 return ''
  22555.                         endif
  22556.                         this.nPos = this.nPos + 1
  22557.                 next
  22558.         return eValue
  22559.        
  22560.  
  22561.         function parseObject()
  22562.         local retval, cPropName, xValue
  22563.                 retval = createObject('myObj')
  22564.                 this.nPos = this.nPos + 1 && Eat {
  22565.                 if this.getToken()<>'}'
  22566.                         do while .t.
  22567.                                 cPropName = this.parseString()
  22568.                                 if not empty(this.cError)
  22569.                                         return null
  22570.                                 endif
  22571.                                 if this.getToken()<>':'
  22572.                                         this.setError("Expected ':' when parsing object")
  22573.                                         return null
  22574.                                 endif
  22575.                                 this.nPos = this.nPos + 1
  22576.                                 xValue = this.parseValue()
  22577.                                 if not empty(this.cError)
  22578.                                         return null
  22579.                                 endif                          
  22580.                                 ** Debug ? cPropName, type('xValue')
  22581.                                 retval.set(cPropName, xValue)
  22582.                                 if this.getToken()<>','
  22583.                                         exit
  22584.                                 endif
  22585.                                 this.nPos = this.nPos + 1
  22586.                         enddo
  22587.                 endif
  22588.                 if this.getToken()<>'}'
  22589.                         this.setError("Expected '}' at the end of object")
  22590.                         return null
  22591.                 endif
  22592.                 this.nPos = this.nPos + 1
  22593.         return retval
  22594.  
  22595.  
  22596.         function parseArray()
  22597.         local retVal, xValue
  22598.                 retval = createObject('MyArray')
  22599.                 this.nPos = this.nPos + 1       && Eat [
  22600.                 if this.getToken() <> ']'
  22601.                         do while .t.
  22602.                                 xValue = this.parseValue()
  22603.                                 if not empty(this.cError)
  22604.                                         return null
  22605.                                 endif
  22606.                                 retval.add( xValue )
  22607.                                 if this.getToken()<>','
  22608.                                         exit
  22609.                                 endif
  22610.                                 this.nPos = this.nPos + 1
  22611.                         enddo
  22612.                         if this.getToken() <> ']'
  22613.                                 this.setError('Expected ] at the end of array')
  22614.                                 return null
  22615.                         endif
  22616.                 endif
  22617.                 this.nPos = this.nPos + 1
  22618.         return retval
  22619.        
  22620.  
  22621.         function parseString()
  22622.         local cRetVal, c
  22623.                 if this.getToken()<>'"'
  22624.                         this.setError('Expected "')
  22625.                         return ''
  22626.                 endif
  22627.                 this.nPos = this.nPos + 1       && Eat "
  22628.                 cRetVal = ''
  22629.                 do while .t.
  22630.                         c = this.getChar()
  22631.                         if c==''
  22632.                                 return ''
  22633.                         endif
  22634.                         if c == '"'
  22635.                                 this.nPos = this.nPos + 1
  22636.                                 exit
  22637.                         endif
  22638.                         if c == '\'
  22639.                                 this.nPos = this.nPos + 1
  22640.                                 if (this.nPos>this.nLen)
  22641.                                         this.setError('\\ at the end of input')
  22642.                                         return ''
  22643.                                 endif
  22644.                                 c = this.getChar()
  22645.                                 if c==''
  22646.                                         return ''
  22647.                                 endif
  22648.                                 do case
  22649.                                 case c=='"'
  22650.                                         c='"'
  22651.                                 case c=='\'
  22652.                                         c='\'
  22653.                                 case c=='/'
  22654.                                         c='/'
  22655.                                 case c=='b'
  22656.                                         c=chr(8)
  22657.                                 case c=='t'
  22658.                                         c=chr(9)
  22659.                                 case c=='n'
  22660.                                         c=chr(10)
  22661.                                 case c=='f'
  22662.                                         c=chr(12)
  22663.                                 case c=='r'
  22664.                                         c=chr(13)
  22665.                                 otherwise
  22666.                                         ******* FALTAN LOS UNICODE
  22667.                                         this.setError('Invalid escape sequence in string literal')
  22668.                                         return ''
  22669.                                 endcase
  22670.                         endif
  22671.                         cRetVal = cRetVal + c
  22672.                         this.nPos = this.nPos + 1
  22673.                 enddo
  22674.         return cRetVal
  22675.                                        
  22676.  
  22677.         **** Pendiente numeros con E
  22678.         function parseNumber()
  22679.         local nStartPos,c, isInt, cNumero
  22680.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  22681.                         this.setError('Expected number literal')
  22682.                         return 0
  22683.                 endif
  22684.                 nStartPos = this.nPos
  22685.                 c = this.getChar()
  22686.                 if c == '-'
  22687.                         c = this.nextChar()
  22688.                 endif
  22689.                 if c == '0'
  22690.                         c = this.nextChar()
  22691.                 else
  22692.                         if isdigit(c)
  22693.                                 c = this.nextChar()
  22694.                                 do while isdigit(c)
  22695.                                         c = this.nextChar()
  22696.                                 enddo
  22697.                         else
  22698.                                 this.setError('Expected digit when parsing number')
  22699.                                 return 0
  22700.                         endif
  22701.                 endif
  22702.                
  22703.                 isInt = .t.
  22704.                 if c=='.'
  22705.                         c = this.nextChar()
  22706.                         if isdigit(c)
  22707.                                 c = this.nextChar()
  22708.                                 isInt = .f.
  22709.                                 do while isDigit(c)
  22710.                                         c = this.nextChar()
  22711.                                 enddo
  22712.                         else
  22713.                                 this.setError('Expected digit following dot comma')
  22714.                                 return 0
  22715.                         endif
  22716.                 endif
  22717.                
  22718.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  22719.         return val(cNumero)
  22720.  
  22721.  
  22722.  
  22723.         function getToken()
  22724.         local char1
  22725.                 do while .t.
  22726.                         if this.nPos > this.nLen
  22727.                                 return null
  22728.                         endif
  22729.                         char1 = substr(this.cJson, this.nPos, 1)
  22730.                         if char1==' '
  22731.                                 this.nPos = this.nPos + 1
  22732.                                 loop
  22733.                         endif
  22734.                         return char1
  22735.                 enddo
  22736.         return
  22737.        
  22738.                
  22739.                
  22740.         function getChar()
  22741.                 if this.nPos > this.nLen
  22742.                         this.setError('Unexpected end of JSON stream')
  22743.                         return ''
  22744.                 endif
  22745.         return substr(this.cJson, this.nPos, 1)
  22746.        
  22747.         function nextChar()
  22748.                 this.nPos = this.nPos + 1
  22749.                 if this.nPos > this.nLen
  22750.                         return ''
  22751.                 endif
  22752.         return substr(this.cJson, this.nPos, 1)
  22753.        
  22754.         function setError(cMsg)
  22755.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  22756.         return
  22757.  
  22758.  
  22759.         function fixUnicode(cStr)
  22760.                 cStr = StrTran(cStr,'\u00e1','á')
  22761.                 cStr = StrTran(cStr,'\u00e9','é')
  22762.                 cStr = StrTran(cStr,'\u00ed','í')
  22763.                 cStr = StrTran(cStr,'\u00f3','ó')
  22764.                 cStr = StrTran(cStr,'\u00fa','ú')
  22765.                 cStr = StrTran(cStr,'\u00c1','Á')
  22766.                 cStr = StrTran(cStr,'\u00c9','É')
  22767.                 cStr = StrTran(cStr,'\u00cd','Í')
  22768.                 cStr = StrTran(cStr,'\u00d3','Ó')
  22769.                 cStr = StrTran(cStr,'\u00da','Ú')
  22770.                 cStr = StrTran(cStr,'\u00f1','ń')
  22771.                 cStr = StrTran(cStr,'\u00d1','Ń')
  22772.         return cStr
  22773.  
  22774.  
  22775.  
  22776. enddefine
  22777.  
  22778.  
  22779.  
  22780.  
  22781.  
  22782. *
  22783. * class used to return an array
  22784. *
  22785. define class myArray as custom
  22786.         nSize = 0
  22787.         dimension array[1]
  22788.  
  22789.         function add(xExpr)
  22790.                 this.nSize = this.nSize + 1
  22791.                 dimension this.array[this.nSize]
  22792.                 this.array[this.nSize] = xExpr
  22793.         return
  22794.  
  22795.         function get(n)
  22796.         return this.array[n]
  22797.  
  22798. enddefine
  22799.  
  22800.  
  22801.  
  22802. *
  22803. * class used to simulate an object
  22804. * all properties are prefixed with 'prop' to permit property names like: error, init
  22805. * that already exists like vfp methods
  22806. *
  22807. define class myObj as custom
  22808. Hidden ;
  22809.         ClassLibrary,Comment, ;
  22810.         BaseClass,ControlCount, ;
  22811.         Controls,Objects,Object,;
  22812.         Height,HelpContextID,Left,Name, ;
  22813.         Parent,ParentClass,Picture, ;
  22814.         Tag,Top,WhatsThisHelpID,Width
  22815.                
  22816.         function set(cPropName, xValue)
  22817.                 cPropName = '_'+cPropName
  22818.                 if type('this.'+cPropName)=='U'
  22819.                         this.addProperty(cPropName,xValue)
  22820.                 else
  22821.                         local cmd
  22822.                         cmd = 'this.'+cPropName+'=xValue'
  22823.                         &cmd
  22824.                 endif
  22825.         return
  22826.        
  22827.         procedure get (cPropName)
  22828.                 cPropName = '_'+cPropName
  22829.                 If type('this.'+cPropName)=='U'
  22830.                         return ''
  22831.                 Else
  22832.                         local cmd
  22833.                         cmd = 'return this.'+cPropName
  22834.                         &cmd
  22835.                 endif
  22836.         return ''
  22837. enddefine
  22838.  
  22839.  
  22840.  
  22841.  
  22842.  
  22843. function testJsonClass
  22844.         clear
  22845.         set decimal to 10
  22846.         oJson = newObject('json')
  22847.        
  22848.        
  22849.         ? 'Test Basic Types'
  22850.         ? '----------------'
  22851.         ? oJson.decode('null')
  22852.         ? oJson.decode('true')
  22853.         ? oJson.decode('false')
  22854.         ?
  22855.         ? oJson.decode('791123')
  22856.         ? oJson.decode('791123.45')
  22857.         ? oJson.decode('791123.45.')
  22858.         ? oJson.decode('"nacho gtz"')
  22859.         if not empty(oJson.cError)
  22860.                 ? oJson.cError
  22861.                 return
  22862.         endif
  22863.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  22864.         if not empty(oJson.cError)
  22865.                 ? oJson.cError
  22866.                 return
  22867.         endif
  22868.        
  22869.         ? 'Test Array'
  22870.         ? '----------'
  22871.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  22872.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  22873.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  22874.         nombres = arr.get(1)
  22875.         edades  = arr.get(2)
  22876.         ? nombres.get(1), edades.get(1)
  22877.         ? nombres.get(2), edades.get(2)
  22878.         ? nombres.get(3), edades.get(3)
  22879.         ?
  22880.         ? 'Test Object'
  22881.         ? '-----------'
  22882.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  22883.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  22884.         ? obj._Nombre, obj._Edad, obj._IsGood
  22885.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  22886.         ? obj.get('jsonrpc'), obj._jsonrpc
  22887.         ? obj.get('id'), obj._id
  22888.         ? obj.get('method'), obj._method
  22889.         ? obj._Params.array[1], obj._Params.get(1)
  22890.  
  22891.         ?
  22892.         ? 'Test nested object'
  22893.         ? '------------------'
  22894.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  22895.         obj = oJson.decode(cJson)
  22896.         if not empty(oJson.cError)
  22897.                 ? oJson.cError
  22898.                 return
  22899.         endif
  22900.         ? cJson
  22901.         ? 'method -->',obj._method
  22902.         ? 'usrkey -->',obj._params._data._usrkey
  22903.         ? 'sendto -->',obj._params._data._sendto
  22904.         ? 'name  --->',obj._params._data._name
  22905.         ? 'expires ->',obj._params._data._expires
  22906.  
  22907.         ?
  22908.         ? 'Test empty object'
  22909.         ? '-----------------'
  22910.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  22911.         obj = oJson.decode(cJson)
  22912.         if not empty(oJson.cError)     
  22913.                 ? oJson.cError
  22914.                 return
  22915.         endif
  22916.         ? cJson
  22917.         ? 'result -->',obj._result, obj.get('result')
  22918.         oError = obj.get('error')
  22919.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  22920.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  22921.         ? 'id  ----->',obj._id, obj.get('id')
  22922.         ?  type("oError._code")
  22923.  
  22924.         ?
  22925.         ? 'Probar decode-enconde-decode-encode'
  22926.         ? '------------------------------------'
  22927.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  22928.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  22929.         ? cJson
  22930.         oSmtp = json_decode(cJson)
  22931.         cJson =  json_encode(oSmtp)
  22932.         ? cJson
  22933.         oSmtp = json_decode(cJson)
  22934.         cJson =  json_encode(oSmtp)
  22935.         ? cJson
  22936.  
  22937.         * Probar falla
  22938.         ?
  22939.         ? 'Probar una falla en el json'
  22940.         ? '---------------------------'
  22941.         cJson = ' {"server":"", "user":"", "password":"" ,'
  22942.         oSmtp = json_decode(cJson)
  22943.         if not empty(json_getErrorMsg())
  22944.                 ? json_getErrorMsg()
  22945.         endif
  22946.  
  22947.         ?
  22948.         ? 'Pruebas Finalizadas'
  22949. retur
  22950. *       ? oCustomer.get('lastname')
  22951. *
  22952. *
  22953. lRunTest = .f.
  22954. if lRunTest
  22955.         testJsonClass()
  22956. endif
  22957. return
  22958.  
  22959.  
  22960. function json_encode(xExpr)
  22961.         if vartype(_json)<>'O'
  22962.                 public _json
  22963.                 _json = newobject('json')
  22964.         endif
  22965. return _json.encode(@xExpr)
  22966.  
  22967.  
  22968. function json_decode(cJson)
  22969. local retval
  22970.         if vartype(_json)<>'O'
  22971.                 public _json
  22972.                 _json = newobject('json')
  22973.         endif
  22974.         retval = _json.decode(cJson)
  22975.         if not empty(_json.cError)
  22976.                 return null
  22977.         endif
  22978. return retval
  22979.  
  22980. function json_getErrorMsg()
  22981. return _json.cError
  22982.        
  22983.  
  22984.  
  22985. *
  22986. * json class
  22987. *
  22988. *
  22989. define class json as custom
  22990.  
  22991.  
  22992.         nPos=0
  22993.         nLen=0
  22994.         cJson=''
  22995.         cError=''
  22996.  
  22997.  
  22998.         *
  22999.         * Genera el codigo cJson para parametro que se manda
  23000.         *
  23001.         function encode(xExpr)
  23002.         local cTipo
  23003.                 * Cuando se manda una arreglo,
  23004.                 if type('ALen(xExpr)')=='N'
  23005.                         cTipo = 'A'
  23006.                 Else
  23007.                         cTipo = VarType(xExpr)
  23008.                 Endif
  23009.                
  23010.                 Do Case
  23011.                 Case cTipo=='D'
  23012.                         return '"'+dtos(xExpr)+'"'
  23013.                 Case cTipo=='N'
  23014.                         return Transform(xExpr)
  23015.                 Case cTipo=='L'
  23016.                         return iif(xExpr,'true','false')
  23017.                 Case cTipo=='X'
  23018.                         return 'null'
  23019.                 Case cTipo=='C'
  23020.                         xExpr = allt(xExpr)
  23021.                         xExpr = StrTran(xExpr, '\', '\\' )
  23022.                         xExpr = StrTran(xExpr, '/', '\/' )
  23023.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  23024.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  23025.                         xExpr = StrTran(xExpr, '"', '\"' )
  23026.                         return '"'+xExpr+'"'
  23027.  
  23028.                 case cTipo=='O'
  23029.                         local cProp, cJsonValue, cRetVal, aProp[1]
  23030.                         =AMembers(aProp,xExpr)
  23031.                         cRetVal = ''
  23032.                         for each cProp in aProp
  23033.                                 *? cProp
  23034.                                 *? cRetVal
  23035.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  23036.                                         * algunas propiedades pueden no estar definidas
  23037.                                         * como: activecontrol, parent, etc
  23038.                                         loop
  23039.                                 endif
  23040.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  23041.                                         *
  23042.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  23043.                                         *
  23044.                                         Local i,nTotElem
  23045.                                         cJsonValue = ''
  23046.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  23047.                                         For i=1 to nTotElem
  23048.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  23049.                                         Next
  23050.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  23051.                                 else
  23052.                                         *
  23053.                                         * es otro tipo de dato normal C, N, L
  23054.                                         *
  23055.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  23056.                                 endif
  23057.                                 if left(cProp,1)=='_'
  23058.                                         cProp = substr(cProp,2)
  23059.                                 endif
  23060.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  23061.                         next
  23062.                         return '{' + substr(cRetVal,2) + '}'
  23063.  
  23064.                 case cTipo=='A'
  23065.                         local valor, cRetVal
  23066.                         cRetVal = ''   
  23067.                         for each valor in xExpr
  23068.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  23069.                         next
  23070.                         return  '[' + substr(cRetVal,2) + ']'
  23071.                        
  23072.                 endcase
  23073.  
  23074.         return ''
  23075.  
  23076.  
  23077.  
  23078.  
  23079.  
  23080.         *
  23081.         * regresa un elemento representado por la cadena json que se manda
  23082.         *
  23083.        
  23084.         function decode(cJson)
  23085.         local retValue
  23086.                 cJson = StrTran(cJson,chr(9),'')
  23087.                 cJson = StrTran(cJson,chr(10),'')
  23088.                 cJson = StrTran(cJson,chr(13),'')
  23089.                 cJson = this.fixUnicode(cJson)
  23090.                 this.nPos  = 1
  23091.                 this.cJson = cJson
  23092.                 this.nLen  = len(cJson)
  23093.                 this.cError = ''
  23094.                 retValue = this.parsevalue()
  23095.                 if not empty(this.cError)
  23096.                         return null
  23097.                 endif
  23098.                 if this.getToken()<>null
  23099.                         this.setError('Junk at the end of JSON input')
  23100.                         return null
  23101.                 endif
  23102.         return retValue
  23103.                
  23104.        
  23105.         function parseValue()
  23106.         local token
  23107.                 token = this.getToken()
  23108.                 if token==null
  23109.                         this.setError('Nothing to parse')
  23110.                         return null
  23111.                 endif
  23112.                 do case
  23113.                 case token=='"'
  23114.                         return this.parseString()
  23115.                 case isdigit(token) or token=='-'
  23116.                         return this.parseNumber()
  23117.                 case token=='n'
  23118.                         return this.expectedKeyword('null',null)
  23119.                 case token=='f'
  23120.                         return this.expectedKeyword('false',.f.)
  23121.                 case token=='t'
  23122.                         return this.expectedKeyword('true',.t.)
  23123.                 case token=='{'
  23124.                         return this.parseObject()
  23125.                 case token=='['
  23126.                         return this.parseArray()
  23127.                 otherwise
  23128.                         this.setError('Unexpected token')
  23129.                 endcase
  23130.         return
  23131.                
  23132.        
  23133.         function expectedKeyword(cWord,eValue)
  23134.                 for i=1 to len(cWord)
  23135.                         cChar = this.getChar()
  23136.                         if cChar <> substr(cWord,i,1)
  23137.                                 this.setError("Expected keyword '" + cWord + "'")
  23138.                                 return ''
  23139.                         endif
  23140.                         this.nPos = this.nPos + 1
  23141.                 next
  23142.         return eValue
  23143.        
  23144.  
  23145.         function parseObject()
  23146.         local retval, cPropName, xValue
  23147.                 retval = createObject('myObj')
  23148.                 this.nPos = this.nPos + 1 && Eat {
  23149.                 if this.getToken()<>'}'
  23150.                         do while .t.
  23151.                                 cPropName = this.parseString()
  23152.                                 if not empty(this.cError)
  23153.                                         return null
  23154.                                 endif
  23155.                                 if this.getToken()<>':'
  23156.                                         this.setError("Expected ':' when parsing object")
  23157.                                         return null
  23158.                                 endif
  23159.                                 this.nPos = this.nPos + 1
  23160.                                 xValue = this.parseValue()
  23161.                                 if not empty(this.cError)
  23162.                                         return null
  23163.                                 endif                          
  23164.                                 ** Debug ? cPropName, type('xValue')
  23165.                                 retval.set(cPropName, xValue)
  23166.                                 if this.getToken()<>','
  23167.                                         exit
  23168.                                 endif
  23169.                                 this.nPos = this.nPos + 1
  23170.                         enddo
  23171.                 endif
  23172.                 if this.getToken()<>'}'
  23173.                         this.setError("Expected '}' at the end of object")
  23174.                         return null
  23175.                 endif
  23176.                 this.nPos = this.nPos + 1
  23177.         return retval
  23178.  
  23179.  
  23180.         function parseArray()
  23181.         local retVal, xValue
  23182.                 retval = createObject('MyArray')
  23183.                 this.nPos = this.nPos + 1       && Eat [
  23184.                 if this.getToken() <> ']'
  23185.                         do while .t.
  23186.                                 xValue = this.parseValue()
  23187.                                 if not empty(this.cError)
  23188.                                         return null
  23189.                                 endif
  23190.                                 retval.add( xValue )
  23191.                                 if this.getToken()<>','
  23192.                                         exit
  23193.                                 endif
  23194.                                 this.nPos = this.nPos + 1
  23195.                         enddo
  23196.                         if this.getToken() <> ']'
  23197.                                 this.setError('Expected ] at the end of array')
  23198.                                 return null
  23199.                         endif
  23200.                 endif
  23201.                 this.nPos = this.nPos + 1
  23202.         return retval
  23203.        
  23204.  
  23205.         function parseString()
  23206.         local cRetVal, c
  23207.                 if this.getToken()<>'"'
  23208.                         this.setError('Expected "')
  23209.                         return ''
  23210.                 endif
  23211.                 this.nPos = this.nPos + 1       && Eat "
  23212.                 cRetVal = ''
  23213.                 do while .t.
  23214.                         c = this.getChar()
  23215.                         if c==''
  23216.                                 return ''
  23217.                         endif
  23218.                         if c == '"'
  23219.                                 this.nPos = this.nPos + 1
  23220.                                 exit
  23221.                         endif
  23222.                         if c == '\'
  23223.                                 this.nPos = this.nPos + 1
  23224.                                 if (this.nPos>this.nLen)
  23225.                                         this.setError('\\ at the end of input')
  23226.                                         return ''
  23227.                                 endif
  23228.                                 c = this.getChar()
  23229.                                 if c==''
  23230.                                         return ''
  23231.                                 endif
  23232.                                 do case
  23233.                                 case c=='"'
  23234.                                         c='"'
  23235.                                 case c=='\'
  23236.                                         c='\'
  23237.                                 case c=='/'
  23238.                                         c='/'
  23239.                                 case c=='b'
  23240.                                         c=chr(8)
  23241.                                 case c=='t'
  23242.                                         c=chr(9)
  23243.                                 case c=='n'
  23244.                                         c=chr(10)
  23245.                                 case c=='f'
  23246.                                         c=chr(12)
  23247.                                 case c=='r'
  23248.                                         c=chr(13)
  23249.                                 otherwise
  23250.                                         ******* FALTAN LOS UNICODE
  23251.                                         this.setError('Invalid escape sequence in string literal')
  23252.                                         return ''
  23253.                                 endcase
  23254.                         endif
  23255.                         cRetVal = cRetVal + c
  23256.                         this.nPos = this.nPos + 1
  23257.                 enddo
  23258.         return cRetVal
  23259.                                        
  23260.  
  23261.         **** Pendiente numeros con E
  23262.         function parseNumber()
  23263.         local nStartPos,c, isInt, cNumero
  23264.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  23265.                         this.setError('Expected number literal')
  23266.                         return 0
  23267.                 endif
  23268.                 nStartPos = this.nPos
  23269.                 c = this.getChar()
  23270.                 if c == '-'
  23271.                         c = this.nextChar()
  23272.                 endif
  23273.                 if c == '0'
  23274.                         c = this.nextChar()
  23275.                 else
  23276.                         if isdigit(c)
  23277.                                 c = this.nextChar()
  23278.                                 do while isdigit(c)
  23279.                                         c = this.nextChar()
  23280.                                 enddo
  23281.                         else
  23282.                                 this.setError('Expected digit when parsing number')
  23283.                                 return 0
  23284.                         endif
  23285.                 endif
  23286.                
  23287.                 isInt = .t.
  23288.                 if c=='.'
  23289.                         c = this.nextChar()
  23290.                         if isdigit(c)
  23291.                                 c = this.nextChar()
  23292.                                 isInt = .f.
  23293.                                 do while isDigit(c)
  23294.                                         c = this.nextChar()
  23295.                                 enddo
  23296.                         else
  23297.                                 this.setError('Expected digit following dot comma')
  23298.                                 return 0
  23299.                         endif
  23300.                 endif
  23301.                
  23302.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  23303.         return val(cNumero)
  23304.  
  23305.  
  23306.  
  23307.         function getToken()
  23308.         local char1
  23309.                 do while .t.
  23310.                         if this.nPos > this.nLen
  23311.                                 return null
  23312.                         endif
  23313.                         char1 = substr(this.cJson, this.nPos, 1)
  23314.                         if char1==' '
  23315.                                 this.nPos = this.nPos + 1
  23316.                                 loop
  23317.                         endif
  23318.                         return char1
  23319.                 enddo
  23320.         return
  23321.        
  23322.                
  23323.                
  23324.         function getChar()
  23325.                 if this.nPos > this.nLen
  23326.                         this.setError('Unexpected end of JSON stream')
  23327.                         return ''
  23328.                 endif
  23329.         return substr(this.cJson, this.nPos, 1)
  23330.        
  23331.         function nextChar()
  23332.                 this.nPos = this.nPos + 1
  23333.                 if this.nPos > this.nLen
  23334.                         return ''
  23335.                 endif
  23336.         return substr(this.cJson, this.nPos, 1)
  23337.        
  23338.         function setError(cMsg)
  23339.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  23340.         return
  23341.  
  23342.  
  23343.         function fixUnicode(cStr)
  23344.                 cStr = StrTran(cStr,'\u00e1','á')
  23345.                 cStr = StrTran(cStr,'\u00e9','é')
  23346.                 cStr = StrTran(cStr,'\u00ed','í')
  23347.                 cStr = StrTran(cStr,'\u00f3','ó')
  23348.                 cStr = StrTran(cStr,'\u00fa','ú')
  23349.                 cStr = StrTran(cStr,'\u00c1','Á')
  23350.                 cStr = StrTran(cStr,'\u00c9','É')
  23351.                 cStr = StrTran(cStr,'\u00cd','Í')
  23352.                 cStr = StrTran(cStr,'\u00d3','Ó')
  23353.                 cStr = StrTran(cStr,'\u00da','Ú')
  23354.                 cStr = StrTran(cStr,'\u00f1','ń')
  23355.                 cStr = StrTran(cStr,'\u00d1','Ń')
  23356.         return cStr
  23357.  
  23358.  
  23359.  
  23360. enddefine
  23361.  
  23362.  
  23363.  
  23364.  
  23365.  
  23366. *
  23367. * class used to return an array
  23368. *
  23369. define class myArray as custom
  23370.         nSize = 0
  23371.         dimension array[1]
  23372.  
  23373.         function add(xExpr)
  23374.                 this.nSize = this.nSize + 1
  23375.                 dimension this.array[this.nSize]
  23376.                 this.array[this.nSize] = xExpr
  23377.         return
  23378.  
  23379.         function get(n)
  23380.         return this.array[n]
  23381.  
  23382. enddefine
  23383.  
  23384.  
  23385.  
  23386. *
  23387. * class used to simulate an object
  23388. * all properties are prefixed with 'prop' to permit property names like: error, init
  23389. * that already exists like vfp methods
  23390. *
  23391. define class myObj as custom
  23392. Hidden ;
  23393.         ClassLibrary,Comment, ;
  23394.         BaseClass,ControlCount, ;
  23395.         Controls,Objects,Object,;
  23396.         Height,HelpContextID,Left,Name, ;
  23397.         Parent,ParentClass,Picture, ;
  23398.         Tag,Top,WhatsThisHelpID,Width
  23399.                
  23400.         function set(cPropName, xValue)
  23401.                 cPropName = '_'+cPropName
  23402.                 if type('this.'+cPropName)=='U'
  23403.                         this.addProperty(cPropName,xValue)
  23404.                 else
  23405.                         local cmd
  23406.                         cmd = 'this.'+cPropName+'=xValue'
  23407.                         &cmd
  23408.                 endif
  23409.         return
  23410.        
  23411.         procedure get (cPropName)
  23412.                 cPropName = '_'+cPropName
  23413.                 If type('this.'+cPropName)=='U'
  23414.                         return ''
  23415.                 Else
  23416.                         local cmd
  23417.                         cmd = 'return this.'+cPropName
  23418.                         &cmd
  23419.                 endif
  23420.         return ''
  23421. enddefine
  23422.  
  23423.  
  23424.  
  23425.  
  23426.  
  23427. function testJsonClass
  23428.         clear
  23429.         set decimal to 10
  23430.         oJson = newObject('json')
  23431.        
  23432.        
  23433.         ? 'Test Basic Types'
  23434.         ? '----------------'
  23435.         ? oJson.decode('null')
  23436.         ? oJson.decode('true')
  23437.         ? oJson.decode('false')
  23438.         ?
  23439.         ? oJson.decode('791123')
  23440.         ? oJson.decode('791123.45')
  23441.         ? oJson.decode('791123.45.')
  23442.         ? oJson.decode('"nacho gtz"')
  23443.         if not empty(oJson.cError)
  23444.                 ? oJson.cError
  23445.                 return
  23446.         endif
  23447.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  23448.         if not empty(oJson.cError)
  23449.                 ? oJson.cError
  23450.                 return
  23451.         endif
  23452.        
  23453.         ? 'Test Array'
  23454.         ? '----------'
  23455.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  23456.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  23457.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  23458.         nombres = arr.get(1)
  23459.         edades  = arr.get(2)
  23460.         ? nombres.get(1), edades.get(1)
  23461.         ? nombres.get(2), edades.get(2)
  23462.         ? nombres.get(3), edades.get(3)
  23463.         ?
  23464.         ? 'Test Object'
  23465.         ? '-----------'
  23466.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  23467.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  23468.         ? obj._Nombre, obj._Edad, obj._IsGood
  23469.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  23470.         ? obj.get('jsonrpc'), obj._jsonrpc
  23471.         ? obj.get('id'), obj._id
  23472.         ? obj.get('method'), obj._method
  23473.         ? obj._Params.array[1], obj._Params.get(1)
  23474.  
  23475.         ?
  23476.         ? 'Test nested object'
  23477.         ? '------------------'
  23478.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  23479.         obj = oJson.decode(cJson)
  23480.         if not empty(oJson.cError)
  23481.                 ? oJson.cError
  23482.                 return
  23483.         endif
  23484.         ? cJson
  23485.         ? 'method -->',obj._method
  23486.         ? 'usrkey -->',obj._params._data._usrkey
  23487.         ? 'sendto -->',obj._params._data._sendto
  23488.         ? 'name  --->',obj._params._data._name
  23489.         ? 'expires ->',obj._params._data._expires
  23490.  
  23491.         ?
  23492.         ? 'Test empty object'
  23493.         ? '-----------------'
  23494.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  23495.         obj = oJson.decode(cJson)
  23496.         if not empty(oJson.cError)     
  23497.                 ? oJson.cError
  23498.                 return
  23499.         endif
  23500.         ? cJson
  23501.         ? 'result -->',obj._result, obj.get('result')
  23502.         oError = obj.get('error')
  23503.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  23504.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  23505.         ? 'id  ----->',obj._id, obj.get('id')
  23506.         ?  type("oError._code")
  23507.  
  23508.         ?
  23509.         ? 'Probar decode-enconde-decode-encode'
  23510.         ? '------------------------------------'
  23511.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  23512.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  23513.         ? cJson
  23514.         oSmtp = json_decode(cJson)
  23515.         cJson =  json_encode(oSmtp)
  23516.         ? cJson
  23517.         oSmtp = json_decode(cJson)
  23518.         cJson =  json_encode(oSmtp)
  23519.         ? cJson
  23520.  
  23521.         * Probar falla
  23522.         ?
  23523.         ? 'Probar una falla en el json'
  23524.         ? '---------------------------'
  23525.         cJson = ' {"server":"", "user":"", "password":"" ,'
  23526.         oSmtp = json_decode(cJson)
  23527.         if not empty(json_getErrorMsg())
  23528.                 ? json_getErrorMsg()
  23529.         endif
  23530.  
  23531.         ?
  23532.         ? 'Pruebas Finalizadas'
  23533. retur
  23534. *
  23535. *
  23536. lRunTest = .f.
  23537. if lRunTest
  23538.         testJsonClass()
  23539. endif
  23540. return
  23541.  
  23542.  
  23543. function json_encode(xExpr)
  23544.         if vartype(_json)<>'O'
  23545.                 public _json
  23546.                 _json = newobject('json')
  23547.         endif
  23548. return _json.encode(@xExpr)
  23549.  
  23550.  
  23551. function json_decode(cJson)
  23552. local retval
  23553.         if vartype(_json)<>'O'
  23554.                 public _json
  23555.                 _json = newobject('json')
  23556.         endif
  23557.         retval = _json.decode(cJson)
  23558.         if not empty(_json.cError)
  23559.                 return null
  23560.         endif
  23561. return retval
  23562.  
  23563. function json_getErrorMsg()
  23564. return _json.cError
  23565.        
  23566.  
  23567.  
  23568. *
  23569. * json class
  23570. *
  23571. *
  23572. define class json as custom
  23573.  
  23574.  
  23575.         nPos=0
  23576.         nLen=0
  23577.         cJson=''
  23578.         cError=''
  23579.  
  23580.  
  23581.         *
  23582.         * Genera el codigo cJson para parametro que se manda
  23583.         *
  23584.         function encode(xExpr)
  23585.         local cTipo
  23586.                 * Cuando se manda una arreglo,
  23587.                 if type('ALen(xExpr)')=='N'
  23588.                         cTipo = 'A'
  23589.                 Else
  23590.                         cTipo = VarType(xExpr)
  23591.                 Endif
  23592.                
  23593.                 Do Case
  23594.                 Case cTipo=='D'
  23595.                         return '"'+dtos(xExpr)+'"'
  23596.                 Case cTipo=='N'
  23597.                         return Transform(xExpr)
  23598.                 Case cTipo=='L'
  23599.                         return iif(xExpr,'true','false')
  23600.                 Case cTipo=='X'
  23601.                         return 'null'
  23602.                 Case cTipo=='C'
  23603.                         xExpr = allt(xExpr)
  23604.                         xExpr = StrTran(xExpr, '\', '\\' )
  23605.                         xExpr = StrTran(xExpr, '/', '\/' )
  23606.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  23607.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  23608.                         xExpr = StrTran(xExpr, '"', '\"' )
  23609.                         return '"'+xExpr+'"'
  23610.  
  23611.                 case cTipo=='O'
  23612.                         local cProp, cJsonValue, cRetVal, aProp[1]
  23613.                         =AMembers(aProp,xExpr)
  23614.                         cRetVal = ''
  23615.                         for each cProp in aProp
  23616.                                 *? cProp
  23617.                                 *? cRetVal
  23618.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  23619.                                         * algunas propiedades pueden no estar definidas
  23620.                                         * como: activecontrol, parent, etc
  23621.                                         loop
  23622.                                 endif
  23623.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  23624.                                         *
  23625.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  23626.                                         *
  23627.                                         Local i,nTotElem
  23628.                                         cJsonValue = ''
  23629.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  23630.                                         For i=1 to nTotElem
  23631.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  23632.                                         Next
  23633.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  23634.                                 else
  23635.                                         *
  23636.                                         * es otro tipo de dato normal C, N, L
  23637.                                         *
  23638.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  23639.                                 endif
  23640.                                 if left(cProp,1)=='_'
  23641.                                         cProp = substr(cProp,2)
  23642.                                 endif
  23643.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  23644.                         next
  23645.                         return '{' + substr(cRetVal,2) + '}'
  23646.  
  23647.                 case cTipo=='A'
  23648.                         local valor, cRetVal
  23649.                         cRetVal = ''   
  23650.                         for each valor in xExpr
  23651.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  23652.                         next
  23653.                         return  '[' + substr(cRetVal,2) + ']'
  23654.                        
  23655.                 endcase
  23656.  
  23657.         return ''
  23658.  
  23659.  
  23660.  
  23661.  
  23662.  
  23663.         *
  23664.         * regresa un elemento representado por la cadena json que se manda
  23665.         *
  23666.        
  23667.         function decode(cJson)
  23668.         local retValue
  23669.                 cJson = StrTran(cJson,chr(9),'')
  23670.                 cJson = StrTran(cJson,chr(10),'')
  23671.                 cJson = StrTran(cJson,chr(13),'')
  23672.                 cJson = this.fixUnicode(cJson)
  23673.                 this.nPos  = 1
  23674.                 this.cJson = cJson
  23675.                 this.nLen  = len(cJson)
  23676.                 this.cError = ''
  23677.                 retValue = this.parsevalue()
  23678.                 if not empty(this.cError)
  23679.                         return null
  23680.                 endif
  23681.                 if this.getToken()<>null
  23682.                         this.setError('Junk at the end of JSON input')
  23683.                         return null
  23684.                 endif
  23685.         return retValue
  23686.                
  23687.        
  23688.         function parseValue()
  23689.         local token
  23690.                 token = this.getToken()
  23691.                 if token==null
  23692.                         this.setError('Nothing to parse')
  23693.                         return null
  23694.                 endif
  23695.                 do case
  23696.                 case token=='"'
  23697.                         return this.parseString()
  23698.                 case isdigit(token) or token=='-'
  23699.                         return this.parseNumber()
  23700.                 case token=='n'
  23701.                         return this.expectedKeyword('null',null)
  23702.                 case token=='f'
  23703.                         return this.expectedKeyword('false',.f.)
  23704.                 case token=='t'
  23705.                         return this.expectedKeyword('true',.t.)
  23706.                 case token=='{'
  23707.                         return this.parseObject()
  23708.                 case token=='['
  23709.                         return this.parseArray()
  23710.                 otherwise
  23711.                         this.setError('Unexpected token')
  23712.                 endcase
  23713.         return
  23714.                
  23715.        
  23716.         function expectedKeyword(cWord,eValue)
  23717.                 for i=1 to len(cWord)
  23718.                         cChar = this.getChar()
  23719.                         if cChar <> substr(cWord,i,1)
  23720.                                 this.setError("Expected keyword '" + cWord + "'")
  23721.                                 return ''
  23722.                         endif
  23723.                         this.nPos = this.nPos + 1
  23724.                 next
  23725.         return eValue
  23726.        
  23727.  
  23728.         function parseObject()
  23729.         local retval, cPropName, xValue
  23730.                 retval = createObject('myObj')
  23731.                 this.nPos = this.nPos + 1 && Eat {
  23732.                 if this.getToken()<>'}'
  23733.                         do while .t.
  23734.                                 cPropName = this.parseString()
  23735.                                 if not empty(this.cError)
  23736.                                         return null
  23737.                                 endif
  23738.                                 if this.getToken()<>':'
  23739.                                         this.setError("Expected ':' when parsing object")
  23740.                                         return null
  23741.                                 endif
  23742.                                 this.nPos = this.nPos + 1
  23743.                                 xValue = this.parseValue()
  23744.                                 if not empty(this.cError)
  23745.                                         return null
  23746.                                 endif                          
  23747.                                 ** Debug ? cPropName, type('xValue')
  23748.                                 retval.set(cPropName, xValue)
  23749.                                 if this.getToken()<>','
  23750.                                         exit
  23751.                                 endif
  23752.                                 this.nPos = this.nPos + 1
  23753.                         enddo
  23754.                 endif
  23755.                 if this.getToken()<>'}'
  23756.                         this.setError("Expected '}' at the end of object")
  23757.                         return null
  23758.                 endif
  23759.                 this.nPos = this.nPos + 1
  23760.         return retval
  23761.  
  23762.  
  23763.         function parseArray()
  23764.         local retVal, xValue
  23765.                 retval = createObject('MyArray')
  23766.                 this.nPos = this.nPos + 1       && Eat [
  23767.                 if this.getToken() <> ']'
  23768.                         do while .t.
  23769.                                 xValue = this.parseValue()
  23770.                                 if not empty(this.cError)
  23771.                                         return null
  23772.                                 endif
  23773.                                 retval.add( xValue )
  23774.                                 if this.getToken()<>','
  23775.                                         exit
  23776.                                 endif
  23777.                                 this.nPos = this.nPos + 1
  23778.                         enddo
  23779.                         if this.getToken() <> ']'
  23780.                                 this.setError('Expected ] at the end of array')
  23781.                                 return null
  23782.                         endif
  23783.                 endif
  23784.                 this.nPos = this.nPos + 1
  23785.         return retval
  23786.        
  23787.  
  23788.         function parseString()
  23789.         local cRetVal, c
  23790.                 if this.getToken()<>'"'
  23791.                         this.setError('Expected "')
  23792.                         return ''
  23793.                 endif
  23794.                 this.nPos = this.nPos + 1       && Eat "
  23795.                 cRetVal = ''
  23796.                 do while .t.
  23797.                         c = this.getChar()
  23798.                         if c==''
  23799.                                 return ''
  23800.                         endif
  23801.                         if c == '"'
  23802.                                 this.nPos = this.nPos + 1
  23803.                                 exit
  23804.                         endif
  23805.                         if c == '\'
  23806.                                 this.nPos = this.nPos + 1
  23807.                                 if (this.nPos>this.nLen)
  23808.                                         this.setError('\\ at the end of input')
  23809.                                         return ''
  23810.                                 endif
  23811.                                 c = this.getChar()
  23812.                                 if c==''
  23813.                                         return ''
  23814.                                 endif
  23815.                                 do case
  23816.                                 case c=='"'
  23817.                                         c='"'
  23818.                                 case c=='\'
  23819.                                         c='\'
  23820.                                 case c=='/'
  23821.                                         c='/'
  23822.                                 case c=='b'
  23823.                                         c=chr(8)
  23824.                                 case c=='t'
  23825.                                         c=chr(9)
  23826.                                 case c=='n'
  23827.                                         c=chr(10)
  23828.                                 case c=='f'
  23829.                                         c=chr(12)
  23830.                                 case c=='r'
  23831.                                         c=chr(13)
  23832.                                 otherwise
  23833.                                         ******* FALTAN LOS UNICODE
  23834.                                         this.setError('Invalid escape sequence in string literal')
  23835.                                         return ''
  23836.                                 endcase
  23837.                         endif
  23838.                         cRetVal = cRetVal + c
  23839.                         this.nPos = this.nPos + 1
  23840.                 enddo
  23841.         return cRetVal
  23842.                                        
  23843.  
  23844.         **** Pendiente numeros con E
  23845.         function parseNumber()
  23846.         local nStartPos,c, isInt, cNumero
  23847.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  23848.                         this.setError('Expected number literal')
  23849.                         return 0
  23850.                 endif
  23851.                 nStartPos = this.nPos
  23852.                 c = this.getChar()
  23853.                 if c == '-'
  23854.                         c = this.nextChar()
  23855.                 endif
  23856.                 if c == '0'
  23857.                         c = this.nextChar()
  23858.                 else
  23859.                         if isdigit(c)
  23860.                                 c = this.nextChar()
  23861.                                 do while isdigit(c)
  23862.                                         c = this.nextChar()
  23863.                                 enddo
  23864.                         else
  23865.                                 this.setError('Expected digit when parsing number')
  23866.                                 return 0
  23867.                         endif
  23868.                 endif
  23869.                
  23870.                 isInt = .t.
  23871.                 if c=='.'
  23872.                         c = this.nextChar()
  23873.                         if isdigit(c)
  23874.                                 c = this.nextChar()
  23875.                                 isInt = .f.
  23876.                                 do while isDigit(c)
  23877.                                         c = this.nextChar()
  23878.                                 enddo
  23879.                         else
  23880.                                 this.setError('Expected digit following dot comma')
  23881.                                 return 0
  23882.                         endif
  23883.                 endif
  23884.                
  23885.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  23886.         return val(cNumero)
  23887.  
  23888.  
  23889.  
  23890.         function getToken()
  23891.         local char1
  23892.                 do while .t.
  23893.                         if this.nPos > this.nLen
  23894.                                 return null
  23895.                         endif
  23896.                         char1 = substr(this.cJson, this.nPos, 1)
  23897.                         if char1==' '
  23898.                                 this.nPos = this.nPos + 1
  23899.                                 loop
  23900.                         endif
  23901.                         return char1
  23902.                 enddo
  23903.         return
  23904.        
  23905.                
  23906.                
  23907.         function getChar()
  23908.                 if this.nPos > this.nLen
  23909.                         this.setError('Unexpected end of JSON stream')
  23910.                         return ''
  23911.                 endif
  23912.         return substr(this.cJson, this.nPos, 1)
  23913.        
  23914.         function nextChar()
  23915.                 this.nPos = this.nPos + 1
  23916.                 if this.nPos > this.nLen
  23917.                         return ''
  23918.                 endif
  23919.         return substr(this.cJson, this.nPos, 1)
  23920.        
  23921.         function setError(cMsg)
  23922.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  23923.         return
  23924.  
  23925.  
  23926.         function fixUnicode(cStr)
  23927.                 cStr = StrTran(cStr,'\u00e1','á')
  23928.                 cStr = StrTran(cStr,'\u00e9','é')
  23929.                 cStr = StrTran(cStr,'\u00ed','í')
  23930.                 cStr = StrTran(cStr,'\u00f3','ó')
  23931.                 cStr = StrTran(cStr,'\u00fa','ú')
  23932.                 cStr = StrTran(cStr,'\u00c1','Á')
  23933.                 cStr = StrTran(cStr,'\u00c9','É')
  23934.                 cStr = StrTran(cStr,'\u00cd','Í')
  23935.                 cStr = StrTran(cStr,'\u00d3','Ó')
  23936.                 cStr = StrTran(cStr,'\u00da','Ú')
  23937.                 cStr = StrTran(cStr,'\u00f1','ń')
  23938.                 cStr = StrTran(cStr,'\u00d1','Ń')
  23939.         return cStr
  23940.  
  23941.  
  23942.  
  23943. enddefine
  23944.  
  23945.  
  23946.  
  23947.  
  23948.  
  23949. *
  23950. * class used to return an array
  23951. *
  23952. define class myArray as custom
  23953.         nSize = 0
  23954.         dimension array[1]
  23955.  
  23956.         function add(xExpr)
  23957.                 this.nSize = this.nSize + 1
  23958.                 dimension this.array[this.nSize]
  23959.                 this.array[this.nSize] = xExpr
  23960.         return
  23961.  
  23962.         function get(n)
  23963.         return this.array[n]
  23964.  
  23965. enddefine
  23966.  
  23967.  
  23968.  
  23969. *
  23970. * class used to simulate an object
  23971. * all properties are prefixed with 'prop' to permit property names like: error, init
  23972. * that already exists like vfp methods
  23973. *
  23974. define class myObj as custom
  23975. Hidden ;
  23976.         ClassLibrary,Comment, ;
  23977.         BaseClass,ControlCount, ;
  23978.         Controls,Objects,Object,;
  23979.         Height,HelpContextID,Left,Name, ;
  23980.         Parent,ParentClass,Picture, ;
  23981.         Tag,Top,WhatsThisHelpID,Width
  23982.                
  23983.         function set(cPropName, xValue)
  23984.                 cPropName = '_'+cPropName
  23985.                 if type('this.'+cPropName)=='U'
  23986.                         this.addProperty(cPropName,xValue)
  23987.                 else
  23988.                         local cmd
  23989.                         cmd = 'this.'+cPropName+'=xValue'
  23990.                         &cmd
  23991.                 endif
  23992.         return
  23993.        
  23994.         procedure get (cPropName)
  23995.                 cPropName = '_'+cPropName
  23996.                 If type('this.'+cPropName)=='U'
  23997.                         return ''
  23998.                 Else
  23999.                         local cmd
  24000.                         cmd = 'return this.'+cPropName
  24001.                         &cmd
  24002.                 endif
  24003.         return ''
  24004. enddefine
  24005.  
  24006.  
  24007.  
  24008.  
  24009.  
  24010. function testJsonClass
  24011.         clear
  24012.         set decimal to 10
  24013.         oJson = newObject('json')
  24014.        
  24015.        
  24016.         ? 'Test Basic Types'
  24017.         ? '----------------'
  24018.         ? oJson.decode('null')
  24019.         ? oJson.decode('true')
  24020.         ? oJson.decode('false')
  24021.         ?
  24022.         ? oJson.decode('791123')
  24023.         ? oJson.decode('791123.45')
  24024.         ? oJson.decode('791123.45.')
  24025.         ? oJson.decode('"nacho gtz"')
  24026.         if not empty(oJson.cError)
  24027.                 ? oJson.cError
  24028.                 return
  24029.         endif
  24030.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  24031.         if not empty(oJson.cError)
  24032.                 ? oJson.cError
  24033.                 return
  24034.         endif
  24035.        
  24036.         ? 'Test Array'
  24037.         ? '----------'
  24038.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  24039.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  24040.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  24041.         nombres = arr.get(1)
  24042.         edades  = arr.get(2)
  24043.         ? nombres.get(1), edades.get(1)
  24044.         ? nombres.get(2), edades.get(2)
  24045.         ? nombres.get(3), edades.get(3)
  24046.         ?
  24047.         ? 'Test Object'
  24048.         ? '-----------'
  24049.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  24050.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  24051.         ? obj._Nombre, obj._Edad, obj._IsGood
  24052.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  24053.         ? obj.get('jsonrpc'), obj._jsonrpc
  24054.         ? obj.get('id'), obj._id
  24055.         ? obj.get('method'), obj._method
  24056.         ? obj._Params.array[1], obj._Params.get(1)
  24057.  
  24058.         ?
  24059.         ? 'Test nested object'
  24060.         ? '------------------'
  24061.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  24062.         obj = oJson.decode(cJson)
  24063.         if not empty(oJson.cError)
  24064.                 ? oJson.cError
  24065.                 return
  24066.         endif
  24067.         ? cJson
  24068.         ? 'method -->',obj._method
  24069.         ? 'usrkey -->',obj._params._data._usrkey
  24070.         ? 'sendto -->',obj._params._data._sendto
  24071.         ? 'name  --->',obj._params._data._name
  24072.         ? 'expires ->',obj._params._data._expires
  24073.  
  24074.         ?
  24075.         ? 'Test empty object'
  24076.         ? '-----------------'
  24077.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  24078.         obj = oJson.decode(cJson)
  24079.         if not empty(oJson.cError)     
  24080.                 ? oJson.cError
  24081.                 return
  24082.         endif
  24083.         ? cJson
  24084.         ? 'result -->',obj._result, obj.get('result')
  24085.         oError = obj.get('error')
  24086.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  24087.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  24088.         ? 'id  ----->',obj._id, obj.get('id')
  24089.         ?  type("oError._code")
  24090.  
  24091.         ?
  24092.         ? 'Probar decode-enconde-decode-encode'
  24093.         ? '------------------------------------'
  24094.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  24095.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  24096.         ? cJson
  24097.         oSmtp = json_decode(cJson)
  24098.         cJson =  json_encode(oSmtp)
  24099.         ? cJson
  24100.         oSmtp = json_decode(cJson)
  24101.         cJson =  json_encode(oSmtp)
  24102.         ? cJson
  24103.  
  24104.         * Probar falla
  24105.         ?
  24106.         ? 'Probar una falla en el json'
  24107.         ? '---------------------------'
  24108.         cJson = ' {"server":"", "user":"", "password":"" ,'
  24109.         oSmtp = json_decode(cJson)
  24110.         if not empty(json_getErrorMsg())
  24111.                 ? json_getErrorMsg()
  24112.         endif
  24113.  
  24114.         ?
  24115.         ? 'Pruebas Finalizadas'
  24116. retur
  24117. *
  24118. lRunTest = .f.
  24119. if lRunTest
  24120.         testJsonClass()
  24121. endif
  24122. return
  24123.  
  24124.  
  24125. function json_encode(xExpr)
  24126.         if vartype(_json)<>'O'
  24127.                 public _json
  24128.                 _json = newobject('json')
  24129.         endif
  24130. return _json.encode(@xExpr)
  24131.  
  24132.  
  24133. function json_decode(cJson)
  24134. local retval
  24135.         if vartype(_json)<>'O'
  24136.                 public _json
  24137.                 _json = newobject('json')
  24138.         endif
  24139.         retval = _json.decode(cJson)
  24140.         if not empty(_json.cError)
  24141.                 return null
  24142.         endif
  24143. return retval
  24144.  
  24145. function json_getErrorMsg()
  24146. return _json.cError
  24147.        
  24148.  
  24149. *
  24150. * json class
  24151. *
  24152. *
  24153. define class json as custom
  24154.  
  24155.  
  24156.         nPos=0
  24157.         nLen=0
  24158.         cJson=''
  24159.         cError=''
  24160.  
  24161.  
  24162.         *
  24163.         * Genera el codigo cJson para parametro que se manda
  24164.         *
  24165.         function encode(xExpr)
  24166.         local cTipo
  24167.                 * Cuando se manda una arreglo,
  24168.                 if type('ALen(xExpr)')=='N'
  24169.                         cTipo = 'A'
  24170.                 Else
  24171.                         cTipo = VarType(xExpr)
  24172.                 Endif
  24173.                
  24174.                 Do Case
  24175.                 Case cTipo=='D'
  24176.                         return '"'+dtos(xExpr)+'"'
  24177.                 Case cTipo=='N'
  24178.                         return Transform(xExpr)
  24179.                 Case cTipo=='L'
  24180.                         return iif(xExpr,'true','false')
  24181.                 Case cTipo=='X'
  24182.                         return 'null'
  24183.                 Case cTipo=='C'
  24184.                         xExpr = allt(xExpr)
  24185.                         xExpr = StrTran(xExpr, '\', '\\' )
  24186.                         xExpr = StrTran(xExpr, '/', '\/' )
  24187.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  24188.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  24189.                         xExpr = StrTran(xExpr, '"', '\"' )
  24190.                         return '"'+xExpr+'"'
  24191.  
  24192.                 case cTipo=='O'
  24193.                         local cProp, cJsonValue, cRetVal, aProp[1]
  24194.                         =AMembers(aProp,xExpr)
  24195.                         cRetVal = ''
  24196.                         for each cProp in aProp
  24197.                                 *? cProp
  24198.                                 *? cRetVal
  24199.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  24200.                                         * algunas propiedades pueden no estar definidas
  24201.                                         * como: activecontrol, parent, etc
  24202.                                         loop
  24203.                                 endif
  24204.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  24205.                                         *
  24206.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  24207.                                         *
  24208.                                         Local i,nTotElem
  24209.                                         cJsonValue = ''
  24210.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  24211.                                         For i=1 to nTotElem
  24212.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  24213.                                         Next
  24214.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  24215.                                 else
  24216.                                         *
  24217.                                         * es otro tipo de dato normal C, N, L
  24218.                                         *
  24219.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  24220.                                 endif
  24221.                                 if left(cProp,1)=='_'
  24222.                                         cProp = substr(cProp,2)
  24223.                                 endif
  24224.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  24225.                         next
  24226.                         return '{' + substr(cRetVal,2) + '}'
  24227.  
  24228.                 case cTipo=='A'
  24229.                         local valor, cRetVal
  24230.                         cRetVal = ''   
  24231.                         for each valor in xExpr
  24232.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  24233.                         next
  24234.                         return  '[' + substr(cRetVal,2) + ']'
  24235.                        
  24236.                 endcase
  24237.  
  24238.         return ''
  24239.  
  24240.  
  24241.  
  24242.  
  24243.  
  24244.         *
  24245.         * regresa un elemento representado por la cadena json que se manda
  24246.         *
  24247.        
  24248.         function decode(cJson)
  24249.         local retValue
  24250.                 cJson = StrTran(cJson,chr(9),'')
  24251.                 cJson = StrTran(cJson,chr(10),'')
  24252.                 cJson = StrTran(cJson,chr(13),'')
  24253.                 cJson = this.fixUnicode(cJson)
  24254.                 this.nPos  = 1
  24255.                 this.cJson = cJson
  24256.                 this.nLen  = len(cJson)
  24257.                 this.cError = ''
  24258.                 retValue = this.parsevalue()
  24259.                 if not empty(this.cError)
  24260.                         return null
  24261.                 endif
  24262.                 if this.getToken()<>null
  24263.                         this.setError('Junk at the end of JSON input')
  24264.                         return null
  24265.                 endif
  24266.         return retValue
  24267.                
  24268.        
  24269.         function parseValue()
  24270.         local token
  24271.                 token = this.getToken()
  24272.                 if token==null
  24273.                         this.setError('Nothing to parse')
  24274.                         return null
  24275.                 endif
  24276.                 do case
  24277.                 case token=='"'
  24278.                         return this.parseString()
  24279.                 case isdigit(token) or token=='-'
  24280.                         return this.parseNumber()
  24281.                 case token=='n'
  24282.                         return this.expectedKeyword('null',null)
  24283.                 case token=='f'
  24284.                         return this.expectedKeyword('false',.f.)
  24285.                 case token=='t'
  24286.                         return this.expectedKeyword('true',.t.)
  24287.                 case token=='{'
  24288.                         return this.parseObject()
  24289.                 case token=='['
  24290.                         return this.parseArray()
  24291.                 otherwise
  24292.                         this.setError('Unexpected token')
  24293.                 endcase
  24294.         return
  24295.                
  24296.        
  24297.         function expectedKeyword(cWord,eValue)
  24298.                 for i=1 to len(cWord)
  24299.                         cChar = this.getChar()
  24300.                         if cChar <> substr(cWord,i,1)
  24301.                                 this.setError("Expected keyword '" + cWord + "'")
  24302.                                 return ''
  24303.                         endif
  24304.                         this.nPos = this.nPos + 1
  24305.                 next
  24306.         return eValue
  24307.        
  24308.  
  24309.         function parseObject()
  24310.         local retval, cPropName, xValue
  24311.                 retval = createObject('myObj')
  24312.                 this.nPos = this.nPos + 1 && Eat {
  24313.                 if this.getToken()<>'}'
  24314.                         do while .t.
  24315.                                 cPropName = this.parseString()
  24316.                                 if not empty(this.cError)
  24317.                                         return null
  24318.                                 endif
  24319.                                 if this.getToken()<>':'
  24320.                                         this.setError("Expected ':' when parsing object")
  24321.                                         return null
  24322.                                 endif
  24323.                                 this.nPos = this.nPos + 1
  24324.                                 xValue = this.parseValue()
  24325.                                 if not empty(this.cError)
  24326.                                         return null
  24327.                                 endif                          
  24328.                                 ** Debug ? cPropName, type('xValue')
  24329.                                 retval.set(cPropName, xValue)
  24330.                                 if this.getToken()<>','
  24331.                                         exit
  24332.                                 endif
  24333.                                 this.nPos = this.nPos + 1
  24334.                         enddo
  24335.                 endif
  24336.                 if this.getToken()<>'}'
  24337.                         this.setError("Expected '}' at the end of object")
  24338.                         return null
  24339.                 endif
  24340.                 this.nPos = this.nPos + 1
  24341.         return retval
  24342.  
  24343.  
  24344.         function parseArray()
  24345.         local retVal, xValue
  24346.                 retval = createObject('MyArray')
  24347.                 this.nPos = this.nPos + 1       && Eat [
  24348.                 if this.getToken() <> ']'
  24349.                         do while .t.
  24350.                                 xValue = this.parseValue()
  24351.                                 if not empty(this.cError)
  24352.                                         return null
  24353.                                 endif
  24354.                                 retval.add( xValue )
  24355.                                 if this.getToken()<>','
  24356.                                         exit
  24357.                                 endif
  24358.                                 this.nPos = this.nPos + 1
  24359.                         enddo
  24360.                         if this.getToken() <> ']'
  24361.                                 this.setError('Expected ] at the end of array')
  24362.                                 return null
  24363.                         endif
  24364.                 endif
  24365.                 this.nPos = this.nPos + 1
  24366.         return retval
  24367.        
  24368.  
  24369.         function parseString()
  24370.         local cRetVal, c
  24371.                 if this.getToken()<>'"'
  24372.                         this.setError('Expected "')
  24373.                         return ''
  24374.                 endif
  24375.                 this.nPos = this.nPos + 1       && Eat "
  24376.                 cRetVal = ''
  24377.                 do while .t.
  24378.                         c = this.getChar()
  24379.                         if c==''
  24380.                                 return ''
  24381.                         endif
  24382.                         if c == '"'
  24383.                                 this.nPos = this.nPos + 1
  24384.                                 exit
  24385.                         endif
  24386.                         if c == '\'
  24387.                                 this.nPos = this.nPos + 1
  24388.                                 if (this.nPos>this.nLen)
  24389.                                         this.setError('\\ at the end of input')
  24390.                                         return ''
  24391.                                 endif
  24392.                                 c = this.getChar()
  24393.                                 if c==''
  24394.                                         return ''
  24395.                                 endif
  24396.                                 do case
  24397.                                 case c=='"'
  24398.                                         c='"'
  24399.                                 case c=='\'
  24400.                                         c='\'
  24401.                                 case c=='/'
  24402.                                         c='/'
  24403.                                 case c=='b'
  24404.                                         c=chr(8)
  24405.                                 case c=='t'
  24406.                                         c=chr(9)
  24407.                                 case c=='n'
  24408.                                         c=chr(10)
  24409.                                 case c=='f'
  24410.                                         c=chr(12)
  24411.                                 case c=='r'
  24412.                                         c=chr(13)
  24413.                                 otherwise
  24414.                                         ******* FALTAN LOS UNICODE
  24415.                                         this.setError('Invalid escape sequence in string literal')
  24416.                                         return ''
  24417.                                 endcase
  24418.                         endif
  24419.                         cRetVal = cRetVal + c
  24420.                         this.nPos = this.nPos + 1
  24421.                 enddo
  24422.         return cRetVal
  24423.                                        
  24424.  
  24425.         **** Pendiente numeros con E
  24426.         function parseNumber()
  24427.         local nStartPos,c, isInt, cNumero
  24428.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  24429.                         this.setError('Expected number literal')
  24430.                         return 0
  24431.                 endif
  24432.                 nStartPos = this.nPos
  24433.                 c = this.getChar()
  24434.                 if c == '-'
  24435.                         c = this.nextChar()
  24436.                 endif
  24437.                 if c == '0'
  24438.                         c = this.nextChar()
  24439.                 else
  24440.                         if isdigit(c)
  24441.                                 c = this.nextChar()
  24442.                                 do while isdigit(c)
  24443.                                         c = this.nextChar()
  24444.                                 enddo
  24445.                         else
  24446.                                 this.setError('Expected digit when parsing number')
  24447.                                 return 0
  24448.                         endif
  24449.                 endif
  24450.                
  24451.                 isInt = .t.
  24452.                 if c=='.'
  24453.                         c = this.nextChar()
  24454.                         if isdigit(c)
  24455.                                 c = this.nextChar()
  24456.                                 isInt = .f.
  24457.                                 do while isDigit(c)
  24458.                                         c = this.nextChar()
  24459.                                 enddo
  24460.                         else
  24461.                                 this.setError('Expected digit following dot comma')
  24462.                                 return 0
  24463.                         endif
  24464.                 endif
  24465.                
  24466.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  24467.         return val(cNumero)
  24468.  
  24469.  
  24470.  
  24471.         function getToken()
  24472.         local char1
  24473.                 do while .t.
  24474.                         if this.nPos > this.nLen
  24475.                                 return null
  24476.                         endif
  24477.                         char1 = substr(this.cJson, this.nPos, 1)
  24478.                         if char1==' '
  24479.                                 this.nPos = this.nPos + 1
  24480.                                 loop
  24481.                         endif
  24482.                         return char1
  24483.                 enddo
  24484.         return
  24485.        
  24486.                
  24487.                
  24488.         function getChar()
  24489.                 if this.nPos > this.nLen
  24490.                         this.setError('Unexpected end of JSON stream')
  24491.                         return ''
  24492.                 endif
  24493.         return substr(this.cJson, this.nPos, 1)
  24494.        
  24495.         function nextChar()
  24496.                 this.nPos = this.nPos + 1
  24497.                 if this.nPos > this.nLen
  24498.                         return ''
  24499.                 endif
  24500.         return substr(this.cJson, this.nPos, 1)
  24501.        
  24502.         function setError(cMsg)
  24503.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  24504.         return
  24505.  
  24506.  
  24507.         function fixUnicode(cStr)
  24508.                 cStr = StrTran(cStr,'\u00e1','á')
  24509.                 cStr = StrTran(cStr,'\u00e9','é')
  24510.                 cStr = StrTran(cStr,'\u00ed','í')
  24511.                 cStr = StrTran(cStr,'\u00f3','ó')
  24512.                 cStr = StrTran(cStr,'\u00fa','ú')
  24513.                 cStr = StrTran(cStr,'\u00c1','Á')
  24514.                 cStr = StrTran(cStr,'\u00c9','É')
  24515.                 cStr = StrTran(cStr,'\u00cd','Í')
  24516.                 cStr = StrTran(cStr,'\u00d3','Ó')
  24517.                 cStr = StrTran(cStr,'\u00da','Ú')
  24518.                 cStr = StrTran(cStr,'\u00f1','ń')
  24519.                 cStr = StrTran(cStr,'\u00d1','Ń')
  24520.         return cStr
  24521.  
  24522.  
  24523.  
  24524. enddefine
  24525.  
  24526.  
  24527.  
  24528.  
  24529.  
  24530. *
  24531. * class used to return an array
  24532. *
  24533. define class myArray as custom
  24534.         nSize = 0
  24535.         dimension array[1]
  24536.  
  24537.         function add(xExpr)
  24538.                 this.nSize = this.nSize + 1
  24539.                 dimension this.array[this.nSize]
  24540.                 this.array[this.nSize] = xExpr
  24541.         return
  24542.  
  24543.         function get(n)
  24544.         return this.array[n]
  24545.  
  24546. enddefine
  24547.  
  24548.  
  24549.  
  24550. *
  24551. * class used to simulate an object
  24552. * all properties are prefixed with 'prop' to permit property names like: error, init
  24553. * that already exists like vfp methods
  24554. *
  24555. define class myObj as custom
  24556. Hidden ;
  24557.         ClassLibrary,Comment, ;
  24558.         BaseClass,ControlCount, ;
  24559.         Controls,Objects,Object,;
  24560.         Height,HelpContextID,Left,Name, ;
  24561.         Parent,ParentClass,Picture, ;
  24562.         Tag,Top,WhatsThisHelpID,Width
  24563.                
  24564.         function set(cPropName, xValue)
  24565.                 cPropName = '_'+cPropName
  24566.                 if type('this.'+cPropName)=='U'
  24567.                         this.addProperty(cPropName,xValue)
  24568.                 else
  24569.                         local cmd
  24570.                         cmd = 'this.'+cPropName+'=xValue'
  24571.                         &cmd
  24572.                 endif
  24573.         return
  24574.        
  24575.         procedure get (cPropName)
  24576.                 cPropName = '_'+cPropName
  24577.                 If type('this.'+cPropName)=='U'
  24578.                         return ''
  24579.                 Else
  24580.                         local cmd
  24581.                         cmd = 'return this.'+cPropName
  24582.                         &cmd
  24583.                 endif
  24584.         return ''
  24585. enddefine
  24586.  
  24587.  
  24588.  
  24589.  
  24590.  
  24591. function testJsonClass
  24592.         clear
  24593.         set decimal to 10
  24594.         oJson = newObject('json')
  24595.        
  24596.        
  24597.         ? 'Test Basic Types'
  24598.         ? '----------------'
  24599.         ? oJson.decode('null')
  24600.         ? oJson.decode('true')
  24601.         ? oJson.decode('false')
  24602.         ?
  24603.         ? oJson.decode('791123')
  24604.         ? oJson.decode('791123.45')
  24605.         ? oJson.decode('791123.45.')
  24606.         ? oJson.decode('"nacho gtz"')
  24607.         if not empty(oJson.cError)
  24608.                 ? oJson.cError
  24609.                 return
  24610.         endif
  24611.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  24612.         if not empty(oJson.cError)
  24613.                 ? oJson.cError
  24614.                 return
  24615.         endif
  24616.        
  24617.         ? 'Test Array'
  24618.         ? '----------'
  24619.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  24620.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  24621.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  24622.         nombres = arr.get(1)
  24623.         edades  = arr.get(2)
  24624.         ? nombres.get(1), edades.get(1)
  24625.         ? nombres.get(2), edades.get(2)
  24626.         ? nombres.get(3), edades.get(3)
  24627.         ?
  24628.         ? 'Test Object'
  24629.         ? '-----------'
  24630.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  24631.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  24632.         ? obj._Nombre, obj._Edad, obj._IsGood
  24633.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  24634.         ? obj.get('jsonrpc'), obj._jsonrpc
  24635.         ? obj.get('id'), obj._id
  24636.         ? obj.get('method'), obj._method
  24637.         ? obj._Params.array[1], obj._Params.get(1)
  24638.  
  24639.         ?
  24640.         ? 'Test nested object'
  24641.         ? '------------------'
  24642.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  24643.         obj = oJson.decode(cJson)
  24644.         if not empty(oJson.cError)
  24645.                 ? oJson.cError
  24646.                 return
  24647.         endif
  24648.         ? cJson
  24649.         ? 'method -->',obj._method
  24650.         ? 'usrkey -->',obj._params._data._usrkey
  24651.         ? 'sendto -->',obj._params._data._sendto
  24652.         ? 'name  --->',obj._params._data._name
  24653.         ? 'expires ->',obj._params._data._expires
  24654.  
  24655.         ?
  24656.         ? 'Test empty object'
  24657.         ? '-----------------'
  24658.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  24659.         obj = oJson.decode(cJson)
  24660.         if not empty(oJson.cError)     
  24661.                 ? oJson.cError
  24662.                 return
  24663.         endif
  24664.         ? cJson
  24665.         ? 'result -->',obj._result, obj.get('result')
  24666.         oError = obj.get('error')
  24667.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  24668.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  24669.         ? 'id  ----->',obj._id, obj.get('id')
  24670.         ?  type("oError._code")
  24671.  
  24672.         ?
  24673.         ? 'Probar decode-enconde-decode-encode'
  24674.         ? '------------------------------------'
  24675.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  24676.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  24677.         ? cJson
  24678.         oSmtp = json_decode(cJson)
  24679.         cJson =  json_encode(oSmtp)
  24680.         ? cJson
  24681.         oSmtp = json_decode(cJson)
  24682.         cJson =  json_encode(oSmtp)
  24683.         ? cJson
  24684.  
  24685.         * Probar falla
  24686.         ?
  24687.         ? 'Probar una falla en el json'
  24688.         ? '---------------------------'
  24689.         cJson = ' {"server":"", "user":"", "password":"" ,'
  24690.         oSmtp = json_decode(cJson)
  24691.         if not empty(json_getErrorMsg())
  24692.                 ? json_getErrorMsg()
  24693.         endif
  24694.  
  24695.         ?
  24696.         ? 'Pruebas Finalizadas'
  24697. retur
  24698. *
  24699. * json class
  24700. *
  24701. *
  24702. define class json as custom
  24703.  
  24704.  
  24705.         nPos=0
  24706.         nLen=0
  24707.         cJson=''
  24708.         cError=''
  24709.  
  24710.  
  24711.         *
  24712.         * Genera el codigo cJson para parametro que se manda
  24713.         *
  24714.         function encode(xExpr)
  24715.         local cTipo
  24716.                 * Cuando se manda una arreglo,
  24717.                 if type('ALen(xExpr)')=='N'
  24718.                         cTipo = 'A'
  24719.                 Else
  24720.                         cTipo = VarType(xExpr)
  24721.                 Endif
  24722.                
  24723.                 Do Case
  24724.                 Case cTipo=='D'
  24725.                         return '"'+dtos(xExpr)+'"'
  24726.                 Case cTipo=='N'
  24727.                         return Transform(xExpr)
  24728.                 Case cTipo=='L'
  24729.                         return iif(xExpr,'true','false')
  24730.                 Case cTipo=='X'
  24731.                         return 'null'
  24732.                 Case cTipo=='C'
  24733.                         xExpr = allt(xExpr)
  24734.                         xExpr = StrTran(xExpr, '\', '\\' )
  24735.                         xExpr = StrTran(xExpr, '/', '\/' )
  24736.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  24737.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  24738.                         xExpr = StrTran(xExpr, '"', '\"' )
  24739.                         return '"'+xExpr+'"'
  24740.  
  24741.                 case cTipo=='O'
  24742.                         local cProp, cJsonValue, cRetVal, aProp[1]
  24743.                         =AMembers(aProp,xExpr)
  24744.                         cRetVal = ''
  24745.                         for each cProp in aProp
  24746.                                 *? cProp
  24747.                                 *? cRetVal
  24748.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  24749.                                         * algunas propiedades pueden no estar definidas
  24750.                                         * como: activecontrol, parent, etc
  24751.                                         loop
  24752.                                 endif
  24753.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  24754.                                         *
  24755.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  24756.                                         *
  24757.                                         Local i,nTotElem
  24758.                                         cJsonValue = ''
  24759.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  24760.                                         For i=1 to nTotElem
  24761.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  24762.                                         Next
  24763.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  24764.                                 else
  24765.                                         *
  24766.                                         * es otro tipo de dato normal C, N, L
  24767.                                         *
  24768.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  24769.                                 endif
  24770.                                 if left(cProp,1)=='_'
  24771.                                         cProp = substr(cProp,2)
  24772.                                 endif
  24773.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  24774.                         next
  24775.                         return '{' + substr(cRetVal,2) + '}'
  24776.  
  24777.                 case cTipo=='A'
  24778.                         local valor, cRetVal
  24779.                         cRetVal = ''   
  24780.                         for each valor in xExpr
  24781.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  24782.                         next
  24783.                         return  '[' + substr(cRetVal,2) + ']'
  24784.                        
  24785.                 endcase
  24786.  
  24787.         return ''
  24788.  
  24789.  
  24790.  
  24791.  
  24792.  
  24793.         *
  24794.         * regresa un elemento representado por la cadena json que se manda
  24795.         *
  24796.        
  24797.         function decode(cJson)
  24798.         local retValue
  24799.                 cJson = StrTran(cJson,chr(9),'')
  24800.                 cJson = StrTran(cJson,chr(10),'')
  24801.                 cJson = StrTran(cJson,chr(13),'')
  24802.                 cJson = this.fixUnicode(cJson)
  24803.                 this.nPos  = 1
  24804.                 this.cJson = cJson
  24805.                 this.nLen  = len(cJson)
  24806.                 this.cError = ''
  24807.                 retValue = this.parsevalue()
  24808.                 if not empty(this.cError)
  24809.                         return null
  24810.                 endif
  24811.                 if this.getToken()<>null
  24812.                         this.setError('Junk at the end of JSON input')
  24813.                         return null
  24814.                 endif
  24815.         return retValue
  24816.                
  24817.        
  24818.         function parseValue()
  24819.         local token
  24820.                 token = this.getToken()
  24821.                 if token==null
  24822.                         this.setError('Nothing to parse')
  24823.                         return null
  24824.                 endif
  24825.                 do case
  24826.                 case token=='"'
  24827.                         return this.parseString()
  24828.                 case isdigit(token) or token=='-'
  24829.                         return this.parseNumber()
  24830.                 case token=='n'
  24831.                         return this.expectedKeyword('null',null)
  24832.                 case token=='f'
  24833.                         return this.expectedKeyword('false',.f.)
  24834.                 case token=='t'
  24835.                         return this.expectedKeyword('true',.t.)
  24836.                 case token=='{'
  24837.                         return this.parseObject()
  24838.                 case token=='['
  24839.                         return this.parseArray()
  24840.                 otherwise
  24841.                         this.setError('Unexpected token')
  24842.                 endcase
  24843.         return
  24844.                
  24845.        
  24846.         function expectedKeyword(cWord,eValue)
  24847.                 for i=1 to len(cWord)
  24848.                         cChar = this.getChar()
  24849.                         if cChar <> substr(cWord,i,1)
  24850.                                 this.setError("Expected keyword '" + cWord + "'")
  24851.                                 return ''
  24852.                         endif
  24853.                         this.nPos = this.nPos + 1
  24854.                 next
  24855.         return eValue
  24856.        
  24857.  
  24858.         function parseObject()
  24859.         local retval, cPropName, xValue
  24860.                 retval = createObject('myObj')
  24861.                 this.nPos = this.nPos + 1 && Eat {
  24862.                 if this.getToken()<>'}'
  24863.                         do while .t.
  24864.                                 cPropName = this.parseString()
  24865.                                 if not empty(this.cError)
  24866.                                         return null
  24867.                                 endif
  24868.                                 if this.getToken()<>':'
  24869.                                         this.setError("Expected ':' when parsing object")
  24870.                                         return null
  24871.                                 endif
  24872.                                 this.nPos = this.nPos + 1
  24873.                                 xValue = this.parseValue()
  24874.                                 if not empty(this.cError)
  24875.                                         return null
  24876.                                 endif                          
  24877.                                 ** Debug ? cPropName, type('xValue')
  24878.                                 retval.set(cPropName, xValue)
  24879.                                 if this.getToken()<>','
  24880.                                         exit
  24881.                                 endif
  24882.                                 this.nPos = this.nPos + 1
  24883.                         enddo
  24884.                 endif
  24885.                 if this.getToken()<>'}'
  24886.                         this.setError("Expected '}' at the end of object")
  24887.                         return null
  24888.                 endif
  24889.                 this.nPos = this.nPos + 1
  24890.         return retval
  24891.  
  24892.  
  24893.         function parseArray()
  24894.         local retVal, xValue
  24895.                 retval = createObject('MyArray')
  24896.                 this.nPos = this.nPos + 1       && Eat [
  24897.                 if this.getToken() <> ']'
  24898.                         do while .t.
  24899.                                 xValue = this.parseValue()
  24900.                                 if not empty(this.cError)
  24901.                                         return null
  24902.                                 endif
  24903.                                 retval.add( xValue )
  24904.                                 if this.getToken()<>','
  24905.                                         exit
  24906.                                 endif
  24907.                                 this.nPos = this.nPos + 1
  24908.                         enddo
  24909.                         if this.getToken() <> ']'
  24910.                                 this.setError('Expected ] at the end of array')
  24911.                                 return null
  24912.                         endif
  24913.                 endif
  24914.                 this.nPos = this.nPos + 1
  24915.         return retval
  24916.        
  24917.  
  24918.         function parseString()
  24919.         local cRetVal, c
  24920.                 if this.getToken()<>'"'
  24921.                         this.setError('Expected "')
  24922.                         return ''
  24923.                 endif
  24924.                 this.nPos = this.nPos + 1       && Eat "
  24925.                 cRetVal = ''
  24926.                 do while .t.
  24927.                         c = this.getChar()
  24928.                         if c==''
  24929.                                 return ''
  24930.                         endif
  24931.                         if c == '"'
  24932.                                 this.nPos = this.nPos + 1
  24933.                                 exit
  24934.                         endif
  24935.                         if c == '\'
  24936.                                 this.nPos = this.nPos + 1
  24937.                                 if (this.nPos>this.nLen)
  24938.                                         this.setError('\\ at the end of input')
  24939.                                         return ''
  24940.                                 endif
  24941.                                 c = this.getChar()
  24942.                                 if c==''
  24943.                                         return ''
  24944.                                 endif
  24945.                                 do case
  24946.                                 case c=='"'
  24947.                                         c='"'
  24948.                                 case c=='\'
  24949.                                         c='\'
  24950.                                 case c=='/'
  24951.                                         c='/'
  24952.                                 case c=='b'
  24953.                                         c=chr(8)
  24954.                                 case c=='t'
  24955.                                         c=chr(9)
  24956.                                 case c=='n'
  24957.                                         c=chr(10)
  24958.                                 case c=='f'
  24959.                                         c=chr(12)
  24960.                                 case c=='r'
  24961.                                         c=chr(13)
  24962.                                 otherwise
  24963.                                         ******* FALTAN LOS UNICODE
  24964.                                         this.setError('Invalid escape sequence in string literal')
  24965.                                         return ''
  24966.                                 endcase
  24967.                         endif
  24968.                         cRetVal = cRetVal + c
  24969.                         this.nPos = this.nPos + 1
  24970.                 enddo
  24971.         return cRetVal
  24972.                                        
  24973.  
  24974.         **** Pendiente numeros con E
  24975.         function parseNumber()
  24976.         local nStartPos,c, isInt, cNumero
  24977.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  24978.                         this.setError('Expected number literal')
  24979.                         return 0
  24980.                 endif
  24981.                 nStartPos = this.nPos
  24982.                 c = this.getChar()
  24983.                 if c == '-'
  24984.                         c = this.nextChar()
  24985.                 endif
  24986.                 if c == '0'
  24987.                         c = this.nextChar()
  24988.                 else
  24989.                         if isdigit(c)
  24990.                                 c = this.nextChar()
  24991.                                 do while isdigit(c)
  24992.                                         c = this.nextChar()
  24993.                                 enddo
  24994.                         else
  24995.                                 this.setError('Expected digit when parsing number')
  24996.                                 return 0
  24997.                         endif
  24998.                 endif
  24999.                
  25000.                 isInt = .t.
  25001.                 if c=='.'
  25002.                         c = this.nextChar()
  25003.                         if isdigit(c)
  25004.                                 c = this.nextChar()
  25005.                                 isInt = .f.
  25006.                                 do while isDigit(c)
  25007.                                         c = this.nextChar()
  25008.                                 enddo
  25009.                         else
  25010.                                 this.setError('Expected digit following dot comma')
  25011.                                 return 0
  25012.                         endif
  25013.                 endif
  25014.                
  25015.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  25016.         return val(cNumero)
  25017.  
  25018.  
  25019.  
  25020.         function getToken()
  25021.         local char1
  25022.                 do while .t.
  25023.                         if this.nPos > this.nLen
  25024.                                 return null
  25025.                         endif
  25026.                         char1 = substr(this.cJson, this.nPos, 1)
  25027.                         if char1==' '
  25028.                                 this.nPos = this.nPos + 1
  25029.                                 loop
  25030.                         endif
  25031.                         return char1
  25032.                 enddo
  25033.         return
  25034.        
  25035.                
  25036.                
  25037.         function getChar()
  25038.                 if this.nPos > this.nLen
  25039.                         this.setError('Unexpected end of JSON stream')
  25040.                         return ''
  25041.                 endif
  25042.         return substr(this.cJson, this.nPos, 1)
  25043.        
  25044.         function nextChar()
  25045.                 this.nPos = this.nPos + 1
  25046.                 if this.nPos > this.nLen
  25047.                         return ''
  25048.                 endif
  25049.         return substr(this.cJson, this.nPos, 1)
  25050.        
  25051.         function setError(cMsg)
  25052.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  25053.         return
  25054.  
  25055.  
  25056.         function fixUnicode(cStr)
  25057.                 cStr = StrTran(cStr,'\u00e1','á')
  25058.                 cStr = StrTran(cStr,'\u00e9','é')
  25059.                 cStr = StrTran(cStr,'\u00ed','í')
  25060.                 cStr = StrTran(cStr,'\u00f3','ó')
  25061.                 cStr = StrTran(cStr,'\u00fa','ú')
  25062.                 cStr = StrTran(cStr,'\u00c1','Á')
  25063.                 cStr = StrTran(cStr,'\u00c9','É')
  25064.                 cStr = StrTran(cStr,'\u00cd','Í')
  25065.                 cStr = StrTran(cStr,'\u00d3','Ó')
  25066.                 cStr = StrTran(cStr,'\u00da','Ú')
  25067.                 cStr = StrTran(cStr,'\u00f1','ń')
  25068.                 cStr = StrTran(cStr,'\u00d1','Ń')
  25069.         return cStr
  25070.  
  25071.  
  25072.  
  25073. enddefine
  25074.  
  25075.  
  25076.  
  25077.  
  25078.  
  25079. *
  25080. * class used to return an array
  25081. *
  25082. define class myArray as custom
  25083.         nSize = 0
  25084.         dimension array[1]
  25085.  
  25086.         function add(xExpr)
  25087.                 this.nSize = this.nSize + 1
  25088.                 dimension this.array[this.nSize]
  25089.                 this.array[this.nSize] = xExpr
  25090.         return
  25091.  
  25092.         function get(n)
  25093.         return this.array[n]
  25094.  
  25095. enddefine
  25096.  
  25097.  
  25098.  
  25099. *
  25100. * class used to simulate an object
  25101. * all properties are prefixed with 'prop' to permit property names like: error, init
  25102. * that already exists like vfp methods
  25103. *
  25104. define class myObj as custom
  25105. Hidden ;
  25106.         ClassLibrary,Comment, ;
  25107.         BaseClass,ControlCount, ;
  25108.         Controls,Objects,Object,;
  25109.         Height,HelpContextID,Left,Name, ;
  25110.         Parent,ParentClass,Picture, ;
  25111.         Tag,Top,WhatsThisHelpID,Width
  25112.                
  25113.         function set(cPropName, xValue)
  25114.                 cPropName = '_'+cPropName
  25115.                 if type('this.'+cPropName)=='U'
  25116.                         this.addProperty(cPropName,xValue)
  25117.                 else
  25118.                         local cmd
  25119.                         cmd = 'this.'+cPropName+'=xValue'
  25120.                         &cmd
  25121.                 endif
  25122.         return
  25123.        
  25124.         procedure get (cPropName)
  25125.                 cPropName = '_'+cPropName
  25126.                 If type('this.'+cPropName)=='U'
  25127.                         return ''
  25128.                 Else
  25129.                         local cmd
  25130.                         cmd = 'return this.'+cPropName
  25131.                         &cmd
  25132.                 endif
  25133.         return ''
  25134. enddefine
  25135.  
  25136.  
  25137.  
  25138.  
  25139.  
  25140. function testJsonClass
  25141.         clear
  25142.         set decimal to 10
  25143.         oJson = newObject('json')
  25144.        
  25145.        
  25146.         ? 'Test Basic Types'
  25147.         ? '----------------'
  25148.         ? oJson.decode('null')
  25149.         ? oJson.decode('true')
  25150.         ? oJson.decode('false')
  25151.         ?
  25152.         ? oJson.decode('791123')
  25153.         ? oJson.decode('791123.45')
  25154.         ? oJson.decode('791123.45.')
  25155.         ? oJson.decode('"nacho gtz"')
  25156.         if not empty(oJson.cError)
  25157.                 ? oJson.cError
  25158.                 return
  25159.         endif
  25160.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  25161.         if not empty(oJson.cError)
  25162.                 ? oJson.cError
  25163.                 return
  25164.         endif
  25165.        
  25166.         ? 'Test Array'
  25167.         ? '----------'
  25168.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  25169.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  25170.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  25171.         nombres = arr.get(1)
  25172.         edades  = arr.get(2)
  25173.         ? nombres.get(1), edades.get(1)
  25174.         ? nombres.get(2), edades.get(2)
  25175.         ? nombres.get(3), edades.get(3)
  25176.         ?
  25177.         ? 'Test Object'
  25178.         ? '-----------'
  25179.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  25180.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  25181.         ? obj._Nombre, obj._Edad, obj._IsGood
  25182.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  25183.         ? obj.get('jsonrpc'), obj._jsonrpc
  25184.         ? obj.get('id'), obj._id
  25185.         ? obj.get('method'), obj._method
  25186.         ? obj._Params.array[1], obj._Params.get(1)
  25187.  
  25188.         ?
  25189.         ? 'Test nested object'
  25190.         ? '------------------'
  25191.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  25192.         obj = oJson.decode(cJson)
  25193.         if not empty(oJson.cError)
  25194.                 ? oJson.cError
  25195.                 return
  25196.         endif
  25197.         ? cJson
  25198.         ? 'method -->',obj._method
  25199.         ? 'usrkey -->',obj._params._data._usrkey
  25200.         ? 'sendto -->',obj._params._data._sendto
  25201.         ? 'name  --->',obj._params._data._name
  25202.         ? 'expires ->',obj._params._data._expires
  25203.  
  25204.         ?
  25205.         ? 'Test empty object'
  25206.         ? '-----------------'
  25207.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  25208.         obj = oJson.decode(cJson)
  25209.         if not empty(oJson.cError)     
  25210.                 ? oJson.cError
  25211.                 return
  25212.         endif
  25213.         ? cJson
  25214.         ? 'result -->',obj._result, obj.get('result')
  25215.         oError = obj.get('error')
  25216.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  25217.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  25218.         ? 'id  ----->',obj._id, obj.get('id')
  25219.         ?  type("oError._code")
  25220.  
  25221.         ?
  25222.         ? 'Probar decode-enconde-decode-encode'
  25223.         ? '------------------------------------'
  25224.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  25225.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  25226.         ? cJson
  25227.         oSmtp = json_decode(cJson)
  25228.         cJson =  json_encode(oSmtp)
  25229.         ? cJson
  25230.         oSmtp = json_decode(cJson)
  25231.         cJson =  json_encode(oSmtp)
  25232.         ? cJson
  25233.  
  25234.         * Probar falla
  25235.         ?
  25236.         ? 'Probar una falla en el json'
  25237.         ? '---------------------------'
  25238.         cJson = ' {"server":"", "user":"", "password":"" ,'
  25239.         oSmtp = json_decode(cJson)
  25240.         if not empty(json_getErrorMsg())
  25241.                 ? json_getErrorMsg()
  25242.         endif
  25243.  
  25244.         ?
  25245.         ? 'Pruebas Finalizadas'
  25246. retur
  25247. * json class
  25248. *
  25249. *
  25250. define class json as custom
  25251.  
  25252.  
  25253.         nPos=0
  25254.         nLen=0
  25255.         cJson=''
  25256.         cError=''
  25257.  
  25258.  
  25259.         *
  25260.         * Genera el codigo cJson para parametro que se manda
  25261.         *
  25262.         function encode(xExpr)
  25263.         local cTipo
  25264.                 * Cuando se manda una arreglo,
  25265.                 if type('ALen(xExpr)')=='N'
  25266.                         cTipo = 'A'
  25267.                 Else
  25268.                         cTipo = VarType(xExpr)
  25269.                 Endif
  25270.                
  25271.                 Do Case
  25272.                 Case cTipo=='D'
  25273.                         return '"'+dtos(xExpr)+'"'
  25274.                 Case cTipo=='N'
  25275.                         return Transform(xExpr)
  25276.                 Case cTipo=='L'
  25277.                         return iif(xExpr,'true','false')
  25278.                 Case cTipo=='X'
  25279.                         return 'null'
  25280.                 Case cTipo=='C'
  25281.                         xExpr = allt(xExpr)
  25282.                         xExpr = StrTran(xExpr, '\', '\\' )
  25283.                         xExpr = StrTran(xExpr, '/', '\/' )
  25284.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  25285.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  25286.                         xExpr = StrTran(xExpr, '"', '\"' )
  25287.                         return '"'+xExpr+'"'
  25288.  
  25289.                 case cTipo=='O'
  25290.                         local cProp, cJsonValue, cRetVal, aProp[1]
  25291.                         =AMembers(aProp,xExpr)
  25292.                         cRetVal = ''
  25293.                         for each cProp in aProp
  25294.                                 *? cProp
  25295.                                 *? cRetVal
  25296.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  25297.                                         * algunas propiedades pueden no estar definidas
  25298.                                         * como: activecontrol, parent, etc
  25299.                                         loop
  25300.                                 endif
  25301.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  25302.                                         *
  25303.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  25304.                                         *
  25305.                                         Local i,nTotElem
  25306.                                         cJsonValue = ''
  25307.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  25308.                                         For i=1 to nTotElem
  25309.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  25310.                                         Next
  25311.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  25312.                                 else
  25313.                                         *
  25314.                                         * es otro tipo de dato normal C, N, L
  25315.                                         *
  25316.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  25317.                                 endif
  25318.                                 if left(cProp,1)=='_'
  25319.                                         cProp = substr(cProp,2)
  25320.                                 endif
  25321.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  25322.                         next
  25323.                         return '{' + substr(cRetVal,2) + '}'
  25324.  
  25325.                 case cTipo=='A'
  25326.                         local valor, cRetVal
  25327.                         cRetVal = ''   
  25328.                         for each valor in xExpr
  25329.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  25330.                         next
  25331.                         return  '[' + substr(cRetVal,2) + ']'
  25332.                        
  25333.                 endcase
  25334.  
  25335.         return ''
  25336.  
  25337.  
  25338.  
  25339.  
  25340.  
  25341.         *
  25342.         * regresa un elemento representado por la cadena json que se manda
  25343.         *
  25344.        
  25345.         function decode(cJson)
  25346.         local retValue
  25347.                 cJson = StrTran(cJson,chr(9),'')
  25348.                 cJson = StrTran(cJson,chr(10),'')
  25349.                 cJson = StrTran(cJson,chr(13),'')
  25350.                 cJson = this.fixUnicode(cJson)
  25351.                 this.nPos  = 1
  25352.                 this.cJson = cJson
  25353.                 this.nLen  = len(cJson)
  25354.                 this.cError = ''
  25355.                 retValue = this.parsevalue()
  25356.                 if not empty(this.cError)
  25357.                         return null
  25358.                 endif
  25359.                 if this.getToken()<>null
  25360.                         this.setError('Junk at the end of JSON input')
  25361.                         return null
  25362.                 endif
  25363.         return retValue
  25364.                
  25365.        
  25366.         function parseValue()
  25367.         local token
  25368.                 token = this.getToken()
  25369.                 if token==null
  25370.                         this.setError('Nothing to parse')
  25371.                         return null
  25372.                 endif
  25373.                 do case
  25374.                 case token=='"'
  25375.                         return this.parseString()
  25376.                 case isdigit(token) or token=='-'
  25377.                         return this.parseNumber()
  25378.                 case token=='n'
  25379.                         return this.expectedKeyword('null',null)
  25380.                 case token=='f'
  25381.                         return this.expectedKeyword('false',.f.)
  25382.                 case token=='t'
  25383.                         return this.expectedKeyword('true',.t.)
  25384.                 case token=='{'
  25385.                         return this.parseObject()
  25386.                 case token=='['
  25387.                         return this.parseArray()
  25388.                 otherwise
  25389.                         this.setError('Unexpected token')
  25390.                 endcase
  25391.         return
  25392.                
  25393.        
  25394.         function expectedKeyword(cWord,eValue)
  25395.                 for i=1 to len(cWord)
  25396.                         cChar = this.getChar()
  25397.                         if cChar <> substr(cWord,i,1)
  25398.                                 this.setError("Expected keyword '" + cWord + "'")
  25399.                                 return ''
  25400.                         endif
  25401.                         this.nPos = this.nPos + 1
  25402.                 next
  25403.         return eValue
  25404.        
  25405.  
  25406.         function parseObject()
  25407.         local retval, cPropName, xValue
  25408.                 retval = createObject('myObj')
  25409.                 this.nPos = this.nPos + 1 && Eat {
  25410.                 if this.getToken()<>'}'
  25411.                         do while .t.
  25412.                                 cPropName = this.parseString()
  25413.                                 if not empty(this.cError)
  25414.                                         return null
  25415.                                 endif
  25416.                                 if this.getToken()<>':'
  25417.                                         this.setError("Expected ':' when parsing object")
  25418.                                         return null
  25419.                                 endif
  25420.                                 this.nPos = this.nPos + 1
  25421.                                 xValue = this.parseValue()
  25422.                                 if not empty(this.cError)
  25423.                                         return null
  25424.                                 endif                          
  25425.                                 ** Debug ? cPropName, type('xValue')
  25426.                                 retval.set(cPropName, xValue)
  25427.                                 if this.getToken()<>','
  25428.                                         exit
  25429.                                 endif
  25430.                                 this.nPos = this.nPos + 1
  25431.                         enddo
  25432.                 endif
  25433.                 if this.getToken()<>'}'
  25434.                         this.setError("Expected '}' at the end of object")
  25435.                         return null
  25436.                 endif
  25437.                 this.nPos = this.nPos + 1
  25438.         return retval
  25439.  
  25440.  
  25441.         function parseArray()
  25442.         local retVal, xValue
  25443.                 retval = createObject('MyArray')
  25444.                 this.nPos = this.nPos + 1       && Eat [
  25445.                 if this.getToken() <> ']'
  25446.                         do while .t.
  25447.                                 xValue = this.parseValue()
  25448.                                 if not empty(this.cError)
  25449.                                         return null
  25450.                                 endif
  25451.                                 retval.add( xValue )
  25452.                                 if this.getToken()<>','
  25453.                                         exit
  25454.                                 endif
  25455.                                 this.nPos = this.nPos + 1
  25456.                         enddo
  25457.                         if this.getToken() <> ']'
  25458.                                 this.setError('Expected ] at the end of array')
  25459.                                 return null
  25460.                         endif
  25461.                 endif
  25462.                 this.nPos = this.nPos + 1
  25463.         return retval
  25464.        
  25465.  
  25466.         function parseString()
  25467.         local cRetVal, c
  25468.                 if this.getToken()<>'"'
  25469.                         this.setError('Expected "')
  25470.                         return ''
  25471.                 endif
  25472.                 this.nPos = this.nPos + 1       && Eat "
  25473.                 cRetVal = ''
  25474.                 do while .t.
  25475.                         c = this.getChar()
  25476.                         if c==''
  25477.                                 return ''
  25478.                         endif
  25479.                         if c == '"'
  25480.                                 this.nPos = this.nPos + 1
  25481.                                 exit
  25482.                         endif
  25483.                         if c == '\'
  25484.                                 this.nPos = this.nPos + 1
  25485.                                 if (this.nPos>this.nLen)
  25486.                                         this.setError('\\ at the end of input')
  25487.                                         return ''
  25488.                                 endif
  25489.                                 c = this.getChar()
  25490.                                 if c==''
  25491.                                         return ''
  25492.                                 endif
  25493.                                 do case
  25494.                                 case c=='"'
  25495.                                         c='"'
  25496.                                 case c=='\'
  25497.                                         c='\'
  25498.                                 case c=='/'
  25499.                                         c='/'
  25500.                                 case c=='b'
  25501.                                         c=chr(8)
  25502.                                 case c=='t'
  25503.                                         c=chr(9)
  25504.                                 case c=='n'
  25505.                                         c=chr(10)
  25506.                                 case c=='f'
  25507.                                         c=chr(12)
  25508.                                 case c=='r'
  25509.                                         c=chr(13)
  25510.                                 otherwise
  25511.                                         ******* FALTAN LOS UNICODE
  25512.                                         this.setError('Invalid escape sequence in string literal')
  25513.                                         return ''
  25514.                                 endcase
  25515.                         endif
  25516.                         cRetVal = cRetVal + c
  25517.                         this.nPos = this.nPos + 1
  25518.                 enddo
  25519.         return cRetVal
  25520.                                        
  25521.  
  25522.         **** Pendiente numeros con E
  25523.         function parseNumber()
  25524.         local nStartPos,c, isInt, cNumero
  25525.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  25526.                         this.setError('Expected number literal')
  25527.                         return 0
  25528.                 endif
  25529.                 nStartPos = this.nPos
  25530.                 c = this.getChar()
  25531.                 if c == '-'
  25532.                         c = this.nextChar()
  25533.                 endif
  25534.                 if c == '0'
  25535.                         c = this.nextChar()
  25536.                 else
  25537.                         if isdigit(c)
  25538.                                 c = this.nextChar()
  25539.                                 do while isdigit(c)
  25540.                                         c = this.nextChar()
  25541.                                 enddo
  25542.                         else
  25543.                                 this.setError('Expected digit when parsing number')
  25544.                                 return 0
  25545.                         endif
  25546.                 endif
  25547.                
  25548.                 isInt = .t.
  25549.                 if c=='.'
  25550.                         c = this.nextChar()
  25551.                         if isdigit(c)
  25552.                                 c = this.nextChar()
  25553.                                 isInt = .f.
  25554.                                 do while isDigit(c)
  25555.                                         c = this.nextChar()
  25556.                                 enddo
  25557.                         else
  25558.                                 this.setError('Expected digit following dot comma')
  25559.                                 return 0
  25560.                         endif
  25561.                 endif
  25562.                
  25563.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  25564.         return val(cNumero)
  25565.  
  25566.  
  25567.  
  25568.         function getToken()
  25569.         local char1
  25570.                 do while .t.
  25571.                         if this.nPos > this.nLen
  25572.                                 return null
  25573.                         endif
  25574.                         char1 = substr(this.cJson, this.nPos, 1)
  25575.                         if char1==' '
  25576.                                 this.nPos = this.nPos + 1
  25577.                                 loop
  25578.                         endif
  25579.                         return char1
  25580.                 enddo
  25581.         return
  25582.        
  25583.                
  25584.                
  25585.         function getChar()
  25586.                 if this.nPos > this.nLen
  25587.                         this.setError('Unexpected end of JSON stream')
  25588.                         return ''
  25589.                 endif
  25590.         return substr(this.cJson, this.nPos, 1)
  25591.        
  25592.         function nextChar()
  25593.                 this.nPos = this.nPos + 1
  25594.                 if this.nPos > this.nLen
  25595.                         return ''
  25596.                 endif
  25597.         return substr(this.cJson, this.nPos, 1)
  25598.        
  25599.         function setError(cMsg)
  25600.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  25601.         return
  25602.  
  25603.  
  25604.         function fixUnicode(cStr)
  25605.                 cStr = StrTran(cStr,'\u00e1','á')
  25606.                 cStr = StrTran(cStr,'\u00e9','é')
  25607.                 cStr = StrTran(cStr,'\u00ed','í')
  25608.                 cStr = StrTran(cStr,'\u00f3','ó')
  25609.                 cStr = StrTran(cStr,'\u00fa','ú')
  25610.                 cStr = StrTran(cStr,'\u00c1','Á')
  25611.                 cStr = StrTran(cStr,'\u00c9','É')
  25612.                 cStr = StrTran(cStr,'\u00cd','Í')
  25613.                 cStr = StrTran(cStr,'\u00d3','Ó')
  25614.                 cStr = StrTran(cStr,'\u00da','Ú')
  25615.                 cStr = StrTran(cStr,'\u00f1','ń')
  25616.                 cStr = StrTran(cStr,'\u00d1','Ń')
  25617.         return cStr
  25618.  
  25619.  
  25620.  
  25621. enddefine
  25622.  
  25623.  
  25624.  
  25625.  
  25626.  
  25627. *
  25628. * class used to return an array
  25629. *
  25630. define class myArray as custom
  25631.         nSize = 0
  25632.         dimension array[1]
  25633.  
  25634.         function add(xExpr)
  25635.                 this.nSize = this.nSize + 1
  25636.                 dimension this.array[this.nSize]
  25637.                 this.array[this.nSize] = xExpr
  25638.         return
  25639.  
  25640.         function get(n)
  25641.         return this.array[n]
  25642.  
  25643. enddefine
  25644.  
  25645.  
  25646.  
  25647. *
  25648. * class used to simulate an object
  25649. * all properties are prefixed with 'prop' to permit property names like: error, init
  25650. * that already exists like vfp methods
  25651. *
  25652. define class myObj as custom
  25653. Hidden ;
  25654.         ClassLibrary,Comment, ;
  25655.         BaseClass,ControlCount, ;
  25656.         Controls,Objects,Object,;
  25657.         Height,HelpContextID,Left,Name, ;
  25658.         Parent,ParentClass,Picture, ;
  25659.         Tag,Top,WhatsThisHelpID,Width
  25660.                
  25661.         function set(cPropName, xValue)
  25662.                 cPropName = '_'+cPropName
  25663.                 if type('this.'+cPropName)=='U'
  25664.                         this.addProperty(cPropName,xValue)
  25665.                 else
  25666.                         local cmd
  25667.                         cmd = 'this.'+cPropName+'=xValue'
  25668.                         &cmd
  25669.                 endif
  25670.         return
  25671.        
  25672.         procedure get (cPropName)
  25673.                 cPropName = '_'+cPropName
  25674.                 If type('this.'+cPropName)=='U'
  25675.                         return ''
  25676.                 Else
  25677.                         local cmd
  25678.                         cmd = 'return this.'+cPropName
  25679.                         &cmd
  25680.                 endif
  25681.         return ''
  25682. enddefine
  25683.  
  25684.  
  25685.  
  25686.  
  25687.  
  25688. function testJsonClass
  25689.         clear
  25690.         set decimal to 10
  25691.         oJson = newObject('json')
  25692.        
  25693.        
  25694.         ? 'Test Basic Types'
  25695.         ? '----------------'
  25696.         ? oJson.decode('null')
  25697.         ? oJson.decode('true')
  25698.         ? oJson.decode('false')
  25699.         ?
  25700.         ? oJson.decode('791123')
  25701.         ? oJson.decode('791123.45')
  25702.         ? oJson.decode('791123.45.')
  25703.         ? oJson.decode('"nacho gtz"')
  25704.         if not empty(oJson.cError)
  25705.                 ? oJson.cError
  25706.                 return
  25707.         endif
  25708.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  25709.         if not empty(oJson.cError)
  25710.                 ? oJson.cError
  25711.                 return
  25712.         endif
  25713.        
  25714.         ? 'Test Array'
  25715.         ? '----------'
  25716.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  25717.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  25718.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  25719.         nombres = arr.get(1)
  25720.         edades  = arr.get(2)
  25721.         ? nombres.get(1), edades.get(1)
  25722.         ? nombres.get(2), edades.get(2)
  25723.         ? nombres.get(3), edades.get(3)
  25724.         ?
  25725.         ? 'Test Object'
  25726.         ? '-----------'
  25727.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  25728.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  25729.         ? obj._Nombre, obj._Edad, obj._IsGood
  25730.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  25731.         ? obj.get('jsonrpc'), obj._jsonrpc
  25732.         ? obj.get('id'), obj._id
  25733.         ? obj.get('method'), obj._method
  25734.         ? obj._Params.array[1], obj._Params.get(1)
  25735.  
  25736.         ?
  25737.         ? 'Test nested object'
  25738.         ? '------------------'
  25739.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  25740.         obj = oJson.decode(cJson)
  25741.         if not empty(oJson.cError)
  25742.                 ? oJson.cError
  25743.                 return
  25744.         endif
  25745.         ? cJson
  25746.         ? 'method -->',obj._method
  25747.         ? 'usrkey -->',obj._params._data._usrkey
  25748.         ? 'sendto -->',obj._params._data._sendto
  25749.         ? 'name  --->',obj._params._data._name
  25750.         ? 'expires ->',obj._params._data._expires
  25751.  
  25752.         ?
  25753.         ? 'Test empty object'
  25754.         ? '-----------------'
  25755.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  25756.         obj = oJson.decode(cJson)
  25757.         if not empty(oJson.cError)     
  25758.                 ? oJson.cError
  25759.                 return
  25760.         endif
  25761.         ? cJson
  25762.         ? 'result -->',obj._result, obj.get('result')
  25763.         oError = obj.get('error')
  25764.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  25765.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  25766.         ? 'id  ----->',obj._id, obj.get('id')
  25767.         ?  type("oError._code")
  25768.  
  25769.         ?
  25770.         ? 'Probar decode-enconde-decode-encode'
  25771.         ? '------------------------------------'
  25772.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  25773.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  25774.         ? cJson
  25775.         oSmtp = json_decode(cJson)
  25776.         cJson =  json_encode(oSmtp)
  25777.         ? cJson
  25778.         oSmtp = json_decode(cJson)
  25779.         cJson =  json_encode(oSmtp)
  25780.         ? cJson
  25781.  
  25782.         * Probar falla
  25783.         ?
  25784.         ? 'Probar una falla en el json'
  25785.         ? '---------------------------'
  25786.         cJson = ' {"server":"", "user":"", "password":"" ,'
  25787.         oSmtp = json_decode(cJson)
  25788.         if not empty(json_getErrorMsg())
  25789.                 ? json_getErrorMsg()
  25790.         endif
  25791.  
  25792.         ?
  25793.         ? 'Pruebas Finalizadas'
  25794. retur
  25795. *
  25796. *
  25797. define class json as custom
  25798.  
  25799.  
  25800.         nPos=0
  25801.         nLen=0
  25802.         cJson=''
  25803.         cError=''
  25804.  
  25805.  
  25806.         *
  25807.         * Genera el codigo cJson para parametro que se manda
  25808.         *
  25809.         function encode(xExpr)
  25810.         local cTipo
  25811.                 * Cuando se manda una arreglo,
  25812.                 if type('ALen(xExpr)')=='N'
  25813.                         cTipo = 'A'
  25814.                 Else
  25815.                         cTipo = VarType(xExpr)
  25816.                 Endif
  25817.                
  25818.                 Do Case
  25819.                 Case cTipo=='D'
  25820.                         return '"'+dtos(xExpr)+'"'
  25821.                 Case cTipo=='N'
  25822.                         return Transform(xExpr)
  25823.                 Case cTipo=='L'
  25824.                         return iif(xExpr,'true','false')
  25825.                 Case cTipo=='X'
  25826.                         return 'null'
  25827.                 Case cTipo=='C'
  25828.                         xExpr = allt(xExpr)
  25829.                         xExpr = StrTran(xExpr, '\', '\\' )
  25830.                         xExpr = StrTran(xExpr, '/', '\/' )
  25831.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  25832.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  25833.                         xExpr = StrTran(xExpr, '"', '\"' )
  25834.                         return '"'+xExpr+'"'
  25835.  
  25836.                 case cTipo=='O'
  25837.                         local cProp, cJsonValue, cRetVal, aProp[1]
  25838.                         =AMembers(aProp,xExpr)
  25839.                         cRetVal = ''
  25840.                         for each cProp in aProp
  25841.                                 *? cProp
  25842.                                 *? cRetVal
  25843.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  25844.                                         * algunas propiedades pueden no estar definidas
  25845.                                         * como: activecontrol, parent, etc
  25846.                                         loop
  25847.                                 endif
  25848.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  25849.                                         *
  25850.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  25851.                                         *
  25852.                                         Local i,nTotElem
  25853.                                         cJsonValue = ''
  25854.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  25855.                                         For i=1 to nTotElem
  25856.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  25857.                                         Next
  25858.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  25859.                                 else
  25860.                                         *
  25861.                                         * es otro tipo de dato normal C, N, L
  25862.                                         *
  25863.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  25864.                                 endif
  25865.                                 if left(cProp,1)=='_'
  25866.                                         cProp = substr(cProp,2)
  25867.                                 endif
  25868.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  25869.                         next
  25870.                         return '{' + substr(cRetVal,2) + '}'
  25871.  
  25872.                 case cTipo=='A'
  25873.                         local valor, cRetVal
  25874.                         cRetVal = ''   
  25875.                         for each valor in xExpr
  25876.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  25877.                         next
  25878.                         return  '[' + substr(cRetVal,2) + ']'
  25879.                        
  25880.                 endcase
  25881.  
  25882.         return ''
  25883.  
  25884.  
  25885.  
  25886.  
  25887.  
  25888.         *
  25889.         * regresa un elemento representado por la cadena json que se manda
  25890.         *
  25891.        
  25892.         function decode(cJson)
  25893.         local retValue
  25894.                 cJson = StrTran(cJson,chr(9),'')
  25895.                 cJson = StrTran(cJson,chr(10),'')
  25896.                 cJson = StrTran(cJson,chr(13),'')
  25897.                 cJson = this.fixUnicode(cJson)
  25898.                 this.nPos  = 1
  25899.                 this.cJson = cJson
  25900.                 this.nLen  = len(cJson)
  25901.                 this.cError = ''
  25902.                 retValue = this.parsevalue()
  25903.                 if not empty(this.cError)
  25904.                         return null
  25905.                 endif
  25906.                 if this.getToken()<>null
  25907.                         this.setError('Junk at the end of JSON input')
  25908.                         return null
  25909.                 endif
  25910.         return retValue
  25911.                
  25912.        
  25913.         function parseValue()
  25914.         local token
  25915.                 token = this.getToken()
  25916.                 if token==null
  25917.                         this.setError('Nothing to parse')
  25918.                         return null
  25919.                 endif
  25920.                 do case
  25921.                 case token=='"'
  25922.                         return this.parseString()
  25923.                 case isdigit(token) or token=='-'
  25924.                         return this.parseNumber()
  25925.                 case token=='n'
  25926.                         return this.expectedKeyword('null',null)
  25927.                 case token=='f'
  25928.                         return this.expectedKeyword('false',.f.)
  25929.                 case token=='t'
  25930.                         return this.expectedKeyword('true',.t.)
  25931.                 case token=='{'
  25932.                         return this.parseObject()
  25933.                 case token=='['
  25934.                         return this.parseArray()
  25935.                 otherwise
  25936.                         this.setError('Unexpected token')
  25937.                 endcase
  25938.         return
  25939.                
  25940.        
  25941.         function expectedKeyword(cWord,eValue)
  25942.                 for i=1 to len(cWord)
  25943.                         cChar = this.getChar()
  25944.                         if cChar <> substr(cWord,i,1)
  25945.                                 this.setError("Expected keyword '" + cWord + "'")
  25946.                                 return ''
  25947.                         endif
  25948.                         this.nPos = this.nPos + 1
  25949.                 next
  25950.         return eValue
  25951.        
  25952.  
  25953.         function parseObject()
  25954.         local retval, cPropName, xValue
  25955.                 retval = createObject('myObj')
  25956.                 this.nPos = this.nPos + 1 && Eat {
  25957.                 if this.getToken()<>'}'
  25958.                         do while .t.
  25959.                                 cPropName = this.parseString()
  25960.                                 if not empty(this.cError)
  25961.                                         return null
  25962.                                 endif
  25963.                                 if this.getToken()<>':'
  25964.                                         this.setError("Expected ':' when parsing object")
  25965.                                         return null
  25966.                                 endif
  25967.                                 this.nPos = this.nPos + 1
  25968.                                 xValue = this.parseValue()
  25969.                                 if not empty(this.cError)
  25970.                                         return null
  25971.                                 endif                          
  25972.                                 ** Debug ? cPropName, type('xValue')
  25973.                                 retval.set(cPropName, xValue)
  25974.                                 if this.getToken()<>','
  25975.                                         exit
  25976.                                 endif
  25977.                                 this.nPos = this.nPos + 1
  25978.                         enddo
  25979.                 endif
  25980.                 if this.getToken()<>'}'
  25981.                         this.setError("Expected '}' at the end of object")
  25982.                         return null
  25983.                 endif
  25984.                 this.nPos = this.nPos + 1
  25985.         return retval
  25986.  
  25987.  
  25988.         function parseArray()
  25989.         local retVal, xValue
  25990.                 retval = createObject('MyArray')
  25991.                 this.nPos = this.nPos + 1       && Eat [
  25992.                 if this.getToken() <> ']'
  25993.                         do while .t.
  25994.                                 xValue = this.parseValue()
  25995.                                 if not empty(this.cError)
  25996.                                         return null
  25997.                                 endif
  25998.                                 retval.add( xValue )
  25999.                                 if this.getToken()<>','
  26000.                                         exit
  26001.                                 endif
  26002.                                 this.nPos = this.nPos + 1
  26003.                         enddo
  26004.                         if this.getToken() <> ']'
  26005.                                 this.setError('Expected ] at the end of array')
  26006.                                 return null
  26007.                         endif
  26008.                 endif
  26009.                 this.nPos = this.nPos + 1
  26010.         return retval
  26011.        
  26012.  
  26013.         function parseString()
  26014.         local cRetVal, c
  26015.                 if this.getToken()<>'"'
  26016.                         this.setError('Expected "')
  26017.                         return ''
  26018.                 endif
  26019.                 this.nPos = this.nPos + 1       && Eat "
  26020.                 cRetVal = ''
  26021.                 do while .t.
  26022.                         c = this.getChar()
  26023.                         if c==''
  26024.                                 return ''
  26025.                         endif
  26026.                         if c == '"'
  26027.                                 this.nPos = this.nPos + 1
  26028.                                 exit
  26029.                         endif
  26030.                         if c == '\'
  26031.                                 this.nPos = this.nPos + 1
  26032.                                 if (this.nPos>this.nLen)
  26033.                                         this.setError('\\ at the end of input')
  26034.                                         return ''
  26035.                                 endif
  26036.                                 c = this.getChar()
  26037.                                 if c==''
  26038.                                         return ''
  26039.                                 endif
  26040.                                 do case
  26041.                                 case c=='"'
  26042.                                         c='"'
  26043.                                 case c=='\'
  26044.                                         c='\'
  26045.                                 case c=='/'
  26046.                                         c='/'
  26047.                                 case c=='b'
  26048.                                         c=chr(8)
  26049.                                 case c=='t'
  26050.                                         c=chr(9)
  26051.                                 case c=='n'
  26052.                                         c=chr(10)
  26053.                                 case c=='f'
  26054.                                         c=chr(12)
  26055.                                 case c=='r'
  26056.                                         c=chr(13)
  26057.                                 otherwise
  26058.                                         ******* FALTAN LOS UNICODE
  26059.                                         this.setError('Invalid escape sequence in string literal')
  26060.                                         return ''
  26061.                                 endcase
  26062.                         endif
  26063.                         cRetVal = cRetVal + c
  26064.                         this.nPos = this.nPos + 1
  26065.                 enddo
  26066.         return cRetVal
  26067.                                        
  26068.  
  26069.         **** Pendiente numeros con E
  26070.         function parseNumber()
  26071.         local nStartPos,c, isInt, cNumero
  26072.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  26073.                         this.setError('Expected number literal')
  26074.                         return 0
  26075.                 endif
  26076.                 nStartPos = this.nPos
  26077.                 c = this.getChar()
  26078.                 if c == '-'
  26079.                         c = this.nextChar()
  26080.                 endif
  26081.                 if c == '0'
  26082.                         c = this.nextChar()
  26083.                 else
  26084.                         if isdigit(c)
  26085.                                 c = this.nextChar()
  26086.                                 do while isdigit(c)
  26087.                                         c = this.nextChar()
  26088.                                 enddo
  26089.                         else
  26090.                                 this.setError('Expected digit when parsing number')
  26091.                                 return 0
  26092.                         endif
  26093.                 endif
  26094.                
  26095.                 isInt = .t.
  26096.                 if c=='.'
  26097.                         c = this.nextChar()
  26098.                         if isdigit(c)
  26099.                                 c = this.nextChar()
  26100.                                 isInt = .f.
  26101.                                 do while isDigit(c)
  26102.                                         c = this.nextChar()
  26103.                                 enddo
  26104.                         else
  26105.                                 this.setError('Expected digit following dot comma')
  26106.                                 return 0
  26107.                         endif
  26108.                 endif
  26109.                
  26110.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  26111.         return val(cNumero)
  26112.  
  26113.  
  26114.  
  26115.         function getToken()
  26116.         local char1
  26117.                 do while .t.
  26118.                         if this.nPos > this.nLen
  26119.                                 return null
  26120.                         endif
  26121.                         char1 = substr(this.cJson, this.nPos, 1)
  26122.                         if char1==' '
  26123.                                 this.nPos = this.nPos + 1
  26124.                                 loop
  26125.                         endif
  26126.                         return char1
  26127.                 enddo
  26128.         return
  26129.        
  26130.                
  26131.                
  26132.         function getChar()
  26133.                 if this.nPos > this.nLen
  26134.                         this.setError('Unexpected end of JSON stream')
  26135.                         return ''
  26136.                 endif
  26137.         return substr(this.cJson, this.nPos, 1)
  26138.        
  26139.         function nextChar()
  26140.                 this.nPos = this.nPos + 1
  26141.                 if this.nPos > this.nLen
  26142.                         return ''
  26143.                 endif
  26144.         return substr(this.cJson, this.nPos, 1)
  26145.        
  26146.         function setError(cMsg)
  26147.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  26148.         return
  26149.  
  26150.  
  26151.         function fixUnicode(cStr)
  26152.                 cStr = StrTran(cStr,'\u00e1','á')
  26153.                 cStr = StrTran(cStr,'\u00e9','é')
  26154.                 cStr = StrTran(cStr,'\u00ed','í')
  26155.                 cStr = StrTran(cStr,'\u00f3','ó')
  26156.                 cStr = StrTran(cStr,'\u00fa','ú')
  26157.                 cStr = StrTran(cStr,'\u00c1','Á')
  26158.                 cStr = StrTran(cStr,'\u00c9','É')
  26159.                 cStr = StrTran(cStr,'\u00cd','Í')
  26160.                 cStr = StrTran(cStr,'\u00d3','Ó')
  26161.                 cStr = StrTran(cStr,'\u00da','Ú')
  26162.                 cStr = StrTran(cStr,'\u00f1','ń')
  26163.                 cStr = StrTran(cStr,'\u00d1','Ń')
  26164.         return cStr
  26165.  
  26166.  
  26167.  
  26168. enddefine
  26169.  
  26170.  
  26171.  
  26172.  
  26173.  
  26174. *
  26175. * class used to return an array
  26176. *
  26177. define class myArray as custom
  26178.         nSize = 0
  26179.         dimension array[1]
  26180.  
  26181.         function add(xExpr)
  26182.                 this.nSize = this.nSize + 1
  26183.                 dimension this.array[this.nSize]
  26184.                 this.array[this.nSize] = xExpr
  26185.         return
  26186.  
  26187.         function get(n)
  26188.         return this.array[n]
  26189.  
  26190. enddefine
  26191.  
  26192.  
  26193.  
  26194. *
  26195. * class used to simulate an object
  26196. * all properties are prefixed with 'prop' to permit property names like: error, init
  26197. * that already exists like vfp methods
  26198. *
  26199. define class myObj as custom
  26200. Hidden ;
  26201.         ClassLibrary,Comment, ;
  26202.         BaseClass,ControlCount, ;
  26203.         Controls,Objects,Object,;
  26204.         Height,HelpContextID,Left,Name, ;
  26205.         Parent,ParentClass,Picture, ;
  26206.         Tag,Top,WhatsThisHelpID,Width
  26207.                
  26208.         function set(cPropName, xValue)
  26209.                 cPropName = '_'+cPropName
  26210.                 if type('this.'+cPropName)=='U'
  26211.                         this.addProperty(cPropName,xValue)
  26212.                 else
  26213.                         local cmd
  26214.                         cmd = 'this.'+cPropName+'=xValue'
  26215.                         &cmd
  26216.                 endif
  26217.         return
  26218.        
  26219.         procedure get (cPropName)
  26220.                 cPropName = '_'+cPropName
  26221.                 If type('this.'+cPropName)=='U'
  26222.                         return ''
  26223.                 Else
  26224.                         local cmd
  26225.                         cmd = 'return this.'+cPropName
  26226.                         &cmd
  26227.                 endif
  26228.         return ''
  26229. enddefine
  26230.  
  26231.  
  26232.  
  26233.  
  26234.  
  26235. function testJsonClass
  26236.         clear
  26237.         set decimal to 10
  26238.         oJson = newObject('json')
  26239.        
  26240.        
  26241.         ? 'Test Basic Types'
  26242.         ? '----------------'
  26243.         ? oJson.decode('null')
  26244.         ? oJson.decode('true')
  26245.         ? oJson.decode('false')
  26246.         ?
  26247.         ? oJson.decode('791123')
  26248.         ? oJson.decode('791123.45')
  26249.         ? oJson.decode('791123.45.')
  26250.         ? oJson.decode('"nacho gtz"')
  26251.         if not empty(oJson.cError)
  26252.                 ? oJson.cError
  26253.                 return
  26254.         endif
  26255.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  26256.         if not empty(oJson.cError)
  26257.                 ? oJson.cError
  26258.                 return
  26259.         endif
  26260.        
  26261.         ? 'Test Array'
  26262.         ? '----------'
  26263.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  26264.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  26265.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  26266.         nombres = arr.get(1)
  26267.         edades  = arr.get(2)
  26268.         ? nombres.get(1), edades.get(1)
  26269.         ? nombres.get(2), edades.get(2)
  26270.         ? nombres.get(3), edades.get(3)
  26271.         ?
  26272.         ? 'Test Object'
  26273.         ? '-----------'
  26274.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  26275.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  26276.         ? obj._Nombre, obj._Edad, obj._IsGood
  26277.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  26278.         ? obj.get('jsonrpc'), obj._jsonrpc
  26279.         ? obj.get('id'), obj._id
  26280.         ? obj.get('method'), obj._method
  26281.         ? obj._Params.array[1], obj._Params.get(1)
  26282.  
  26283.         ?
  26284.         ? 'Test nested object'
  26285.         ? '------------------'
  26286.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  26287.         obj = oJson.decode(cJson)
  26288.         if not empty(oJson.cError)
  26289.                 ? oJson.cError
  26290.                 return
  26291.         endif
  26292.         ? cJson
  26293.         ? 'method -->',obj._method
  26294.         ? 'usrkey -->',obj._params._data._usrkey
  26295.         ? 'sendto -->',obj._params._data._sendto
  26296.         ? 'name  --->',obj._params._data._name
  26297.         ? 'expires ->',obj._params._data._expires
  26298.  
  26299.         ?
  26300.         ? 'Test empty object'
  26301.         ? '-----------------'
  26302.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  26303.         obj = oJson.decode(cJson)
  26304.         if not empty(oJson.cError)     
  26305.                 ? oJson.cError
  26306.                 return
  26307.         endif
  26308.         ? cJson
  26309.         ? 'result -->',obj._result, obj.get('result')
  26310.         oError = obj.get('error')
  26311.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  26312.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  26313.         ? 'id  ----->',obj._id, obj.get('id')
  26314.         ?  type("oError._code")
  26315.  
  26316.         ?
  26317.         ? 'Probar decode-enconde-decode-encode'
  26318.         ? '------------------------------------'
  26319.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  26320.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  26321.         ? cJson
  26322.         oSmtp = json_decode(cJson)
  26323.         cJson =  json_encode(oSmtp)
  26324.         ? cJson
  26325.         oSmtp = json_decode(cJson)
  26326.         cJson =  json_encode(oSmtp)
  26327.         ? cJson
  26328.  
  26329.         * Probar falla
  26330.         ?
  26331.         ? 'Probar una falla en el json'
  26332.         ? '---------------------------'
  26333.         cJson = ' {"server":"", "user":"", "password":"" ,'
  26334.         oSmtp = json_decode(cJson)
  26335.         if not empty(json_getErrorMsg())
  26336.                 ? json_getErrorMsg()
  26337.         endif
  26338.  
  26339.         ?
  26340.         ? 'Pruebas Finalizadas'
  26341. retur
  26342. *
  26343. define class json as custom
  26344.  
  26345.  
  26346.         nPos=0
  26347.         nLen=0
  26348.         cJson=''
  26349.         cError=''
  26350.  
  26351.  
  26352.         *
  26353.         * Genera el codigo cJson para parametro que se manda
  26354.         *
  26355.         function encode(xExpr)
  26356.         local cTipo
  26357.                 * Cuando se manda una arreglo,
  26358.                 if type('ALen(xExpr)')=='N'
  26359.                         cTipo = 'A'
  26360.                 Else
  26361.                         cTipo = VarType(xExpr)
  26362.                 Endif
  26363.                
  26364.                 Do Case
  26365.                 Case cTipo=='D'
  26366.                         return '"'+dtos(xExpr)+'"'
  26367.                 Case cTipo=='N'
  26368.                         return Transform(xExpr)
  26369.                 Case cTipo=='L'
  26370.                         return iif(xExpr,'true','false')
  26371.                 Case cTipo=='X'
  26372.                         return 'null'
  26373.                 Case cTipo=='C'
  26374.                         xExpr = allt(xExpr)
  26375.                         xExpr = StrTran(xExpr, '\', '\\' )
  26376.                         xExpr = StrTran(xExpr, '/', '\/' )
  26377.                         xExpr = StrTran(xExpr, Chr(13), '\n' )
  26378.                         xExpr = StrTran(xExpr, Chr(10), '\f' )
  26379.                         xExpr = StrTran(xExpr, '"', '\"' )
  26380.                         return '"'+xExpr+'"'
  26381.  
  26382.                 case cTipo=='O'
  26383.                         local cProp, cJsonValue, cRetVal, aProp[1]
  26384.                         =AMembers(aProp,xExpr)
  26385.                         cRetVal = ''
  26386.                         for each cProp in aProp
  26387.                                 *? cProp
  26388.                                 *? cRetVal
  26389.                                 if type('xExpr.'+cProp)=='U' or cProp=='CLASS'
  26390.                                         * algunas propiedades pueden no estar definidas
  26391.                                         * como: activecontrol, parent, etc
  26392.                                         loop
  26393.                                 endif
  26394.                                 if type( 'ALen(xExpr.'+cProp+')' ) == 'N'
  26395.                                         *
  26396.                                         * es un arreglo, recorrerlo usando los [ ] y eval
  26397.                                         *
  26398.                                         Local i,nTotElem
  26399.                                         cJsonValue = ''
  26400.                                         nTotElem = Eval('ALen(xExpr.'+cProp+')')
  26401.                                         For i=1 to nTotElem
  26402.                                                 cJsonValue = cJsonValue + ' , ' +  this.encode( Eval( 'xExpr.'+cProp+'['+Str(i)+']' ))
  26403.                                         Next
  26404.                                         cJsonValue = '[' + substr(cJsonValue,4) + ']'
  26405.                                 else
  26406.                                         *
  26407.                                         * es otro tipo de dato normal C, N, L
  26408.                                         *
  26409.                                         cJsonValue = this.encode( evaluate( 'xExpr.'+cProp ) )                         
  26410.                                 endif
  26411.                                 if left(cProp,1)=='_'
  26412.                                         cProp = substr(cProp,2)
  26413.                                 endif
  26414.                                 cRetVal = cRetVal + ',' + '"' + lower(cProp) + '":' + cJsonValue
  26415.                         next
  26416.                         return '{' + substr(cRetVal,2) + '}'
  26417.  
  26418.                 case cTipo=='A'
  26419.                         local valor, cRetVal
  26420.                         cRetVal = ''   
  26421.                         for each valor in xExpr
  26422.                                 cRetVal = cRetVal + ',' +  this.encode( @valor )                       
  26423.                         next
  26424.                         return  '[' + substr(cRetVal,2) + ']'
  26425.                        
  26426.                 endcase
  26427.  
  26428.         return ''
  26429.  
  26430.  
  26431.  
  26432.  
  26433.  
  26434.         *
  26435.         * regresa un elemento representado por la cadena json que se manda
  26436.         *
  26437.        
  26438.         function decode(cJson)
  26439.         local retValue
  26440.                 cJson = StrTran(cJson,chr(9),'')
  26441.                 cJson = StrTran(cJson,chr(10),'')
  26442.                 cJson = StrTran(cJson,chr(13),'')
  26443.                 cJson = this.fixUnicode(cJson)
  26444.                 this.nPos  = 1
  26445.                 this.cJson = cJson
  26446.                 this.nLen  = len(cJson)
  26447.                 this.cError = ''
  26448.                 retValue = this.parsevalue()
  26449.                 if not empty(this.cError)
  26450.                         return null
  26451.                 endif
  26452.                 if this.getToken()<>null
  26453.                         this.setError('Junk at the end of JSON input')
  26454.                         return null
  26455.                 endif
  26456.         return retValue
  26457.                
  26458.        
  26459.         function parseValue()
  26460.         local token
  26461.                 token = this.getToken()
  26462.                 if token==null
  26463.                         this.setError('Nothing to parse')
  26464.                         return null
  26465.                 endif
  26466.                 do case
  26467.                 case token=='"'
  26468.                         return this.parseString()
  26469.                 case isdigit(token) or token=='-'
  26470.                         return this.parseNumber()
  26471.                 case token=='n'
  26472.                         return this.expectedKeyword('null',null)
  26473.                 case token=='f'
  26474.                         return this.expectedKeyword('false',.f.)
  26475.                 case token=='t'
  26476.                         return this.expectedKeyword('true',.t.)
  26477.                 case token=='{'
  26478.                         return this.parseObject()
  26479.                 case token=='['
  26480.                         return this.parseArray()
  26481.                 otherwise
  26482.                         this.setError('Unexpected token')
  26483.                 endcase
  26484.         return
  26485.                
  26486.        
  26487.         function expectedKeyword(cWord,eValue)
  26488.                 for i=1 to len(cWord)
  26489.                         cChar = this.getChar()
  26490.                         if cChar <> substr(cWord,i,1)
  26491.                                 this.setError("Expected keyword '" + cWord + "'")
  26492.                                 return ''
  26493.                         endif
  26494.                         this.nPos = this.nPos + 1
  26495.                 next
  26496.         return eValue
  26497.        
  26498.  
  26499.         function parseObject()
  26500.         local retval, cPropName, xValue
  26501.                 retval = createObject('myObj')
  26502.                 this.nPos = this.nPos + 1 && Eat {
  26503.                 if this.getToken()<>'}'
  26504.                         do while .t.
  26505.                                 cPropName = this.parseString()
  26506.                                 if not empty(this.cError)
  26507.                                         return null
  26508.                                 endif
  26509.                                 if this.getToken()<>':'
  26510.                                         this.setError("Expected ':' when parsing object")
  26511.                                         return null
  26512.                                 endif
  26513.                                 this.nPos = this.nPos + 1
  26514.                                 xValue = this.parseValue()
  26515.                                 if not empty(this.cError)
  26516.                                         return null
  26517.                                 endif                          
  26518.                                 ** Debug ? cPropName, type('xValue')
  26519.                                 retval.set(cPropName, xValue)
  26520.                                 if this.getToken()<>','
  26521.                                         exit
  26522.                                 endif
  26523.                                 this.nPos = this.nPos + 1
  26524.                         enddo
  26525.                 endif
  26526.                 if this.getToken()<>'}'
  26527.                         this.setError("Expected '}' at the end of object")
  26528.                         return null
  26529.                 endif
  26530.                 this.nPos = this.nPos + 1
  26531.         return retval
  26532.  
  26533.  
  26534.         function parseArray()
  26535.         local retVal, xValue
  26536.                 retval = createObject('MyArray')
  26537.                 this.nPos = this.nPos + 1       && Eat [
  26538.                 if this.getToken() <> ']'
  26539.                         do while .t.
  26540.                                 xValue = this.parseValue()
  26541.                                 if not empty(this.cError)
  26542.                                         return null
  26543.                                 endif
  26544.                                 retval.add( xValue )
  26545.                                 if this.getToken()<>','
  26546.                                         exit
  26547.                                 endif
  26548.                                 this.nPos = this.nPos + 1
  26549.                         enddo
  26550.                         if this.getToken() <> ']'
  26551.                                 this.setError('Expected ] at the end of array')
  26552.                                 return null
  26553.                         endif
  26554.                 endif
  26555.                 this.nPos = this.nPos + 1
  26556.         return retval
  26557.        
  26558.  
  26559.         function parseString()
  26560.         local cRetVal, c
  26561.                 if this.getToken()<>'"'
  26562.                         this.setError('Expected "')
  26563.                         return ''
  26564.                 endif
  26565.                 this.nPos = this.nPos + 1       && Eat "
  26566.                 cRetVal = ''
  26567.                 do while .t.
  26568.                         c = this.getChar()
  26569.                         if c==''
  26570.                                 return ''
  26571.                         endif
  26572.                         if c == '"'
  26573.                                 this.nPos = this.nPos + 1
  26574.                                 exit
  26575.                         endif
  26576.                         if c == '\'
  26577.                                 this.nPos = this.nPos + 1
  26578.                                 if (this.nPos>this.nLen)
  26579.                                         this.setError('\\ at the end of input')
  26580.                                         return ''
  26581.                                 endif
  26582.                                 c = this.getChar()
  26583.                                 if c==''
  26584.                                         return ''
  26585.                                 endif
  26586.                                 do case
  26587.                                 case c=='"'
  26588.                                         c='"'
  26589.                                 case c=='\'
  26590.                                         c='\'
  26591.                                 case c=='/'
  26592.                                         c='/'
  26593.                                 case c=='b'
  26594.                                         c=chr(8)
  26595.                                 case c=='t'
  26596.                                         c=chr(9)
  26597.                                 case c=='n'
  26598.                                         c=chr(10)
  26599.                                 case c=='f'
  26600.                                         c=chr(12)
  26601.                                 case c=='r'
  26602.                                         c=chr(13)
  26603.                                 otherwise
  26604.                                         ******* FALTAN LOS UNICODE
  26605.                                         this.setError('Invalid escape sequence in string literal')
  26606.                                         return ''
  26607.                                 endcase
  26608.                         endif
  26609.                         cRetVal = cRetVal + c
  26610.                         this.nPos = this.nPos + 1
  26611.                 enddo
  26612.         return cRetVal
  26613.                                        
  26614.  
  26615.         **** Pendiente numeros con E
  26616.         function parseNumber()
  26617.         local nStartPos,c, isInt, cNumero
  26618.                 if not ( isdigit(this.getToken()) or this.getToken()=='-')
  26619.                         this.setError('Expected number literal')
  26620.                         return 0
  26621.                 endif
  26622.                 nStartPos = this.nPos
  26623.                 c = this.getChar()
  26624.                 if c == '-'
  26625.                         c = this.nextChar()
  26626.                 endif
  26627.                 if c == '0'
  26628.                         c = this.nextChar()
  26629.                 else
  26630.                         if isdigit(c)
  26631.                                 c = this.nextChar()
  26632.                                 do while isdigit(c)
  26633.                                         c = this.nextChar()
  26634.                                 enddo
  26635.                         else
  26636.                                 this.setError('Expected digit when parsing number')
  26637.                                 return 0
  26638.                         endif
  26639.                 endif
  26640.                
  26641.                 isInt = .t.
  26642.                 if c=='.'
  26643.                         c = this.nextChar()
  26644.                         if isdigit(c)
  26645.                                 c = this.nextChar()
  26646.                                 isInt = .f.
  26647.                                 do while isDigit(c)
  26648.                                         c = this.nextChar()
  26649.                                 enddo
  26650.                         else
  26651.                                 this.setError('Expected digit following dot comma')
  26652.                                 return 0
  26653.                         endif
  26654.                 endif
  26655.                
  26656.                 cNumero = substr(this.cJson, nStartPos, this.nPos - nStartPos)
  26657.         return val(cNumero)
  26658.  
  26659.  
  26660.  
  26661.         function getToken()
  26662.         local char1
  26663.                 do while .t.
  26664.                         if this.nPos > this.nLen
  26665.                                 return null
  26666.                         endif
  26667.                         char1 = substr(this.cJson, this.nPos, 1)
  26668.                         if char1==' '
  26669.                                 this.nPos = this.nPos + 1
  26670.                                 loop
  26671.                         endif
  26672.                         return char1
  26673.                 enddo
  26674.         return
  26675.        
  26676.                
  26677.                
  26678.         function getChar()
  26679.                 if this.nPos > this.nLen
  26680.                         this.setError('Unexpected end of JSON stream')
  26681.                         return ''
  26682.                 endif
  26683.         return substr(this.cJson, this.nPos, 1)
  26684.        
  26685.         function nextChar()
  26686.                 this.nPos = this.nPos + 1
  26687.                 if this.nPos > this.nLen
  26688.                         return ''
  26689.                 endif
  26690.         return substr(this.cJson, this.nPos, 1)
  26691.        
  26692.         function setError(cMsg)
  26693.                 this.cError= 'ERROR parsing JSON at Position:'+allt(str(this.nPos,6,0))+' '+cMsg
  26694.         return
  26695.  
  26696.  
  26697.         function fixUnicode(cStr)
  26698.                 cStr = StrTran(cStr,'\u00e1','á')
  26699.                 cStr = StrTran(cStr,'\u00e9','é')
  26700.                 cStr = StrTran(cStr,'\u00ed','í')
  26701.                 cStr = StrTran(cStr,'\u00f3','ó')
  26702.                 cStr = StrTran(cStr,'\u00fa','ú')
  26703.                 cStr = StrTran(cStr,'\u00c1','Á')
  26704.                 cStr = StrTran(cStr,'\u00c9','É')
  26705.                 cStr = StrTran(cStr,'\u00cd','Í')
  26706.                 cStr = StrTran(cStr,'\u00d3','Ó')
  26707.                 cStr = StrTran(cStr,'\u00da','Ú')
  26708.                 cStr = StrTran(cStr,'\u00f1','ń')
  26709.                 cStr = StrTran(cStr,'\u00d1','Ń')
  26710.         return cStr
  26711.  
  26712.  
  26713.  
  26714. enddefine
  26715.  
  26716.  
  26717.  
  26718.  
  26719. *
  26720. * class used to return an array
  26721. *
  26722. define class myArray as custom
  26723.         nSize = 0
  26724.         dimension array[1]
  26725.  
  26726.         function add(xExpr)
  26727.                 this.nSize = this.nSize + 1
  26728.                 dimension this.array[this.nSize]
  26729.                 this.array[this.nSize] = xExpr
  26730.         return
  26731.  
  26732.         function get(n)
  26733.         return this.array[n]
  26734.  
  26735. enddefine
  26736.  
  26737.  
  26738.  
  26739. *
  26740. * class used to simulate an object
  26741. * all properties are prefixed with 'prop' to permit property names like: error, init
  26742. * that already exists like vfp methods
  26743. *
  26744. define class myObj as custom
  26745. Hidden ;
  26746.         ClassLibrary,Comment, ;
  26747.         BaseClass,ControlCount, ;
  26748.         Controls,Objects,Object,;
  26749.         Height,HelpContextID,Left,Name, ;
  26750.         Parent,ParentClass,Picture, ;
  26751.         Tag,Top,WhatsThisHelpID,Width
  26752.                
  26753.         function set(cPropName, xValue)
  26754.                 cPropName = '_'+cPropName
  26755.                 if type('this.'+cPropName)=='U'
  26756.                         this.addProperty(cPropName,xValue)
  26757.                 else
  26758.                         local cmd
  26759.                         cmd = 'this.'+cPropName+'=xValue'
  26760.                         &cmd
  26761.                 endif
  26762.         return
  26763.        
  26764.         procedure get (cPropName)
  26765.                 cPropName = '_'+cPropName
  26766.                 If type('this.'+cPropName)=='U'
  26767.                         return ''
  26768.                 Else
  26769.                         local cmd
  26770.                         cmd = 'return this.'+cPropName
  26771.                         &cmd
  26772.                 endif
  26773.         return ''
  26774. enddefine
  26775.  
  26776.  
  26777.  
  26778.  
  26779.  
  26780. function testJsonClass
  26781.         clear
  26782.         set decimal to 10
  26783.         oJson = newObject('json')
  26784.        
  26785.        
  26786.         ? 'Test Basic Types'
  26787.         ? '----------------'
  26788.         ? oJson.decode('null')
  26789.         ? oJson.decode('true')
  26790.         ? oJson.decode('false')
  26791.         ?
  26792.         ? oJson.decode('791123')
  26793.         ? oJson.decode('791123.45')
  26794.         ? oJson.decode('791123.45.')
  26795.         ? oJson.decode('"nacho gtz"')
  26796.         if not empty(oJson.cError)
  26797.                 ? oJson.cError
  26798.                 return
  26799.         endif
  26800.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  26801.         if not empty(oJson.cError)
  26802.                 ? oJson.cError
  26803.                 return
  26804.         endif
  26805.        
  26806.         ? 'Test Array'
  26807.         ? '----------'
  26808.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  26809.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  26810.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  26811.         nombres = arr.get(1)
  26812.         edades  = arr.get(2)
  26813.         ? nombres.get(1), edades.get(1)
  26814.         ? nombres.get(2), edades.get(2)
  26815.         ? nombres.get(3), edades.get(3)
  26816.         ?
  26817.         ? 'Test Object'
  26818.         ? '-----------'
  26819.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  26820.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  26821.         ? obj._Nombre, obj._Edad, obj._IsGood
  26822.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  26823.         ? obj.get('jsonrpc'), obj._jsonrpc
  26824.         ? obj.get('id'), obj._id
  26825.         ? obj.get('method'), obj._method
  26826.         ? obj._Params.array[1], obj._Params.get(1)
  26827.  
  26828.         ?
  26829.         ? 'Test nested object'
  26830.         ? '------------------'
  26831.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  26832.         obj = oJson.decode(cJson)
  26833.         if not empty(oJson.cError)
  26834.                 ? oJson.cError
  26835.                 return
  26836.         endif
  26837.         ? cJson
  26838.         ? 'method -->',obj._method
  26839.         ? 'usrkey -->',obj._params._data._usrkey
  26840.         ? 'sendto -->',obj._params._data._sendto
  26841.         ? 'name  --->',obj._params._data._name
  26842.         ? 'expires ->',obj._params._data._expires
  26843.  
  26844.         ?
  26845.         ? 'Test empty object'
  26846.         ? '-----------------'
  26847.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  26848.         obj = oJson.decode(cJson)
  26849.         if not empty(oJson.cError)     
  26850.                 ? oJson.cError
  26851.                 return
  26852.         endif
  26853.         ? cJson
  26854.         ? 'result -->',obj._result, obj.get('result')
  26855.         oError = obj.get('error')
  26856.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  26857.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  26858.         ? 'id  ----->',obj._id, obj.get('id')
  26859.         ?  type("oError._code")
  26860.  
  26861.         ?
  26862.         ? 'Probar decode-enconde-decode-encode'
  26863.         ? '------------------------------------'
  26864.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  26865.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  26866.         ? cJson
  26867.         oSmtp = json_decode(cJson)
  26868.         cJson =  json_encode(oSmtp)
  26869.         ? cJson
  26870.         oSmtp = json_decode(cJson)
  26871.         cJson =  json_encode(oSmtp)
  26872.         ? cJson
  26873.  
  26874.         * Probar falla
  26875.         ?
  26876.         ? 'Probar una falla en el json'
  26877.         ? '---------------------------'
  26878.         cJson = ' {"server":"", "user":"", "password":"" ,'
  26879.         oSmtp = json_decode(cJson)
  26880.         if not empty(json_getErrorMsg())
  26881.                 ? json_getErrorMsg()
  26882.         endif
  26883.  
  26884.         ?
  26885.         ? 'Pruebas Finalizadas'
  26886. retur
  26887. *
  26888. * class used to return an array
  26889. *
  26890. define class myArray as custom
  26891.         nSize = 0
  26892.         dimension array[1]
  26893.  
  26894.         function add(xExpr)
  26895.                 this.nSize = this.nSize + 1
  26896.                 dimension this.array[this.nSize]
  26897.                 this.array[this.nSize] = xExpr
  26898.         return
  26899.  
  26900.         function get(n)
  26901.         return this.array[n]
  26902.  
  26903. enddefine
  26904.  
  26905.  
  26906.  
  26907. *
  26908. * class used to simulate an object
  26909. * all properties are prefixed with 'prop' to permit property names like: error, init
  26910. * that already exists like vfp methods
  26911. *
  26912. define class myObj as custom
  26913. Hidden ;
  26914.         ClassLibrary,Comment, ;
  26915.         BaseClass,ControlCount, ;
  26916.         Controls,Objects,Object,;
  26917.         Height,HelpContextID,Left,Name, ;
  26918.         Parent,ParentClass,Picture, ;
  26919.         Tag,Top,WhatsThisHelpID,Width
  26920.                
  26921.         function set(cPropName, xValue)
  26922.                 cPropName = '_'+cPropName
  26923.                 if type('this.'+cPropName)=='U'
  26924.                         this.addProperty(cPropName,xValue)
  26925.                 else
  26926.                         local cmd
  26927.                         cmd = 'this.'+cPropName+'=xValue'
  26928.                         &cmd
  26929.                 endif
  26930.         return
  26931.        
  26932.         procedure get (cPropName)
  26933.                 cPropName = '_'+cPropName
  26934.                 If type('this.'+cPropName)=='U'
  26935.                         return ''
  26936.                 Else
  26937.                         local cmd
  26938.                         cmd = 'return this.'+cPropName
  26939.                         &cmd
  26940.                 endif
  26941.         return ''
  26942. enddefine
  26943.  
  26944.  
  26945.  
  26946.  
  26947.  
  26948. function testJsonClass
  26949.         clear
  26950.         set decimal to 10
  26951.         oJson = newObject('json')
  26952.        
  26953.        
  26954.         ? 'Test Basic Types'
  26955.         ? '----------------'
  26956.         ? oJson.decode('null')
  26957.         ? oJson.decode('true')
  26958.         ? oJson.decode('false')
  26959.         ?
  26960.         ? oJson.decode('791123')
  26961.         ? oJson.decode('791123.45')
  26962.         ? oJson.decode('791123.45.')
  26963.         ? oJson.decode('"nacho gtz"')
  26964.         if not empty(oJson.cError)
  26965.                 ? oJson.cError
  26966.                 return
  26967.         endif
  26968.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  26969.         if not empty(oJson.cError)
  26970.                 ? oJson.cError
  26971.                 return
  26972.         endif
  26973.        
  26974.         ? 'Test Array'
  26975.         ? '----------'
  26976.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  26977.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  26978.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  26979.         nombres = arr.get(1)
  26980.         edades  = arr.get(2)
  26981.         ? nombres.get(1), edades.get(1)
  26982.         ? nombres.get(2), edades.get(2)
  26983.         ? nombres.get(3), edades.get(3)
  26984.         ?
  26985.         ? 'Test Object'
  26986.         ? '-----------'
  26987.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  26988.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  26989.         ? obj._Nombre, obj._Edad, obj._IsGood
  26990.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  26991.         ? obj.get('jsonrpc'), obj._jsonrpc
  26992.         ? obj.get('id'), obj._id
  26993.         ? obj.get('method'), obj._method
  26994.         ? obj._Params.array[1], obj._Params.get(1)
  26995.  
  26996.         ?
  26997.         ? 'Test nested object'
  26998.         ? '------------------'
  26999.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  27000.         obj = oJson.decode(cJson)
  27001.         if not empty(oJson.cError)
  27002.                 ? oJson.cError
  27003.                 return
  27004.         endif
  27005.         ? cJson
  27006.         ? 'method -->',obj._method
  27007.         ? 'usrkey -->',obj._params._data._usrkey
  27008.         ? 'sendto -->',obj._params._data._sendto
  27009.         ? 'name  --->',obj._params._data._name
  27010.         ? 'expires ->',obj._params._data._expires
  27011.  
  27012.         ?
  27013.         ? 'Test empty object'
  27014.         ? '-----------------'
  27015.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  27016.         obj = oJson.decode(cJson)
  27017.         if not empty(oJson.cError)     
  27018.                 ? oJson.cError
  27019.                 return
  27020.         endif
  27021.         ? cJson
  27022.         ? 'result -->',obj._result, obj.get('result')
  27023.         oError = obj.get('error')
  27024.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  27025.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  27026.         ? 'id  ----->',obj._id, obj.get('id')
  27027.         ?  type("oError._code")
  27028.  
  27029.         ?
  27030.         ? 'Probar decode-enconde-decode-encode'
  27031.         ? '------------------------------------'
  27032.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  27033.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  27034.         ? cJson
  27035.         oSmtp = json_decode(cJson)
  27036.         cJson =  json_encode(oSmtp)
  27037.         ? cJson
  27038.         oSmtp = json_decode(cJson)
  27039.         cJson =  json_encode(oSmtp)
  27040.         ? cJson
  27041.  
  27042.         * Probar falla
  27043.         ?
  27044.         ? 'Probar una falla en el json'
  27045.         ? '---------------------------'
  27046.         cJson = ' {"server":"", "user":"", "password":"" ,'
  27047.         oSmtp = json_decode(cJson)
  27048.         if not empty(json_getErrorMsg())
  27049.                 ? json_getErrorMsg()
  27050.         endif
  27051.  
  27052.         ?
  27053.         ? 'Pruebas Finalizadas'
  27054. retur
  27055. * class used to return an array
  27056. *
  27057. define class myArray as custom
  27058.         nSize = 0
  27059.         dimension array[1]
  27060.  
  27061.         function add(xExpr)
  27062.                 this.nSize = this.nSize + 1
  27063.                 dimension this.array[this.nSize]
  27064.                 this.array[this.nSize] = xExpr
  27065.         return
  27066.  
  27067.         function get(n)
  27068.         return this.array[n]
  27069.  
  27070. enddefine
  27071.  
  27072.  
  27073.  
  27074. *
  27075. * class used to simulate an object
  27076. * all properties are prefixed with 'prop' to permit property names like: error, init
  27077. * that already exists like vfp methods
  27078. *
  27079. define class myObj as custom
  27080. Hidden ;
  27081.         ClassLibrary,Comment, ;
  27082.         BaseClass,ControlCount, ;
  27083.         Controls,Objects,Object,;
  27084.         Height,HelpContextID,Left,Name, ;
  27085.         Parent,ParentClass,Picture, ;
  27086.         Tag,Top,WhatsThisHelpID,Width
  27087.                
  27088.         function set(cPropName, xValue)
  27089.                 cPropName = '_'+cPropName
  27090.                 if type('this.'+cPropName)=='U'
  27091.                         this.addProperty(cPropName,xValue)
  27092.                 else
  27093.                         local cmd
  27094.                         cmd = 'this.'+cPropName+'=xValue'
  27095.                         &cmd
  27096.                 endif
  27097.         return
  27098.        
  27099.         procedure get (cPropName)
  27100.                 cPropName = '_'+cPropName
  27101.                 If type('this.'+cPropName)=='U'
  27102.                         return ''
  27103.                 Else
  27104.                         local cmd
  27105.                         cmd = 'return this.'+cPropName
  27106.                         &cmd
  27107.                 endif
  27108.         return ''
  27109. enddefine
  27110.  
  27111.  
  27112.  
  27113.  
  27114.  
  27115. function testJsonClass
  27116.         clear
  27117.         set decimal to 10
  27118.         oJson = newObject('json')
  27119.        
  27120.        
  27121.         ? 'Test Basic Types'
  27122.         ? '----------------'
  27123.         ? oJson.decode('null')
  27124.         ? oJson.decode('true')
  27125.         ? oJson.decode('false')
  27126.         ?
  27127.         ? oJson.decode('791123')
  27128.         ? oJson.decode('791123.45')
  27129.         ? oJson.decode('791123.45.')
  27130.         ? oJson.decode('"nacho gtz"')
  27131.         if not empty(oJson.cError)
  27132.                 ? oJson.cError
  27133.                 return
  27134.         endif
  27135.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  27136.         if not empty(oJson.cError)
  27137.                 ? oJson.cError
  27138.                 return
  27139.         endif
  27140.        
  27141.         ? 'Test Array'
  27142.         ? '----------'
  27143.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  27144.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  27145.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  27146.         nombres = arr.get(1)
  27147.         edades  = arr.get(2)
  27148.         ? nombres.get(1), edades.get(1)
  27149.         ? nombres.get(2), edades.get(2)
  27150.         ? nombres.get(3), edades.get(3)
  27151.         ?
  27152.         ? 'Test Object'
  27153.         ? '-----------'
  27154.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  27155.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  27156.         ? obj._Nombre, obj._Edad, obj._IsGood
  27157.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  27158.         ? obj.get('jsonrpc'), obj._jsonrpc
  27159.         ? obj.get('id'), obj._id
  27160.         ? obj.get('method'), obj._method
  27161.         ? obj._Params.array[1], obj._Params.get(1)
  27162.  
  27163.         ?
  27164.         ? 'Test nested object'
  27165.         ? '------------------'
  27166.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  27167.         obj = oJson.decode(cJson)
  27168.         if not empty(oJson.cError)
  27169.                 ? oJson.cError
  27170.                 return
  27171.         endif
  27172.         ? cJson
  27173.         ? 'method -->',obj._method
  27174.         ? 'usrkey -->',obj._params._data._usrkey
  27175.         ? 'sendto -->',obj._params._data._sendto
  27176.         ? 'name  --->',obj._params._data._name
  27177.         ? 'expires ->',obj._params._data._expires
  27178.  
  27179.         ?
  27180.         ? 'Test empty object'
  27181.         ? '-----------------'
  27182.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  27183.         obj = oJson.decode(cJson)
  27184.         if not empty(oJson.cError)     
  27185.                 ? oJson.cError
  27186.                 return
  27187.         endif
  27188.         ? cJson
  27189.         ? 'result -->',obj._result, obj.get('result')
  27190.         oError = obj.get('error')
  27191.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  27192.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  27193.         ? 'id  ----->',obj._id, obj.get('id')
  27194.         ?  type("oError._code")
  27195.  
  27196.         ?
  27197.         ? 'Probar decode-enconde-decode-encode'
  27198.         ? '------------------------------------'
  27199.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  27200.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  27201.         ? cJson
  27202.         oSmtp = json_decode(cJson)
  27203.         cJson =  json_encode(oSmtp)
  27204.         ? cJson
  27205.         oSmtp = json_decode(cJson)
  27206.         cJson =  json_encode(oSmtp)
  27207.         ? cJson
  27208.  
  27209.         * Probar falla
  27210.         ?
  27211.         ? 'Probar una falla en el json'
  27212.         ? '---------------------------'
  27213.         cJson = ' {"server":"", "user":"", "password":"" ,'
  27214.         oSmtp = json_decode(cJson)
  27215.         if not empty(json_getErrorMsg())
  27216.                 ? json_getErrorMsg()
  27217.         endif
  27218.  
  27219.         ?
  27220.         ? 'Pruebas Finalizadas'
  27221. retur
  27222. *
  27223. define class myArray as custom
  27224.         nSize = 0
  27225.         dimension array[1]
  27226.  
  27227.         function add(xExpr)
  27228.                 this.nSize = this.nSize + 1
  27229.                 dimension this.array[this.nSize]
  27230.                 this.array[this.nSize] = xExpr
  27231.         return
  27232.  
  27233.         function get(n)
  27234.         return this.array[n]
  27235.  
  27236. enddefine
  27237.  
  27238.  
  27239. *
  27240. * class used to simulate an object
  27241. * all properties are prefixed with 'prop' to permit property names like: error, init
  27242. * that already exists like vfp methods
  27243. *
  27244. define class myObj as custom
  27245. Hidden ;
  27246.         ClassLibrary,Comment, ;
  27247.         BaseClass,ControlCount, ;
  27248.         Controls,Objects,Object,;
  27249.         Height,HelpContextID,Left,Name, ;
  27250.         Parent,ParentClass,Picture, ;
  27251.         Tag,Top,WhatsThisHelpID,Width
  27252.                
  27253.         function set(cPropName, xValue)
  27254.                 cPropName = '_'+cPropName
  27255.                 if type('this.'+cPropName)=='U'
  27256.                         this.addProperty(cPropName,xValue)
  27257.                 else
  27258.                         local cmd
  27259.                         cmd = 'this.'+cPropName+'=xValue'
  27260.                         &cmd
  27261.                 endif
  27262.         return
  27263.        
  27264.         procedure get (cPropName)
  27265.                 cPropName = '_'+cPropName
  27266.                 If type('this.'+cPropName)=='U'
  27267.                         return ''
  27268.                 Else
  27269.                         local cmd
  27270.                         cmd = 'return this.'+cPropName
  27271.                         &cmd
  27272.                 endif
  27273.         return ''
  27274. enddefine
  27275.  
  27276.  
  27277.  
  27278.  
  27279.  
  27280. function testJsonClass
  27281.         clear
  27282.         set decimal to 10
  27283.         oJson = newObject('json')
  27284.        
  27285.        
  27286.         ? 'Test Basic Types'
  27287.         ? '----------------'
  27288.         ? oJson.decode('null')
  27289.         ? oJson.decode('true')
  27290.         ? oJson.decode('false')
  27291.         ?
  27292.         ? oJson.decode('791123')
  27293.         ? oJson.decode('791123.45')
  27294.         ? oJson.decode('791123.45.')
  27295.         ? oJson.decode('"nacho gtz"')
  27296.         if not empty(oJson.cError)
  27297.                 ? oJson.cError
  27298.                 return
  27299.         endif
  27300.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  27301.         if not empty(oJson.cError)
  27302.                 ? oJson.cError
  27303.                 return
  27304.         endif
  27305.        
  27306.         ? 'Test Array'
  27307.         ? '----------'
  27308.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  27309.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  27310.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  27311.         nombres = arr.get(1)
  27312.         edades  = arr.get(2)
  27313.         ? nombres.get(1), edades.get(1)
  27314.         ? nombres.get(2), edades.get(2)
  27315.         ? nombres.get(3), edades.get(3)
  27316.         ?
  27317.         ? 'Test Object'
  27318.         ? '-----------'
  27319.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  27320.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  27321.         ? obj._Nombre, obj._Edad, obj._IsGood
  27322.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  27323.         ? obj.get('jsonrpc'), obj._jsonrpc
  27324.         ? obj.get('id'), obj._id
  27325.         ? obj.get('method'), obj._method
  27326.         ? obj._Params.array[1], obj._Params.get(1)
  27327.  
  27328.         ?
  27329.         ? 'Test nested object'
  27330.         ? '------------------'
  27331.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  27332.         obj = oJson.decode(cJson)
  27333.         if not empty(oJson.cError)
  27334.                 ? oJson.cError
  27335.                 return
  27336.         endif
  27337.         ? cJson
  27338.         ? 'method -->',obj._method
  27339.         ? 'usrkey -->',obj._params._data._usrkey
  27340.         ? 'sendto -->',obj._params._data._sendto
  27341.         ? 'name  --->',obj._params._data._name
  27342.         ? 'expires ->',obj._params._data._expires
  27343.  
  27344.         ?
  27345.         ? 'Test empty object'
  27346.         ? '-----------------'
  27347.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  27348.         obj = oJson.decode(cJson)
  27349.         if not empty(oJson.cError)     
  27350.                 ? oJson.cError
  27351.                 return
  27352.         endif
  27353.         ? cJson
  27354.         ? 'result -->',obj._result, obj.get('result')
  27355.         oError = obj.get('error')
  27356.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  27357.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  27358.         ? 'id  ----->',obj._id, obj.get('id')
  27359.         ?  type("oError._code")
  27360.  
  27361.         ?
  27362.         ? 'Probar decode-enconde-decode-encode'
  27363.         ? '------------------------------------'
  27364.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  27365.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  27366.         ? cJson
  27367.         oSmtp = json_decode(cJson)
  27368.         cJson =  json_encode(oSmtp)
  27369.         ? cJson
  27370.         oSmtp = json_decode(cJson)
  27371.         cJson =  json_encode(oSmtp)
  27372.         ? cJson
  27373.  
  27374.         * Probar falla
  27375.         ?
  27376.         ? 'Probar una falla en el json'
  27377.         ? '---------------------------'
  27378.         cJson = ' {"server":"", "user":"", "password":"" ,'
  27379.         oSmtp = json_decode(cJson)
  27380.         if not empty(json_getErrorMsg())
  27381.                 ? json_getErrorMsg()
  27382.         endif
  27383.  
  27384.         ?
  27385.         ? 'Pruebas Finalizadas'
  27386. retur
  27387. *
  27388. * class used to simulate an object
  27389. * all properties are prefixed with 'prop' to permit property names like: error, init
  27390. * that already exists like vfp methods
  27391. *
  27392. define class myObj as custom
  27393. Hidden ;
  27394.         ClassLibrary,Comment, ;
  27395.         BaseClass,ControlCount, ;
  27396.         Controls,Objects,Object,;
  27397.         Height,HelpContextID,Left,Name, ;
  27398.         Parent,ParentClass,Picture, ;
  27399.         Tag,Top,WhatsThisHelpID,Width
  27400.                
  27401.         function set(cPropName, xValue)
  27402.                 cPropName = '_'+cPropName
  27403.                 if type('this.'+cPropName)=='U'
  27404.                         this.addProperty(cPropName,xValue)
  27405.                 else
  27406.                         local cmd
  27407.                         cmd = 'this.'+cPropName+'=xValue'
  27408.                         &cmd
  27409.                 endif
  27410.         return
  27411.        
  27412.         procedure get (cPropName)
  27413.                 cPropName = '_'+cPropName
  27414.                 If type('this.'+cPropName)=='U'
  27415.                         return ''
  27416.                 Else
  27417.                         local cmd
  27418.                         cmd = 'return this.'+cPropName
  27419.                         &cmd
  27420.                 endif
  27421.         return ''
  27422. enddefine
  27423.  
  27424.  
  27425.  
  27426.  
  27427.  
  27428. function testJsonClass
  27429.         clear
  27430.         set decimal to 10
  27431.         oJson = newObject('json')
  27432.        
  27433.        
  27434.         ? 'Test Basic Types'
  27435.         ? '----------------'
  27436.         ? oJson.decode('null')
  27437.         ? oJson.decode('true')
  27438.         ? oJson.decode('false')
  27439.         ?
  27440.         ? oJson.decode('791123')
  27441.         ? oJson.decode('791123.45')
  27442.         ? oJson.decode('791123.45.')
  27443.         ? oJson.decode('"nacho gtz"')
  27444.         if not empty(oJson.cError)
  27445.                 ? oJson.cError
  27446.                 return
  27447.         endif
  27448.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  27449.         if not empty(oJson.cError)
  27450.                 ? oJson.cError
  27451.                 return
  27452.         endif
  27453.        
  27454.         ? 'Test Array'
  27455.         ? '----------'
  27456.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  27457.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  27458.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  27459.         nombres = arr.get(1)
  27460.         edades  = arr.get(2)
  27461.         ? nombres.get(1), edades.get(1)
  27462.         ? nombres.get(2), edades.get(2)
  27463.         ? nombres.get(3), edades.get(3)
  27464.         ?
  27465.         ? 'Test Object'
  27466.         ? '-----------'
  27467.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  27468.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  27469.         ? obj._Nombre, obj._Edad, obj._IsGood
  27470.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  27471.         ? obj.get('jsonrpc'), obj._jsonrpc
  27472.         ? obj.get('id'), obj._id
  27473.         ? obj.get('method'), obj._method
  27474.         ? obj._Params.array[1], obj._Params.get(1)
  27475.  
  27476.         ?
  27477.         ? 'Test nested object'
  27478.         ? '------------------'
  27479.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  27480.         obj = oJson.decode(cJson)
  27481.         if not empty(oJson.cError)
  27482.                 ? oJson.cError
  27483.                 return
  27484.         endif
  27485.         ? cJson
  27486.         ? 'method -->',obj._method
  27487.         ? 'usrkey -->',obj._params._data._usrkey
  27488.         ? 'sendto -->',obj._params._data._sendto
  27489.         ? 'name  --->',obj._params._data._name
  27490.         ? 'expires ->',obj._params._data._expires
  27491.  
  27492.         ?
  27493.         ? 'Test empty object'
  27494.         ? '-----------------'
  27495.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  27496.         obj = oJson.decode(cJson)
  27497.         if not empty(oJson.cError)     
  27498.                 ? oJson.cError
  27499.                 return
  27500.         endif
  27501.         ? cJson
  27502.         ? 'result -->',obj._result, obj.get('result')
  27503.         oError = obj.get('error')
  27504.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  27505.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  27506.         ? 'id  ----->',obj._id, obj.get('id')
  27507.         ?  type("oError._code")
  27508.  
  27509.         ?
  27510.         ? 'Probar decode-enconde-decode-encode'
  27511.         ? '------------------------------------'
  27512.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  27513.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  27514.         ? cJson
  27515.         oSmtp = json_decode(cJson)
  27516.         cJson =  json_encode(oSmtp)
  27517.         ? cJson
  27518.         oSmtp = json_decode(cJson)
  27519.         cJson =  json_encode(oSmtp)
  27520.         ? cJson
  27521.  
  27522.         * Probar falla
  27523.         ?
  27524.         ? 'Probar una falla en el json'
  27525.         ? '---------------------------'
  27526.         cJson = ' {"server":"", "user":"", "password":"" ,'
  27527.         oSmtp = json_decode(cJson)
  27528.         if not empty(json_getErrorMsg())
  27529.                 ? json_getErrorMsg()
  27530.         endif
  27531.  
  27532.         ?
  27533.         ? 'Pruebas Finalizadas'
  27534. retur
  27535. * class used to simulate an object
  27536. * all properties are prefixed with 'prop' to permit property names like: error, init
  27537. * that already exists like vfp methods
  27538. *
  27539. define class myObj as custom
  27540. Hidden ;
  27541.         ClassLibrary,Comment, ;
  27542.         BaseClass,ControlCount, ;
  27543.         Controls,Objects,Object,;
  27544.         Height,HelpContextID,Left,Name, ;
  27545.         Parent,ParentClass,Picture, ;
  27546.         Tag,Top,WhatsThisHelpID,Width
  27547.                
  27548.         function set(cPropName, xValue)
  27549.                 cPropName = '_'+cPropName
  27550.                 if type('this.'+cPropName)=='U'
  27551.                         this.addProperty(cPropName,xValue)
  27552.                 else
  27553.                         local cmd
  27554.                         cmd = 'this.'+cPropName+'=xValue'
  27555.                         &cmd
  27556.                 endif
  27557.         return
  27558.        
  27559.         procedure get (cPropName)
  27560.                 cPropName = '_'+cPropName
  27561.                 If type('this.'+cPropName)=='U'
  27562.                         return ''
  27563.                 Else
  27564.                         local cmd
  27565.                         cmd = 'return this.'+cPropName
  27566.                         &cmd
  27567.                 endif
  27568.         return ''
  27569. enddefine
  27570.  
  27571.  
  27572.  
  27573.  
  27574.  
  27575. function testJsonClass
  27576.         clear
  27577.         set decimal to 10
  27578.         oJson = newObject('json')
  27579.        
  27580.        
  27581.         ? 'Test Basic Types'
  27582.         ? '----------------'
  27583.         ? oJson.decode('null')
  27584.         ? oJson.decode('true')
  27585.         ? oJson.decode('false')
  27586.         ?
  27587.         ? oJson.decode('791123')
  27588.         ? oJson.decode('791123.45')
  27589.         ? oJson.decode('791123.45.')
  27590.         ? oJson.decode('"nacho gtz"')
  27591.         if not empty(oJson.cError)
  27592.                 ? oJson.cError
  27593.                 return
  27594.         endif
  27595.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  27596.         if not empty(oJson.cError)
  27597.                 ? oJson.cError
  27598.                 return
  27599.         endif
  27600.        
  27601.         ? 'Test Array'
  27602.         ? '----------'
  27603.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  27604.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  27605.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  27606.         nombres = arr.get(1)
  27607.         edades  = arr.get(2)
  27608.         ? nombres.get(1), edades.get(1)
  27609.         ? nombres.get(2), edades.get(2)
  27610.         ? nombres.get(3), edades.get(3)
  27611.         ?
  27612.         ? 'Test Object'
  27613.         ? '-----------'
  27614.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  27615.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  27616.         ? obj._Nombre, obj._Edad, obj._IsGood
  27617.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  27618.         ? obj.get('jsonrpc'), obj._jsonrpc
  27619.         ? obj.get('id'), obj._id
  27620.         ? obj.get('method'), obj._method
  27621.         ? obj._Params.array[1], obj._Params.get(1)
  27622.  
  27623.         ?
  27624.         ? 'Test nested object'
  27625.         ? '------------------'
  27626.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  27627.         obj = oJson.decode(cJson)
  27628.         if not empty(oJson.cError)
  27629.                 ? oJson.cError
  27630.                 return
  27631.         endif
  27632.         ? cJson
  27633.         ? 'method -->',obj._method
  27634.         ? 'usrkey -->',obj._params._data._usrkey
  27635.         ? 'sendto -->',obj._params._data._sendto
  27636.         ? 'name  --->',obj._params._data._name
  27637.         ? 'expires ->',obj._params._data._expires
  27638.  
  27639.         ?
  27640.         ? 'Test empty object'
  27641.         ? '-----------------'
  27642.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  27643.         obj = oJson.decode(cJson)
  27644.         if not empty(oJson.cError)     
  27645.                 ? oJson.cError
  27646.                 return
  27647.         endif
  27648.         ? cJson
  27649.         ? 'result -->',obj._result, obj.get('result')
  27650.         oError = obj.get('error')
  27651.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  27652.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  27653.         ? 'id  ----->',obj._id, obj.get('id')
  27654.         ?  type("oError._code")
  27655.  
  27656.         ?
  27657.         ? 'Probar decode-enconde-decode-encode'
  27658.         ? '------------------------------------'
  27659.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  27660.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  27661.         ? cJson
  27662.         oSmtp = json_decode(cJson)
  27663.         cJson =  json_encode(oSmtp)
  27664.         ? cJson
  27665.         oSmtp = json_decode(cJson)
  27666.         cJson =  json_encode(oSmtp)
  27667.         ? cJson
  27668.  
  27669.         * Probar falla
  27670.         ?
  27671.         ? 'Probar una falla en el json'
  27672.         ? '---------------------------'
  27673.         cJson = ' {"server":"", "user":"", "password":"" ,'
  27674.         oSmtp = json_decode(cJson)
  27675.         if not empty(json_getErrorMsg())
  27676.                 ? json_getErrorMsg()
  27677.         endif
  27678.  
  27679.         ?
  27680.         ? 'Pruebas Finalizadas'
  27681. retur
  27682. * all properties are prefixed with 'prop' to permit property names like: error, init
  27683. * that already exists like vfp methods
  27684. *
  27685. define class myObj as custom
  27686. Hidden ;
  27687.         ClassLibrary,Comment, ;
  27688.         BaseClass,ControlCount, ;
  27689.         Controls,Objects,Object,;
  27690.         Height,HelpContextID,Left,Name, ;
  27691.         Parent,ParentClass,Picture, ;
  27692.         Tag,Top,WhatsThisHelpID,Width
  27693.                
  27694.         function set(cPropName, xValue)
  27695.                 cPropName = '_'+cPropName
  27696.                 if type('this.'+cPropName)=='U'
  27697.                         this.addProperty(cPropName,xValue)
  27698.                 else
  27699.                         local cmd
  27700.                         cmd = 'this.'+cPropName+'=xValue'
  27701.                         &cmd
  27702.                 endif
  27703.         return
  27704.        
  27705.         procedure get (cPropName)
  27706.                 cPropName = '_'+cPropName
  27707.                 If type('this.'+cPropName)=='U'
  27708.                         return ''
  27709.                 Else
  27710.                         local cmd
  27711.                         cmd = 'return this.'+cPropName
  27712.                         &cmd
  27713.                 endif
  27714.         return ''
  27715. enddefine
  27716.  
  27717.  
  27718.  
  27719.  
  27720.  
  27721. function testJsonClass
  27722.         clear
  27723.         set decimal to 10
  27724.         oJson = newObject('json')
  27725.        
  27726.        
  27727.         ? 'Test Basic Types'
  27728.         ? '----------------'
  27729.         ? oJson.decode('null')
  27730.         ? oJson.decode('true')
  27731.         ? oJson.decode('false')
  27732.         ?
  27733.         ? oJson.decode('791123')
  27734.         ? oJson.decode('791123.45')
  27735.         ? oJson.decode('791123.45.')
  27736.         ? oJson.decode('"nacho gtz"')
  27737.         if not empty(oJson.cError)
  27738.                 ? oJson.cError
  27739.                 return
  27740.         endif
  27741.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  27742.         if not empty(oJson.cError)
  27743.                 ? oJson.cError
  27744.                 return
  27745.         endif
  27746.        
  27747.         ? 'Test Array'
  27748.         ? '----------'
  27749.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  27750.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  27751.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  27752.         nombres = arr.get(1)
  27753.         edades  = arr.get(2)
  27754.         ? nombres.get(1), edades.get(1)
  27755.         ? nombres.get(2), edades.get(2)
  27756.         ? nombres.get(3), edades.get(3)
  27757.         ?
  27758.         ? 'Test Object'
  27759.         ? '-----------'
  27760.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  27761.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  27762.         ? obj._Nombre, obj._Edad, obj._IsGood
  27763.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  27764.         ? obj.get('jsonrpc'), obj._jsonrpc
  27765.         ? obj.get('id'), obj._id
  27766.         ? obj.get('method'), obj._method
  27767.         ? obj._Params.array[1], obj._Params.get(1)
  27768.  
  27769.         ?
  27770.         ? 'Test nested object'
  27771.         ? '------------------'
  27772.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  27773.         obj = oJson.decode(cJson)
  27774.         if not empty(oJson.cError)
  27775.                 ? oJson.cError
  27776.                 return
  27777.         endif
  27778.         ? cJson
  27779.         ? 'method -->',obj._method
  27780.         ? 'usrkey -->',obj._params._data._usrkey
  27781.         ? 'sendto -->',obj._params._data._sendto
  27782.         ? 'name  --->',obj._params._data._name
  27783.         ? 'expires ->',obj._params._data._expires
  27784.  
  27785.         ?
  27786.         ? 'Test empty object'
  27787.         ? '-----------------'
  27788.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  27789.         obj = oJson.decode(cJson)
  27790.         if not empty(oJson.cError)     
  27791.                 ? oJson.cError
  27792.                 return
  27793.         endif
  27794.         ? cJson
  27795.         ? 'result -->',obj._result, obj.get('result')
  27796.         oError = obj.get('error')
  27797.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  27798.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  27799.         ? 'id  ----->',obj._id, obj.get('id')
  27800.         ?  type("oError._code")
  27801.  
  27802.         ?
  27803.         ? 'Probar decode-enconde-decode-encode'
  27804.         ? '------------------------------------'
  27805.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  27806.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  27807.         ? cJson
  27808.         oSmtp = json_decode(cJson)
  27809.         cJson =  json_encode(oSmtp)
  27810.         ? cJson
  27811.         oSmtp = json_decode(cJson)
  27812.         cJson =  json_encode(oSmtp)
  27813.         ? cJson
  27814.  
  27815.         * Probar falla
  27816.         ?
  27817.         ? 'Probar una falla en el json'
  27818.         ? '---------------------------'
  27819.         cJson = ' {"server":"", "user":"", "password":"" ,'
  27820.         oSmtp = json_decode(cJson)
  27821.         if not empty(json_getErrorMsg())
  27822.                 ? json_getErrorMsg()
  27823.         endif
  27824.  
  27825.         ?
  27826.         ? 'Pruebas Finalizadas'
  27827. retur
  27828. * that already exists like vfp methods
  27829. *
  27830. define class myObj as custom
  27831. Hidden ;
  27832.         ClassLibrary,Comment, ;
  27833.         BaseClass,ControlCount, ;
  27834.         Controls,Objects,Object,;
  27835.         Height,HelpContextID,Left,Name, ;
  27836.         Parent,ParentClass,Picture, ;
  27837.         Tag,Top,WhatsThisHelpID,Width
  27838.                
  27839.         function set(cPropName, xValue)
  27840.                 cPropName = '_'+cPropName
  27841.                 if type('this.'+cPropName)=='U'
  27842.                         this.addProperty(cPropName,xValue)
  27843.                 else
  27844.                         local cmd
  27845.                         cmd = 'this.'+cPropName+'=xValue'
  27846.                         &cmd
  27847.                 endif
  27848.         return
  27849.        
  27850.         procedure get (cPropName)
  27851.                 cPropName = '_'+cPropName
  27852.                 If type('this.'+cPropName)=='U'
  27853.                         return ''
  27854.                 Else
  27855.                         local cmd
  27856.                         cmd = 'return this.'+cPropName
  27857.                         &cmd
  27858.                 endif
  27859.         return ''
  27860. enddefine
  27861.  
  27862.  
  27863.  
  27864.  
  27865.  
  27866. function testJsonClass
  27867.         clear
  27868.         set decimal to 10
  27869.         oJson = newObject('json')
  27870.        
  27871.        
  27872.         ? 'Test Basic Types'
  27873.         ? '----------------'
  27874.         ? oJson.decode('null')
  27875.         ? oJson.decode('true')
  27876.         ? oJson.decode('false')
  27877.         ?
  27878.         ? oJson.decode('791123')
  27879.         ? oJson.decode('791123.45')
  27880.         ? oJson.decode('791123.45.')
  27881.         ? oJson.decode('"nacho gtz"')
  27882.         if not empty(oJson.cError)
  27883.                 ? oJson.cError
  27884.                 return
  27885.         endif
  27886.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  27887.         if not empty(oJson.cError)
  27888.                 ? oJson.cError
  27889.                 return
  27890.         endif
  27891.        
  27892.         ? 'Test Array'
  27893.         ? '----------'
  27894.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  27895.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  27896.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  27897.         nombres = arr.get(1)
  27898.         edades  = arr.get(2)
  27899.         ? nombres.get(1), edades.get(1)
  27900.         ? nombres.get(2), edades.get(2)
  27901.         ? nombres.get(3), edades.get(3)
  27902.         ?
  27903.         ? 'Test Object'
  27904.         ? '-----------'
  27905.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  27906.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  27907.         ? obj._Nombre, obj._Edad, obj._IsGood
  27908.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  27909.         ? obj.get('jsonrpc'), obj._jsonrpc
  27910.         ? obj.get('id'), obj._id
  27911.         ? obj.get('method'), obj._method
  27912.         ? obj._Params.array[1], obj._Params.get(1)
  27913.  
  27914.         ?
  27915.         ? 'Test nested object'
  27916.         ? '------------------'
  27917.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  27918.         obj = oJson.decode(cJson)
  27919.         if not empty(oJson.cError)
  27920.                 ? oJson.cError
  27921.                 return
  27922.         endif
  27923.         ? cJson
  27924.         ? 'method -->',obj._method
  27925.         ? 'usrkey -->',obj._params._data._usrkey
  27926.         ? 'sendto -->',obj._params._data._sendto
  27927.         ? 'name  --->',obj._params._data._name
  27928.         ? 'expires ->',obj._params._data._expires
  27929.  
  27930.         ?
  27931.         ? 'Test empty object'
  27932.         ? '-----------------'
  27933.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  27934.         obj = oJson.decode(cJson)
  27935.         if not empty(oJson.cError)     
  27936.                 ? oJson.cError
  27937.                 return
  27938.         endif
  27939.         ? cJson
  27940.         ? 'result -->',obj._result, obj.get('result')
  27941.         oError = obj.get('error')
  27942.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  27943.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  27944.         ? 'id  ----->',obj._id, obj.get('id')
  27945.         ?  type("oError._code")
  27946.  
  27947.         ?
  27948.         ? 'Probar decode-enconde-decode-encode'
  27949.         ? '------------------------------------'
  27950.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  27951.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  27952.         ? cJson
  27953.         oSmtp = json_decode(cJson)
  27954.         cJson =  json_encode(oSmtp)
  27955.         ? cJson
  27956.         oSmtp = json_decode(cJson)
  27957.         cJson =  json_encode(oSmtp)
  27958.         ? cJson
  27959.  
  27960.         * Probar falla
  27961.         ?
  27962.         ? 'Probar una falla en el json'
  27963.         ? '---------------------------'
  27964.         cJson = ' {"server":"", "user":"", "password":"" ,'
  27965.         oSmtp = json_decode(cJson)
  27966.         if not empty(json_getErrorMsg())
  27967.                 ? json_getErrorMsg()
  27968.         endif
  27969.  
  27970.         ?
  27971.         ? 'Pruebas Finalizadas'
  27972. retur
  27973. *
  27974. define class myObj as custom
  27975. Hidden ;
  27976.         ClassLibrary,Comment, ;
  27977.         BaseClass,ControlCount, ;
  27978.         Controls,Objects,Object,;
  27979.         Height,HelpContextID,Left,Name, ;
  27980.         Parent,ParentClass,Picture, ;
  27981.         Tag,Top,WhatsThisHelpID,Width
  27982.                
  27983.         function set(cPropName, xValue)
  27984.                 cPropName = '_'+cPropName
  27985.                 if type('this.'+cPropName)=='U'
  27986.                         this.addProperty(cPropName,xValue)
  27987.                 else
  27988.                         local cmd
  27989.                         cmd = 'this.'+cPropName+'=xValue'
  27990.                         &cmd
  27991.                 endif
  27992.         return
  27993.        
  27994.         procedure get (cPropName)
  27995.                 cPropName = '_'+cPropName
  27996.                 If type('this.'+cPropName)=='U'
  27997.                         return ''
  27998.                 Else
  27999.                         local cmd
  28000.                         cmd = 'return this.'+cPropName
  28001.                         &cmd
  28002.                 endif
  28003.         return ''
  28004. enddefine
  28005.  
  28006.  
  28007.  
  28008.  
  28009.  
  28010. function testJsonClass
  28011.         clear
  28012.         set decimal to 10
  28013.         oJson = newObject('json')
  28014.        
  28015.        
  28016.         ? 'Test Basic Types'
  28017.         ? '----------------'
  28018.         ? oJson.decode('null')
  28019.         ? oJson.decode('true')
  28020.         ? oJson.decode('false')
  28021.         ?
  28022.         ? oJson.decode('791123')
  28023.         ? oJson.decode('791123.45')
  28024.         ? oJson.decode('791123.45.')
  28025.         ? oJson.decode('"nacho gtz"')
  28026.         if not empty(oJson.cError)
  28027.                 ? oJson.cError
  28028.                 return
  28029.         endif
  28030.         ? oJson.decode('"nacho gtz\nEs \"bueno\"\nMuy Bueno\ba"')
  28031.         if not empty(oJson.cError)
  28032.                 ? oJson.cError
  28033.                 return
  28034.         endif
  28035.        
  28036.         ? 'Test Array'
  28037.         ? '----------'
  28038.         arr = oJson.decode('[3.1416,"Ignacio",false,null]')
  28039.         ? arr.get(1), arr.get(2), arr.get(3), arr.get(4)
  28040.         arr = oJson.decode('[ ["Hugo","Paco","Luis"] , [ 8,9,11] ] ')
  28041.         nombres = arr.get(1)
  28042.         edades  = arr.get(2)
  28043.         ? nombres.get(1), edades.get(1)
  28044.         ? nombres.get(2), edades.get(2)
  28045.         ? nombres.get(3), edades.get(3)
  28046.         ?
  28047.         ? 'Test Object'
  28048.         ? '-----------'
  28049.         obj = oJson.decode('{"nombre":"Ignacio", "edad":33.17, "isGood":true}')
  28050.         ? obj.get('nombre'), obj.get('edad'), obj.get('isGood')
  28051.         ? obj._Nombre, obj._Edad, obj._IsGood
  28052.         obj = oJson.decode('{"jsonrpc":"1.0", "id":1, "method":"sumArray", "params":[3.1415,2.14,10],"version":1.0}')
  28053.         ? obj.get('jsonrpc'), obj._jsonrpc
  28054.         ? obj.get('id'), obj._id
  28055.         ? obj.get('method'), obj._method
  28056.         ? obj._Params.array[1], obj._Params.get(1)
  28057.  
  28058.         ?
  28059.         ? 'Test nested object'
  28060.         ? '------------------'
  28061.         cJson = '{"jsonrpc":"1.0", "id":1, "method":"upload", "params": {"data":{ "usrkey":"288af77b", "sendto":"[email protected]", "name":"Ignacio is \"Nacho\"","expires":"20120731" }}}'
  28062.         obj = oJson.decode(cJson)
  28063.         if not empty(oJson.cError)
  28064.                 ? oJson.cError
  28065.                 return
  28066.         endif
  28067.         ? cJson
  28068.         ? 'method -->',obj._method
  28069.         ? 'usrkey -->',obj._params._data._usrkey
  28070.         ? 'sendto -->',obj._params._data._sendto
  28071.         ? 'name  --->',obj._params._data._name
  28072.         ? 'expires ->',obj._params._data._expires
  28073.  
  28074.         ?
  28075.         ? 'Test empty object'
  28076.         ? '-----------------'
  28077.         cJson = '{"result":null,"error":{"code":-3200.012,"message":"invalid usrkey","data":{}},"id":"1"}'
  28078.         obj = oJson.decode(cJson)
  28079.         if not empty(oJson.cError)     
  28080.                 ? oJson.cError
  28081.                 return
  28082.         endif
  28083.         ? cJson
  28084.         ? 'result -->',obj._result, obj.get('result')
  28085.         oError = obj.get('error')
  28086.         ? 'ErrCode ->',obj._error._code, oError.get('code')
  28087.         ? 'ErrMsg -->',obj._error._message, oError.get('message')
  28088.         ? 'id  ----->',obj._id, obj.get('id')
  28089.         ?  type("oError._code")
  28090.  
  28091.         ?
  28092.         ? 'Probar decode-enconde-decode-encode'
  28093.         ? '------------------------------------'
  28094.         cJson = ' {"server":"", "user":"", "password":"" ,'+;
  28095.                         ' "port":0, "auth":false, "ssl":false, "timeout":20, "error":404}'
  28096.         ? cJson
  28097.         oSmtp = json_decode(cJson)
  28098.         cJson =  json_encode(oSmtp)
  28099.         ? cJson
  28100.         oSmtp = json_decode(cJson)
  28101.         cJson =  json_encode(oSmtp)
  28102.         ? cJson
  28103.  
  28104.         * Probar falla
  28105.         ?
  28106.         ? 'Probar una falla en el json'
  28107.         ? '---------------------------'
  28108.         cJson = ' {"server":"", "user":"", "password":"" ,'
  28109.         oSmtp = json_decode(cJson)
  28110.         if not empty(json_getErrorMsg())
  28111.                 ? json_getErrorMsg()
  28112.         endif
  28113.  
  28114.         ?
  28115.         ? 'Pruebas Finalizadas'
  28116. return
  28117.