![]() |
#2
schtg2024-12-22 06:10
|
这篇主要是讲如何用foxpro写json解析程序,纯vfp代码,没有dll,fll库~
由于对于你的请求微信公众号返回的是json格式的,所以了解json格式是必须的。
主要思路:
1,把json结构看做一个collection,ckey就是json的属性;cvalue就是json的属性值,json的属性不可重复。
2,构建一个数组类jsonarray,对应于json的数组,数组的元素可以是任意值,也可以是json本身。
3,json类有3个重要的方法,
a),json.parse 解析json
b),json.generate 生成json脚本
c),json.getvalue 根据给定的属性名返回属性值,如果值是数组,则返回整个数组的脚本。
4,使用add方法,添加属性及属性值
5,使用remove方法,删除属性及属性值
类代码如下:

**************************************************
*-- 类: json (d:\documents\visual foxpro 项目\myclass.vcx)
*-- 父类: collection
*-- 基类: collection
*-- 时间戳: 12/21/24 09:19:08 PM
*
DEFINE CLASS json AS collection
Height = 23
Width = 23
*-- 生成的json脚本
jsscript = ""
Name = "json"
*-- 返回json结构中指定属性的值。
PROCEDURE getvalue
PARAMETERS ckey,noccurrence
cvalue=""
IF PARAMETERS()=1
noccurrence=1
ENDIF
n=AT(ckey,this.jsscript,noccurrence)
IF n>0
*!* 确定指定json属性的值的起始位置
nleft=n+LEN(ckey)
DO WHILE .t.
nleft=nleft+1
cchar=SUBSTR(this.jsscript,nleft,1)
IF cchar=":"
nleft=nleft+1 &&从":"后面一个字符开始
EXIT
ELSE
LOOP
ENDIF
ENDDO
*!*
DO WHILE .t.
cchar=ALLTRIM(SUBSTR(this.jsscript,nleft,1))
IF cchar=" " &&空格,冒号后面可能不小心输入了空格,剔除它
nleft=nleft+1
LOOP
ELSE
EXIT
ENDIF
ENDDO
IF cchar="[" &&说明是数组
*!*确定与"["匹配的"]"的位置
i=0
DO WHILE .t.
i=i+1
nright=AT("]",this.jsscript,i)
IF nright<nleft
LOOP
ELSE
cvalue=SUBSTR(this.jsscript,nleft,nright-nleft+1)
IF OCCURS("[",cvalue)=OCCURS("]",cvalue)
EXIT
ELSE
LOOP
ENDIF
ENDIF
ENDDO
ELSE &&否则就是普通数值
j=0
DO WHILE .t.
j=j+1
nright=AT(",",this.jsscript,j)
IF nright<nleft
LOOP
ELSE
cvalue=SUBSTR(this.jsscript,nleft,nright-nleft)
cvalue=STRTRAN(cvalue,["],"")
cvalue=STRTRAN(cvalue,[}],"")
cvalue=ALLTRIM(cvalue)
EXIT
ENDIF
ENDDO
ENDIF
ENDIF
RETURN cvalue
ENDPROC
*-- 解析json结构。
PROCEDURE parse
LPARAMETERS jsonscript
LOCAL ckey,cstr,i,j,n,ccontent,ckey,cvalue,atemp,nleft,nright,jsarray,ncount,m
jsonscript=STRTRAN(jsonscript,CHR(9),"") &&清除tab键
jsonscript=STRTRAN(jsonscript,CHR(10),"") &&清除ctrl+enter
jsonscript=STRTRAN(jsonscript,CHR(13),"") &&清除换行符
jsonscript=allt(jsonscript) &&清除首尾空格
*!* ************最基本的json结构检查************
IF OCCURS("{",jsonscript)<>OCCURS("}",jsonscript) OR OCCURS("[",jsonscript)<>+OCCURS("]",jsonscript) OR MOD(OCCURS(["],jsonscript),2)<>0
MESSAGEBOX('{,[" 未成对!')
RETURN .f.
ENDIF
this.jsscript=jsonscript
*****
*!* 先检查是不是包含json数组的复合结构
*!* 提取所有json数组,并以AJSON+编号予以替换,保存于jsonarraylist中
n=0
DO WHILE .t.
IF OCCURS("[",jsonscript)>0 &&说明json结构里有数组
DIMENSION jsonarraylist[n+1] &&用以保存json数组的内容
nleft =AT("[",jsonscript) &&确定第一个 "[" 的位置
i=0
*!* 下面这段代码确定与第一个 "[" 匹配的 "]" 的位置
DO WHILE .t.
i=i+1
nright=AT("]",jsonscript,i)
ccontent=SUBSTR(jsonscript,nleft,nright-nleft+1)
IF OCCURS("[",ccontent)<>OCCURS("]",ccontent)
LOOP
ELSE
EXIT
ENDIF
ENDDO
*!*
*!* ccontent=SUBSTR(jsonscript,nleft,nright-nleft+1) &&提取json数组的整体内容
n=n+1
jsarray="AJSON_"+TRANSFORM(n) &&准备替换掉数组的内容
jsonscript=STRTRAN(jsonscript,ccontent,jsarray)
jsonarraylist(n)=ccontent
LOOP
ELSE
EXIT
ENDIF
ENDDO
*!*
*!* 对替换所有数组后的json结构进行解析
DIMENSION atemp[1]
ncount=ALINES(atemp,jsonscript,9,",")
FOR i=1 TO ncount
ckey =ALLTRIM(STREXTRACT(STREXTRACT(atemp[i],"",":"),["],["]))
cvalue=STRtr(STREXTRACT(atemp[i],":",""),["],[])
cvalue=ALLTRIM(STRTRAN(cvalue,"}",""))
this.Add(cvalue,ckey)
ENDFOR
*!*
*!* *!* 如果是复合结构,则复原json数组内容
*!* IF VARTYPE(jsonarraylist)!="U"
*!* FOR i=1 TO ALEN(jsonarraylist)
*!* cvalue="AJSON_"+TRANSFORM(i)
*!* FOR j=1 TO this.count
*!* IF TRANSFORM(this.Item(j))=cvalue
*!* ckey=this.GetKey(j)
*!* this.Remove(ckey)
*!* this.add(jsonarraylist[i],ckey)
*!* ENDIF
*!* ENDFOR
*!* ENDFOR
*!* ENDIF
*!* *!*
*!* 如果是复合结构,则复原json数组内容
IF VARTYPE(jsonarraylist)!="U"
FOR i=1 TO ALEN(jsonarraylist)
cvalue="AJSON_"+TRANSFORM(i)
m=this.count
FOR j=1 TO m
IF TRANSFORM(this.Item(j))=cvalue
ckey=this.GetKey(j)
this.Remove(ckey)
&ckey=NEWOBJECT([jsonarray],[myclass])
&ckey..name=ckey
&ckey..parse(jsonarraylist[i])
IF j=m
this.add(&ckey,ckey)
ELSE
this.Add(&ckey,ckey,j)
ENDIF
ENDIF
ENDFOR
ENDFOR
ENDIF
*!*
ENDPROC
*-- 生成json脚本
PROCEDURE generate
LOCAL cscript,ckey,cvalue,oref,i
cscript=""
IF this.Count>0
FOR i=1 TO this.Count
ckey=this.GetKey(i)
IF VARTYPE(this.Item(i))="O"
oref=this.Item(i)
cvalue=oref.generate()
ELSE
cvalue=this.Item(i)
ENDIF
cscript=cscript+["]+ckey+[":]+IIF(LEFT(TRANSFORM(cvalue),1)="[",TRANSFORM(cvalue),["]+TRANSFORM(cvalue)+["])+","
ENDFOR
cscript=[{]+SUBSTR(cscript,1,LEN(cscript)-1)+[}]
ENDIF
this.jsscript=cscript
RETURN this.jsscript
ENDPROC
PROCEDURE Init
ENDPROC
ENDDEFINE
*
*-- EndDefine: json
**************************************************
6,由于vfp不支持数组嵌套,所以就设计了一个数组类jsonarray,以便做到嵌套数组。
7,提供2两个重要的方法,
a),jsonarray.parse 解析json数组
b),jsonarray.generate 生成json数组脚本
另外提供了一组操作数组的函数,add,remove,copy,item,clear,set
类代码如下:

**************************************************
*-- 类: jsonarray (d:\documents\visual foxpro 项目\myclass.vcx)
*-- 父类: custom
*-- 基类: custom
*-- 时间戳: 12/21/24 10:50:10 PM
*
DEFINE CLASS jsonarray AS custom
*-- Jsonarray数组成员数量。
count = 0
*-- json数组脚本
script = ""
Name = "jsonarray"
*-- Jsonarray数组
DIMENSION array[1]
*-- 添加数组成员。
PROCEDURE add
LPARAMETERS eExpr
this.count= this.count + 1
dimension this.array[this.count]
this.array[this.count] = eExpr
return
ENDPROC
*-- 访问数组成员。
PROCEDURE item
LPARAMETERS n
return this.array[n]
ENDPROC
*-- 移除数组成员。
PROCEDURE remove
LPARAMETERS eExpr
IF TYPE(eExpr)="N"
ADEL(this.array,eExpr)
DIMENSION this.array(this.count-1)
this.count=this.count-1
ELSE
n=ASCAN(this.array,eExpr)
IF n!=0
ADEL(this.array,n)
DIMENSION this.array(this.count-1)
this.count=this.count-1
ENDIF
ENDIF
ENDPROC
*-- 清空数组。
PROCEDURE clear
DIMENSION this.array[1]
this.array[1]=.f.
this.count=0
ENDPROC
*-- 生成json数组文本。
PROCEDURE generate
LOCAL oref,cvalue,i,cexpression
cexpression=""
IF this.count<>0
FOR i=1 TO this.count
IF VARTYPE(this.item(i))="O"
oref=this.item(i)
cvalue=oref.generate()
ELSE
cvalue=this.item(i)
ENDIF
IF [{] $ cvalue
cexpression=cexpression+cvalue+[,]
ELSE
cexpression=cexpression+["]+cvalue+["]+[,]
ENDIF
ENDFOR
cexpression="["+LEFT(cexpression,LEN(cexpression)-1)+"]"
ENDIF
this.script=cexpression
RETURN this.script
ENDPROC
*-- 从已知数组复制。
PROCEDURE copy
PARAMETERS carrayname
*调用该方法时数组变量参数前需加强制引用符号@
n=ALEN(carrayname)
DIMENSION this.array(n)
FOR i=1 TO n
this.array(i)=carrayname(i)
ENDFOR
this.count=n
ENDPROC
*-- 设置数组项的值。
PROCEDURE set
PARAMETERS nindex,eExpr
this.array[nindex]=eExpr
ENDPROC
*-- 解读json数组结构。
PROCEDURE parse
PARAMETERS cjsonarray
LOCAL jsonobjectlist,nleft,right,i,ctemp,n,ii,ncount,nindex,oref
*****
***** 先检查是不是包含json结构的复合数组
*****
this.script=cjsonarray
DIMENSION jsonobjectlist[1]
n=0
DO WHILE .t.
IF OCCURS("{",cjsonarray)>0 &&说明数组里有嵌套json结构
DIMENSION jsonobjectlist[2*n+2] &&用以保存可能的json结构
nleft=AT("{",cjsonarray)
i=0
DO WHILE .t.
i=i+1
nright=AT("}",cjsonarray,i)
ctemp=SUBSTR(cjsonarray,nleft,nright-nleft+1)
IF OCCURS("{",ctemp)<>OCCURS("}",ctemp)
LOOP
ELSE
EXIT
ENDIF
ENDDO
*!* 将json数组中的json结构用OJSON_开头的字符串替换,并保存在数组中
cjsonarray=STRTRAN(cjsonarray,ctemp,"OJSON_"+TRANSFORM(n+1))
*!* oref="OJSON_"+TRANSFORM(n+1)
*!* &oref=NEWOBJECT([json],[myclass])
jsonobjectlist[2*n+1]="OJSON_"+TRANSFORM(n+1)
*!* jsonobjectlist[2*n+1]=&oref
jsonobjectlist[2*n+2]=ctemp
*!* &oref..parse(ctemp)
n=n+1
*!*
LOOP
ELSE
EXIT &&没有json结构则退出循环。
ENDIF
ENDDO
ncount=ALINES(this.array,cjsonarray,9,[,])
FOR ii=1 TO ncount
this.array[ii]=STRTRAN(this.array(ii),CHR(13),[])
this.array[ii]=STRTRAN(this.array(ii),CHR(9),[])
this.array[ii]=STRTRAN(this.array(ii),CHR(10),[])
this.array[ii]=STRTRAN(this.array(ii),["],[])
this.array[ii]=STRTRAN(this.array(ii),"[",[])
this.array[ii]=STRTRAN(this.array(ii),"]",[])
this.array[ii]=ALLTRIM(this.array[ii])
ENDFOR
*!* *!* 复原json数组里的json结构
*!* IF ALEN(jsonobjectlist)>1
*!* FOR j=1 TO ALEN(jsonobjectlist) STEP 2
*!* nindex=ASCAN(this.array,jsonobjectlist[j])
*!* this.array[nindex]=jsonobjectlist[j+1]
*!* ENDFOR
*!* ENDIF
*!* *!*
*!* 复原json数组里的json结构
IF ALEN(jsonobjectlist)>1
FOR j=1 TO ALEN(jsonobjectlist) STEP 2
nindex=ASCAN(this.array,jsonobjectlist[j])
oref=this.array[nindex]
&oref=NEWOBJECT([json],[myclass])
this.array[nindex]=&oref
&oref..name=oref
&oref..parse(jsonobjectlist[j+1])
*!* this.array[nindex]=jsonobjectlist[j+1]
ENDFOR
ENDIF
*!*
this.count=ncount
ENDPROC
ENDDEFINE
*
*-- EndDefine: jsonarray
**************************************************
8,附上测试代码,你们可能需要适当修改,我这两个类是保存在myclass里面的。

*jsontest.prg*
PUBLIC cjscript,ojson
CLEAR
cjscript=''
SET TEXTMERGE TO memvar cjscript noshow
\ {
\ "test":123,
\ "button":[
\ {
\ "type":"click",
\ "name":"今日歌曲",
\ "key":"V1001_TODAY_MUSIC",
\ "array":[111,"aaa","bbb"]
\ },
\ {
\ "name":"菜单",
\ "sub_button":[
\ {
\ "type":"view",
\ "name":"搜索",
\ "url":"http://www.\ },
\ {
\ "type":"miniprogram",
\ "name":"wxa",
\ "url":"http://mp.weixin.,
\ "appid":"wx286b93c14bbf93aa",
\ "pagepath":"pages/lunar/index"
\ },
\ {
\ "type":"click",
\ "name":"赞一下我们",
\ "key":"V1001_GOOD"
\ }],
\ "test":[111,222,333]
\ }],
\ "additive":"123456"
\ }
SET TEXTMERGE TO
ojson=NEWOBJECT("json","myclass")
*!* ojson.jsscript=cjscript
*!* ?cjscript
*!* *?EMPTY(ojson.getvalue("hello",4))
ojson.jsscript=cjscript
ojson.parse(cjscript)
cscript=ojson.getvalue("button")
ojsa=NEWOBJECT([jsonarray],[myclass])
ojsa.parse(cscript)
?ojsa.generate()
*!* FOR i=1 TO ojson.count
*!* ?ojson.getkey(i)
*!* ? "___"
*!* IF VARTYPE(ojson(i))="O"
*!* ?ojson(i).script
*!* else
*!* ?ojson(i)
*!* ENDIF
*!* ENDFOR
*!* ?ojson.getvalue("sub_button",2)
运行环境:win7+vfp9.0(7423),本机编译通过。
[此贴子已经被作者于2024-12-21 23:15编辑过]