fbpx
Welcome, Guest
Username: Password: Remember me
  • Page:
  • 1

TOPIC: DBFCDX driver, array in memo field

DBFCDX driver, array in memo field 30 May 2020 15:33 #14775

  • Jean3riv's Avatar

  • Jean3riv

  • Topic Author


  • Posts: 1
  • Hello everybody
    In cavo28 sp3, I can write and retrieve an array from the memo field.
    In #Sharp, VO, I can only retrieve the array, the FieldPut() function return a crash application...
    XSharp.Error
    Unknown Error occurred

    The question:
    Is xSharp won't support anymore the array in a memo field, or it is just a bug?

    Thank in advance for your cooperation...

    PS:Driver DBFVFP is doing the same

    Best regards
    Jean Raymond
    rayonline.com/

    Please Log in or Create an account to join the conversation.

    DBFCDX driver, array in memo field 30 May 2020 20:08 #14777

  • robert's Avatar

  • robert


  • Posts: 1741
  • Jean,
    For backward compatibility we added reading of arrays from memos.
    However this format is so complicated that we did not add writing.
    In a DotNet environment we recommend that you serialize your arrays to a string and store that string to the memo.
    You can for example use the JSON format, which is supported by other languages as well/

    Robert
    XSharp Development Team
    The Netherlands

    Please Log in or Create an account to join the conversation.

    DBFCDX driver, array in memo field 31 May 2020 10:32 #14781

  • Sherlock's Avatar

  • Sherlock


  • Posts: 24
  • JSON can be a bit a learn curve initially at least. This is Serialiser for Arrays works a treat. I still use and can also can be read natively by PHP. Enough examples in the code... Enjoy

    ACCESS Array2PHPString AS STRING PASCAL CLASS Convert2PHPStringArray
    RETURN SELF:cPHPString

    CLASS Convert2PHPStringArray
    PROTECT aProcessed := {} AS ARRAY // Phil McGuinness - APRIL, 2006
    PROTECT cPHPString := "" AS STRING // cPHPString := Convert2PHPString{ aArray }:Array2PHPString
    PROTECT nLenArrayLevel1 AS DWORD
    PROTECT nLenArrayLevel2 AS DWORD
    PROTECT aDeserialized := {} AS ARRAY
    //
    DECLARE METHOD DataType // Build aProcessed ARRAY with substrings for PHPString output
    DECLARE ACCESS Array2PHPString // Return the PHPString [ ie a:2:{i:0;s:1:"a";i:1;a:3:{i:0;s:1:"c";i:1;s:1:"d";i:2;s:4:"e";}} ]

    // Deserialization - Ales, 01/02/2013
    DECLARE ACCESS PHPString2Array // Return the VO array
    DECLARE METHOD DeserializePHPArray // Called internally if the argument in Init() is a string
    DECLARE METHOD ExtractTextBetweenMarkers
    DECLARE METHOD ExtractPHPArrayString
    //
    METHOD DataType( uWorking AS USUAL, nElement AS DWORD, nArrayElements AS DWORD, symLevel AS SYMBOL) AS VOID PASCAL CLASS Convert2PHPStringArray

    LOCAL cSubstring, cType := [s:] AS STRING
    LOCAL cPreFix := "", cPostFix := "" AS STRING
    LOCAL nItems AS DWORD

    DO CASE
    CASE IsLogic( uWorking )
    cType := [b:] + IIF(uWorking, "1","0")
    //
    CASE IsString( uWorking )
    // IF SLen(uWorking) <= 10 .AND. CToD(uWorking) != NULL_DATE // 02:10 PM passes as CTOD() date, and the date s:10:"02/01/0349"
    IF SLen(uWorking) <= 10 .AND. CToD(uWorking) != NULL_DATE .AND. Occurs(uWorking, "/") = 2 // Change PMG 05/09/2014
    cSubstring := DToC(CToD(uWorking))
    ELSE
    cSubstring := AllTrim(StrTran(uWorking, ["], _CHR(32) )) // Stringify result
    ENDIF
    cType := [s:] + NTrim(SLen(cSubstring)) + [:"] + cSubstring + ["]
    //
    CASE IsNumeric( uWorking )
    cSubstring := AllTrim(AsString(uWorking)) // Stringify result
    cType := IIF( Frac(uWorking) != 0, [d:] , [i:] ) + cSubstring
    //
    CASE IsDate( uWorking )
    cSubstring := DToS(uWorking)
    cType := [s:] + NTrim(SLen(cSubstring)) + [:"] + cSubstring + ["]
    //
    OTHERWISE
    cSubstring := AllTrim(AsString(uWorking)) // Stringify result
    ENDCASE
    //
    IF InList( symLevel, #LEVEL1, #LEVEL2 )
    //
    DO CASE
    CASE symLevel = #LEVEL1 ; nItems := SELF:nLenArrayLevel1
    CASE symLevel = #LEVEL2 ; nItems := SELF:nLenArrayLevel2
    ENDCASE
    //
    IF nElement = 1
    cPreFix := "i:" + AsString( nArrayElements - 1 ) + ";a:" + AsString( nItems ) + ":{"
    //
    ELSEIF nElement = nItems
    cPostFix := "}"
    ENDIF
    ENDIF
    //
    AAdd( SELF:aProcessed, cPreFix + [i:] + AsString(nElement-1) + ";" + cType + ";" + cPostFix )
    //
    RETURN

    METHOD Init( uUserData ) CLASS Convert2PHPStringArray

    LOCAL nLenArray, xx, yy, zz AS DWORD // Allow for Single Dimension and up to 3 dimension array.

    IF IsArray( uUserData )
    nLenArray := ALen( uUserData )
    SELF:cPHPString := [a:] + AsString( nLenArray ) + [:] // a:6:
    SELF:cPHPString += '{'
    //
    FOR xx := 1 TO nLenArray // ?? elements
    IF IsArray( uUserData[xx] )
    SELF:nLenArrayLevel1 := ALen( uUserData[xx] )
    //
    FOR yy := 1 TO SELF:nLenArrayLevel1 // ?? elements
    SELF:DataType( uUserData[xx][yy], yy, xx, #LEVEL1 )
    IF IsArray( uUserData[xx][yy] )
    SELF:nLenArrayLevel2 := ALen( uUserData[xx][yy] )
    //
    FOR zz := 1 TO SELF:nLenArrayLevel2 // ?? elements
    SELF:DataType( uUserData[xx][yy][zz], zz, yy, #LEVEL2 )
    NEXT
    SELF:nLenArrayLevel2 := 0
    ENDIF
    NEXT
    SELF:nLenArrayLevel1 := 0
    ELSE
    SELF:DataType( uUserData[xx], xx, xx, #LEVEL0 )
    ENDIF
    NEXT
    //
    FOR xx := 1 TO ALen( SELF:aProcessed )
    SELF:cPHPString += SELF:aProcessed[xx]
    NEXT
    SELF:cPHPString += '}'
    SELF:aProcessed := {}
    //
    ELSEIF IsString( uUserData )
    SELF:aDeserialized := SELF:DeserializePHPArray(AsString(uUserData))
    ENDIF
    //
    RETURN SELF

    // FUNCTION Start() AS STRING
    // LOCAL aArray AS ARRAY
    // aArray := { 1, "2", "2/03/2006", 1.234 }
    // aArray := { {"a","b","c"},{"d","e","f"}}
    // aArray := { "a",{ "c","d","e" } }
    //
    // cPHPString := Convert2PHPStringArray{ aArray }:Array2PHPString
    // ==========================
    // aArray := { 1, "2", "2/03/2006", 1.234 } // INPUT
    // a:4:{i:0;i:1;i:1;s:1:"2";i:2;s:9:"2/03/2006";i:3;d:1.24;} // OUTPUT
    // ==========================
    // aArray := { {"a","b","c"},{"d","e","f"}} // INPUT
    // a:2:{i:0;a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}i:1;a:3:{i:0;s:1:"d";i:1;s:1:"e";i:2;s:1:"f";}} // OUTPUT
    // ==========================
    // aArray := { { "a",{ "c","d","e" }} } // INPUT
    // a:2:{i:0;s:1:"a";i:1;a:3:{i:0;s:1:"c";i:1;s:1:"d";i:2;s:4:"e";}} // OUTPUT
    // ==========================
    // MemoWrit( "c:\" + 'phptest.txt', cPhpString)
    // ShellExecute( NULL, String2Psz("open"), String2Psz("notepad.exe"), String2Psz("c:\" + 'phptest.txt'), NULL, SW_SHOWNORMAL )
    //
    // aArray := xConvert2PHPStringArray{ 'a:2:{i:0;a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}i:1;a:3:{i:0;s:1:"d";i:1;s:1:"e";i:2;s:1:"f";}}' }:PHPString2Array
    //
    // RETURN NIL

    // But this works:
    //x := xConvert2PHPStringArray{ 'a:4:{i:0;i:1;i:1;s:1:"2";i:2;s:9:"2/03/2006";i:3;d:1.24;}' }
    //x := xConvert2PHPStringArray{ 'a:2:{i:0;s:2:"AB";i:1;s:2:"CD";}' }
    //x := xConvert2PHPStringArray{ 'a:6:{i:0;i:33;i:1;d:12.53999999999999914734871708787977695465087890625;i:2;s:4:"strA";i:3;s:3:"q"q";i:4;s:3:"w"w";i:5;s:6:"abcdef";}' }
    //x := xConvert2PHPStringArray{ 'a:2:{i:0;a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}i:1;a:3:{i:0;s:1:"d";i:1;s:1:"e";i:2;s:1:"f";}}' }
    // aArray := x:PHPString2Array

    // aArray := { 2, 3, 4 }
    // aArray := { 1, "2", "2/03/2006", 1.234 }
    // aArray := { {"a","b","c"},{"d","e","f"}}
    // aArray := { "a",{ "c","d","e" } }
    //

    METHOD DeserializePHPArray(cPHPString AS STRING) AS ARRAY PASCAL CLASS Convert2PHPStringArray

    LOCAL aOut := { } AS ARRAY
    LOCAL nArrlen, nLen, nNEXT, nIndex, nValue, nStrLen AS DWORD
    LOCAL iValue AS LONGINT
    LOCAL rValue AS REAL8
    LOCAL lTypeExpected := FALSE AS LOGIC
    LOCAL lIsOK := TRUE AS LOGIC
    LOCAL cTmp AS STRING
    LOCAL dtValue AS DATE

    IF SubStr(cPHPString, 1, 2) == "a:" // Validation if the PHP array really starts with "a:"
    nLen := SLen(cPHPString)
    cTmp := SELF:ExtractTextBetweenMarkers(cPHPString, ":", ":{", 1, @nNEXT)
    //
    IF SLen(cTmp) > 0 // Notice my paranoia :-)
    nArrlen := DWORD(Val(cTmp)) // Getting the number of a PHP array members
    aOut := ArrayCreate(nArrlen)
    //
    DO WHILE nNEXT <= nLen .AND. lIsOK
    //
    IF lTypeExpected == FALSE
    IF SubStr(cPHPString, nNEXT, 1) == "}" // End of current array - stop parsing
    lIsOK := FALSE
    //
    ELSEIF SubStr(cPHPString, nNEXT, 2) == "i:" // Index
    cTmp := SELF:ExtractTextBetweenMarkers(cPHPString, ":", ";", nNEXT, @nNEXT)
    IF SLen(cTmp) > 0
    nIndex := DWORD(Val(cTmp)) + 1 // PHP index starts with 0, VO starts with 1
    lTypeExpected := TRUE
    ELSE
    lIsOK := FALSE // Can't get an index
    ENDIF
    ELSE
    lIsOK := FALSE // Something's not right, stop parsing
    ENDIF
    ELSE // This branch deals with types
    DO CASE
    CASE SubStr(cPHPString, nNEXT, 2) == "i:" // Integer or DWORD
    cTmp := SELF:ExtractTextBetweenMarkers(cPHPString, ":", ";", nNEXT, @nNEXT)
    IF SLen(cTmp) > 0
    iValue := LONGINT(Val(cTmp))
    IF iValue < 0 // If it's less than zero, store it as LONGINT, otherwise as DWORD
    aOut[nIndex] := iValue
    ELSE
    nValue := DWORD(iValue)
    aOut[nIndex] := nValue
    ENDIF
    lTypeExpected := FALSE
    ELSE
    lIsOK := FALSE // Something's not right, stop parsing
    ENDIF
    // ==========
    CASE SubStr(cPHPString, nNEXT, 2) == "d:" // Double
    cTmp := SELF:ExtractTextBetweenMarkers(cPHPString, ":", ";", nNEXT, @nNEXT)
    IF SLen(cTmp) > 0
    rValue := Val(cTmp)
    aOut[nIndex] := rValue // Store as REAL8
    lTypeExpected := FALSE
    ELSE
    lIsOK := FALSE // Something's not right, stop parsing
    ENDIF
    // ==========
    CASE SubStr(cPHPString, nNEXT, 2) == "s:" // String
    cTmp := SELF:ExtractTextBetweenMarkers(cPHPString, ":", ":", nNEXT, @nNEXT)
    IF SLen(cTmp) > 0
    nStrLen := DWORD(Val(cTmp)) // Get string length
    IF SubStr(cPHPString, nNEXT, 1) == '"' // Verification that the string starts with double quote
    nNEXT += 1
    cTmp := SubStr(cPHPString, nNEXT, nStrLen) // Note: we assume no escape characters here! BEWARE !!!!
    //
    IF (SLen(cTmp) >= 8 .AND. SLen(cTmp) <= 10 .AND. At("/", cTmp) > 0) .OR. SToD(cTmp) != NULL_DATE // Maybe a date - you may omit this branch if you don't want
    // IF (SLen(cTmp) >= 8 .AND. SLen(cTmp) <= 10 .AND. Occurs(cTmp, "/") = 2) .OR. SToD(cTmp) != NULL_DATE // Maybe a date - you may omit this branch if you don't want // PMG change 05/09/2014
    // to convert string to date
    IF SToD(DToS(CToD(cTmp))) != NULL_DATE
    // IF SToD(cTmp) != NULL_DATE
    // dtValue := SToD(cTmp)
    dtValue := SToD(DToS(CToD(cTmp)))
    ELSE
    dtValue := CToD(cTmp)
    ENDIF
    IF dtValue != NULL_DATE
    aOut[nIndex] := dtValue // Set as date
    ELSE
    aOut[nIndex] := cTmp // Set as string
    ENDIF
    ELSE
    aOut[nIndex] := cTmp // Set as string
    ENDIF
    nNEXT += nStrLen - 1
    nNEXT := At3('";', cPHPString, nNEXT) // Advance nNEXT - this works even if the string contained escape characters
    IF nNEXT > 0
    nNEXT += 2
    ENDIF
    ELSE
    lIsOK := FALSE // Something's not right, stop parsing
    ENDIF
    lTypeExpected := FALSE
    ELSE
    lIsOK := FALSE
    ENDIF
    //
    CASE SubStr(cPHPString, nNEXT, 2) == "a:" // Array
    cTmp := SELF:ExtractPHPArrayString(cPHPString, nNEXT, @nNEXT)
    IF SLen(cTmp) > 0
    aOut[nIndex] := SELF:DeserializePHPArray(cTmp) // This method is called recurrently - bear it on mind if you'll debug the code
    lTypeExpected := FALSE
    ELSE
    lIsOK := FALSE // Something's not right, stop parsing
    ENDIF
    OTHERWISE
    lIsOK := FALSE // Something's not right, stop parsing
    ENDCASE
    ENDIF
    //
    IF nNEXT == 0
    lIsOK := FALSE // Another paranoid code - in case no expected pattern detected,
    // end the sordid story
    ENDIF
    ENDDO
    ENDIF
    ENDIF
    //
    RETURN aOut
    METHOD ExtractPHPArrayString(cInput AS STRING, nStartPos AS DWORD, nNEXT REF DWORD) AS STRING PASCAL CLASS Convert2PHPStringArray

    LOCAL cOut := "" AS STRING
    LOCAL cOneChar
    LOCAL nLen, nBrackets AS DWORD
    LOCAL lInsideQuotes := FALSE AS LOGIC

    nNEXT := At3("{", cInput, nStartPos)

    IF nNEXT > 0
    nNEXT += 1
    nBrackets := 1
    nLen := SLen(cInput)
    //
    DO WHILE nNEXT <= nLen .AND. nBrackets > 0
    cOneChar := SubStr(cInput, nNEXT, 1)
    //
    IF cOneChar == '"'
    IF !lInsideQuotes
    lInsideQuotes := TRUE
    ELSE
    IF nNEXT < nLen
    IF SubStr(cInput, nNEXT + 1, 1) == ";"
    lInsideQuotes := FALSE
    ENDIF
    ENDIF
    ENDIF
    ELSEIF cOneChar == "{" .AND. ! lInsideQuotes
    nBrackets += 1
    ELSEIF cOneChar == "}" .AND. ! lInsideQuotes
    nBrackets -= 1
    ENDIF
    nNEXT += 1
    ENDDO
    //
    IF nBrackets == 0
    cOut := SubStr(cInput, nStartPos, nNEXT - nStartPos)
    ENDIF
    //
    IF nNEXT <= nLen
    IF SubStr(cInput, nNEXT, 1) == ";"
    nNEXT += 1
    ENDIF
    ENDIF
    ENDIF
    //
    RETURN cOut

    METHOD ExtractTextBetweenMarkers(cInput AS STRING, cStartMarker AS STRING, cEndMarker AS STRING, nStartPos AS DWORD, nNEXT REF DWORD) AS STRING PASCAL CLASS Convert2PHPStringArray

    LOCAL cOut := "" AS STRING
    LOCAL nStart AS DWORD

    nNEXT := 0
    IF SLen(cStartMarker) == 0
    nNEXT := nStartPos
    ELSE
    nNEXT := At3(cStartMarker, cInput, nStartPos)
    ENDIF

    IF nNEXT > 0
    nNEXT += SLen(cStartMarker)
    nStart := nNEXT

    IF SLen(cEndMarker) == 0
    nNEXT := SLen(cInput) + 1
    ELSE
    nNEXT := At3(cEndMarker, cInput, nNEXT)
    ENDIF

    IF nNEXT > 0
    cOut := SubStr(cInput, nStart, nNEXT - nStart)
    nNEXT += SLen(cEndMarker)
    ENDIF
    ENDIF
    //
    RETURN cOut

    ACCESS PHPString2Array AS ARRAY PASCAL CLASS Convert2PHPStringArray
    RETURN SELF:aDeserialized
    Phil McGuinness

    Please Log in or Create an account to join the conversation.

    • Page:
    • 1