注册 登录
编程论坛 VFP论坛

跟我一起学微信公众平台开发(二)

sam_jiang 发布于 2024-12-21 23:05, 778 次点击
不好意思,因为不是全职程序员,没有及时更新,这篇虽是第二篇,但是跟微信公众号没有关系,只能算是番外篇。。。
这篇主要是讲如何用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编辑过]

13 回复
#2
schtg2024-12-22 06:10
谢谢分享!
#3
sych2024-12-22 09:16
非常棒!谢谢分享
#4
小白到底白不2024-12-22 09:16
看看,学习一下,谢谢分享
#5
sam_jiang2024-12-22 10:06
刚发现json类的getvalue有个bug,碰巧取最后一个属性的值时会出错,所以给出解决方案,大伙自行更改一下。。。

代码如下
......
    ELSE &&否则就是普通数值
        cscript=this.jsscript+","
        j=0
        DO WHILE .t.
            j=j+1
            nright=AT(",",cscript,j)
            IF nright<nleft
                LOOP
            ELSE
                cvalue=SUBSTR(cscript,nleft,nright-nleft)
......

[此贴子已经被作者于2024-12-22 10:07编辑过]

#6
sam_jiang2024-12-22 14:48
我把类的源代码打包发上来吧,之前有网友私信问我要源代码。

解压后把2个文件名分别改成myclass.vcx和myclass.vct就可以使用了。

@schtg ,@jsddx
只有本站会员才能查看附件,请 登录
#7
wcx_cc2024-12-22 20:53
有楼主这样的高才和觉悟,值得大大称赞!收藏啦!!
#8
sam_jiang2024-12-22 21:25
回复7楼
谢谢,你们的表扬是我不断奉献的动力

[此贴子已经被作者于2024-12-22 21:32编辑过]

#9
schtg2024-12-23 05:32
回复 6楼 sam_jiang
谢谢!
#10
easyppt2024-12-23 09:10
向老师致敬!
#11
sych2024-12-23 09:38
cscript=ojson.getvalue("button")改成下面,结果是["123"],为什么加了[和"?
cscript=ojson.getvalue("test")
只有本站会员才能查看附件,请 登录
#12
sam_jiang2024-12-23 13:38
回复 11楼 sych
你下载我发的附件,用附件里的类。解决了昨天发现的几个bug,重新再试一下!我刚试了是没有问题的。

我返回的全是文本,去掉了所有的数据类型了,这点使用时要注意!

只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2024-12-23 13:40编辑过]

#13
sych2024-12-23 15:41
用的就是你发的文件直接改名成myclass
#14
sam_jiang2024-12-23 19:49
各位在使用过程中如果发现bug麻烦告诉我,我改一下。我自己测试,目前没有问题。

ojson=NEWOBJECT([json],[myclass])
ojson.Add("https://bbs.bccn.net/thread-514069-2-1.html","网址")
ojson.Add("Sam Jiang","用户名")
ojson.Add(".t.","性别")
?ojson.generate()
&&显示:{"网址":"https://bbs.bccn.net/thread-514069-2-1.html","用户名":"Sam Jiang","性别":".t."}

ojsa=NEWOBJECT([jsonarray],[myclass])
ojsa.add("123")
ojsa.add("abc")
ojsa.add(".f.")
?ojsa.generate()
&& 显示:["123","abc",".f."]
ojson.Add(ojsa,"array")
?ojson.generate()

&&显示:{"网址":"https://bbs.bccn.net/thread-514069-2-1.html","用户名":"Sam Jiang","性别":".t.","array":["123","abc",".f."]}
1