*------------------------------------------------------------------- * Created by Marco Plaza @vfp2Nofox * ver 1.100 - 24/02/2016 * enabled collection processing * ver 1.101 - 24/02/2016 * solved indentation on nested collections * ver 1.110 -11/03/2016 * -added support for collections inside arrays * -user can pass aMemembersFlag value * ( since Json is intended for DTO creation default value is 'U' ) * check amembers topic on vfp help file for usage * changed cr to crlf * Added Json validation ; throws error for invalid Json. * ver 1.120 * encode control characters ( chr(0) ~ chr(31) ) *----------------------------------------------------------------------- Parameters ovfp,FormattedOutput,nonullarrayitem,crootName,aMembersFlag #Define crlf Chr(13)+Chr(10) Private All aMembersFlag = Evl(m.aMembersFlag,'U') esarray = Type('oVfp',1) = 'A' esobjeto = Vartype(m.ovfp) = 'O' If !m.esarray And !m.esobjeto Error 'must supply a vfp object/array' Endif _nivel = Iif( Cast(m.formattedOutput As l ) , 1, -1) Do Case Case esarray ojson = Createobject('empty') AddProperty(ojson,'array(1)') Acopy(ovfp,ojson.Array) cjson = procobject(ojson,.F.,m.nonullarrayitem,m.aMembersFlag) cjson = Substr( m.cjson,At('[',m.cjson)) Case Type('oVfp.BaseClass')='C' And ovfp.BaseClass = 'Collection' cjson = procobject(ovfp,.T.,m.nonullarrayitem,m.aMembersFlag) crootName = Evl(m.crootName,'collection') cjson = '{"'+m.crootName+collTagName(ovfp)+'": '+cjson+'}'+Iif(FormattedOutput,crlf,'')+'}' Otherwise cjson = '{'+procobject(ovfp,.F.,m.nonullarrayitem,m.aMembersFlag)+'}' Endcase Return Ltrim(cjson) *---------------------------------------- Function collTagName(thiscoll) *---------------------------------------- Return Iif( m.thiscoll.Count > 0 And !Empty( m.thiscoll.GetKey(1) ), '_kv_collection','_kl_collection' ) *---------------------------------------------------------------------------------- Function procobject(obt,iscollection,nonullarrayitem,aMembersFlag) *---------------------------------------------------------------------------------- If Isnull(obt) Return 'null' Endif Private All Except _nivel este = '' xtabs = nivel(2) bc = Iif(Type('m.obt.class')='C',m.obt.Class,'?') iscollection = bc = 'Collection' If m.iscollection este = este+'{ '+xtabs xtabs = nivel(2) este = este+'"collectionitems": ['+xtabs procCollection(obt,m.nonullarrayitem,m.aMembersFlag) xtabs = nivel(-2) este = este+xtabs+']' Else Amembers(am,m.obt,0,m.aMembersFlag) If Vartype(m.am) = 'U' xtabs=m.nivel(-2) Return '' Endif nm = Alen(am) For x1 = 1 To m.nm Var = Lower(am(m.x1)) este = m.este+Iif(m.x1>1,',','')+m.xtabs este = m.este+["]+Strtran(m.var,'_vfpsafe_','')+[":] esobjeto = Type('m.obt.&Var')='O' If Type('m.obt.&var') = 'U' este = m.este+["unable to evaluate expression"] Loop Endif esarray = Type('m.obt.&Var',1) = 'A' Do Case Case m.esarray procarray(obt,m.var,m.nonullarrayitem) Case m.esobjeto thiso=m.obt.&Var bc = Iif(Type('m.thiso.class')='C',m.thiso.Class,'?') If bc = 'Collection' este = Rtrim(m.este,1,'":')+ collTagName( m.thiso )+'":' este = m.este+procobject(m.obt.&Var,.T.,m.nonullarrayitem,m.aMembersFlag)+[}] Else este = m.este+[{]+procobject(m.obt.&Var,.F.,m.nonullarrayitem,m.aMembersFlag)+[}] Endif Otherwise este = este+concatval(m.obt.&Var) Endcase Endfor Endif xtabs = nivel(-2) este = este+m.xtabs Return m.este *---------------------------------------------------- Procedure procarray(obt,arrayName,nonullarrayitem) *---------------------------------------------------- nrows = Alen(m.obt.&arrayName,1) ncols = Alen(m.obt.&arrayName,2) bidim = m.ncols > 0 ncols = Iif(m.ncols=0,m.nrows,m.ncols) titems = Alen(m.obt.&arrayName) xtabs=nivel(2) este = m.este+'['+m.xtabs nelem = 1 Do While nelem <= m.titems este = este+Iif(m.nelem>1,','+m.xtabs,'') If m.bidim xtabs = nivel(2) este = m.este+'['+m.xtabs Endif For pn = m.nelem To m.nelem+m.ncols-1 elem = m.obt.&arrayName( m.pn ) este = m.este+Iif(m.pn>m.nelem,','+m.xtabs,'') If Vartype(m.elem) # 'O' If m.nelem+m.ncols-1 = 1 And Isnull(m.elem) And m.nonullarrayitem este = m.este+"" Else este = m.este+concatval(m.elem) Endif Else bc = Iif(Type('m.elem.class')='C',m.elem.Class,'?') If bc = 'Collection' este = m.este+' { "collection'+ collTagName( m.elem )+'":' este = m.este+procobject(m.elem ,.T.,m.nonullarrayitem,m.aMembersFlag) este = este + '}'+m.xtabs+'}' Else este = m.este+[{]+procobject(m.elem ,.F.,m.nonullarrayitem,m.aMembersFlag)+[}] Endif Endif Endfor nelem = m.pn If m.bidim xtabs=nivel(-2) este = m.este+m.xtabs+']' Endif Enddo xtabs=nivel(-2) este = m.este+m.xtabs+']' *----------------------------- Function nivel(N) *----------------------------- If m._nivel = -1 Return '' Else _nivel= m._nivel+m.n Return crlf+Replicate(' ',m._nivel) Endif *----------------------------- Function concatval(valor) *----------------------------- #Define specialChars ["\/]+Chr(127)+Chr(12)+Chr(10)+Chr(13)+Chr(9)+Chr(0)+Chr(1)+Chr(2)+Chr(3)+Chr(4)+Chr(5)+Chr(6)+Chr(7)+Chr(8)+Chr(9)+Chr(10)+Chr(11)+Chr(12)+Chr(13)+Chr(14)+Chr(15)+Chr(16)+Chr(17)+Chr(18)+Chr(19)+Chr(20)+Chr(21)+Chr(22)+Chr(23)+Chr(24)+Chr(25)+Chr(26)+Chr(27)+Chr(28)+Chr(29)+Chr(30)+Chr(31) If Isnull(m.valor) Return 'null' Else tvar = Vartype(m.valor) ** no cambiar el orden de ejecución! Do Case Case m.tvar $ 'FBYINQ' vc = Rtrim(Cast( m.valor As c(32))) Case m.tvar = 'L' vc = Iif(m.valor,'true','false') Case m.tvar $ 'DT' vc = ["]+Ttoc(m.valor,3)+["] Case mustEncode(m.valor) vc = ["]+escapeandencode(m.valor)+["] Case m.tvar $ 'CVM' vc = ["]+Rtrim(m.valor)+["] Case m.tvar $ 'GQW' vc = ["]+Strconv(m.valor,13)+["] Endcase Return m.vc Endif *----------------------------------- Function mustEncode(valor) *----------------------------------- Return Len(Chrtran(m.valor,specialChars,'')) <> Len(m.valor) *------------------------------- Function escapeandencode(valun) *------------------------------- valun = Strtran(m.valun,'\','\\') valun = Strtran(m.valun,'"','\"') *valun = Strtran(m.valun,'/','\/') If !mustEncode(m.valun) Return Endif valun = Strtran(m.valun,Chr(127),'\b') valun = Strtran(m.valun,Chr(12),'\f') valun = Strtran(m.valun,Chr(10),'\n') valun = Strtran(m.valun,Chr(13),'\r') valun = Strtran(m.valun,Chr(9),'\t') If !mustEncode(m.valun) Return Endif Local x For x = 0 To 31 valun = Strtran(m.valun,Chr(m.x),'\u'+Right(Transform(m.x,'@0'),4)) Endfor Return Rtrim(m.valun) *--------------------------------------------------------------- Function procCollection(obt,nonullArrayItems,aMembersFlag ) *--------------------------------------------------------------- Local iscollection With obt nm = .Count conllave = .Count > 0 And !Empty(.GetKey(1)) For x1 = 1 To .Count If conllave elem = Createobject('empty') AddProperty(elem,'Key', .GetKey(x1) ) AddProperty(elem,'Value',.Item(x1)) Else elem = .Item(x1) Endif este = este+Iif(x1>1,','+xtabs,'') If Vartype(elem) # 'O' este = este+concatval(m.elem) Else If Vartype( m.elem.BaseClass ) = 'C' And m.elem.BaseClass = 'Collection' iscollection = .T. este = m.este+'{ '+m.xtabs+'"collection'+collTagName(m.elem)+'" :' xtabs = nivel(2) Else iscollection = .F. m.este = m.este+'{' Endif este = este+procobject(m.elem, m.iscollection , m.nonullarrayitem, m.aMembersFlag ) este = este+'}' If m.iscollection xtabs = nivel(-2) este = este+m.xtabs+'}' Endif Endif Endfor este = Rtrim(m.este,1,m.xtabs) Endwith