Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations MikeeOK on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

serialize/de-serialize Empty object? 1

Status
Not open for further replies.

wgcs

Programmer
Mar 31, 2002
2,056
EC
Anyone have a way to serialize/deserialize an empty object subclass (ie:
Code:
SELECT Member
SCATTER NAME loMem

lcSerial = SERIALIZEOBJECT( loMem )
SELECT Archive
* Archive.ArcRec is a Memo 
REPLACE ArcDte WITH DATETIME(), ;
        ArcTbl WITH 'MEMBER',   ;
        ArcRec WITH lcSerial
 
wgcs,
OK, I'll "bite" - What's SERIALIZEOBJECT() suppose to do?

What are you trying to accomplish?

Rick
 
Basically, like the java "serializableobject" interface, provide a mechanizm to convert an object to a string, which string can later be converted back into the object.

This is very useful for serial communications (say, sending the object over TCP/IP from a VFP Data server to a VFP data client [wink]).

XML/SOAP is one format of serialization.
CURSORTOXML()/XMLTOCURSOR() is a matched pair of serializer/deserializer.

I figured that if anyone wrote one for the SCATTER NAME/"Empty" object subclasses, I'd use theirs.... otherwise, I'll write my own soon.
 
Ok, here's a simple XML serializer/deserializer for SCATTER NAME objects, and any other objects based on Empty without sub-objects. (it also will serialize all properties for any other object, but the deserializer will always base the resultant object on "empty")

use it like this:
Code:
USE anyTable
SCATTER NAME oRec MEMO
cRec = serializeobject(oRec)
* do anything with the character data cRec...
*  ...say store it in a text file, 
*  ...or send it through an email
*  ...or send it through TCP/IP
*  ...or pass it across a COM boundary
*  ...or, well, you get the picture!

oNewRec = deserializeobject(cRec)

* now examine oNewRec in the Debug Watch Window to see that it resembles oRec!


PROCEDURE SerializeObject
LPARAMETERS toObj
* Written by William GC Steinford
*  Input: toObj is a subclass of Empty (or a SCATTER NAME object)
* Output: a string representing toObj
LOCAL lnMem, laMem[1], lcOut, lcProp, lvData, lnI
lnMem = AMEMBERS(laMem,toObj,0)
SET TEXTMERGE TO MEMVAR lcOut ON
\\<? version="1.0" encoding="UTF-8" ?>
\<object class="empty">
FOR lnI = 1 TO lnMem
  lcProp = laMem[lnI]
  \  <property name="<<laMem[lnI]>>"
  lvData = EVALUATE('toObj.'+lcProp)
  * from lexRegistry.AnyTypeToC
  do case
    case varType(lvData)='T'
      \\ type="DATETIME">
      if empty(lvData)       
        \\{/:}
      ELSE
        lcData = "{^" + Transform(Year(  lvData)) + '/' ;
                      + Transform(Month( lvData)) + '/' ;
                      + Transform(Day(   lvData)) + ' ' ;
                      + Transform(hour(  lvData)) + ':' ;
                      + Transform(Minute(lvData)) + ':' ;
                      + Transform(Sec(   lvData)) ;
                      + '}' 
        \\<<lcData>>
      endif
    case varType(lvData)='D'
      \\ type="DATE">
      if EMPTY(lvData)
        \\{}
      else
        lcData = '{^ '+ alltrim(Str(Year(lvData))) + '/' ;
                      + alltrim(Str(Month(lvData))) + '/' ;
                      + alltrim(Str(Day(lvData))) + '}' 
        \\<<lcData>>
      endif
    case varType(lvData)='L'
      \\ type="LOGICAL"><<iif( vData, '.T.', '.F.' )>>
    case varType(lvData)='N'
      \\ type="NUMERIC"><<Transform(lvData)>>
    case varType(lvData)='C'
      \\ type="CHARACTER">
      * UrlEncode dangerous characters:
      lcData = StrTran(lvData,'%',    '%25')
      lcData = StrTran(lcData,'"',    '%22')
      lcData = StrTran(lcData,['],    '%27')
      lcData = StrTran(lcData,'[',    '%5B')
      lcData = StrTran(lcData,']',    '%5D')
      lcData = StrTran(lcData,'<',    '%3C')
      lcData = StrTran(lcData,'>',    '%3E')
      lcData = StrTran(lcData,chr(13),'%0D')
      lcData = StrTran(lcData,chr(10),'%0A')
      \\<<lcData>>
    case varType(lvData)='O'
      * Skip sub objects.
      * We could recursively call SerializeObject to package this member.
  endcase  
  \\</property>
ENDFOR
\</object>
SET TEXTMERGE TO 
RETURN lcOut

PROCEDURE DeSerializeObject
LPARAMETERS tcObj
* Written by William GC Steinford
*  Input: tcObj is a string representing a subclass of Empty (or a SCATTER NAME object)
* Output: an actual object representing tcObj
* Example tcObj Value:
*  <? version="1.0" encoding="UTF-8" ?>
*  <object class="empty">
*    <property name="TEXT" type="CHARACTER">There     </property>
*    <property name="TEXT2" type="CHARACTER"></property>
*  </object>
*
LOCAL lnProp, laProp[1], lcObj, lnStrt, lnStop, loOut, lnI
lnStrt = ATC('<property',  tcObj )
lnStop = RATC('</object>', tcObj )
lcObj  = SUBSTR( tcObj, lnStrt, lnStop-lnStrt )
lnProp = ALINES(laProp, STRTRAN(lcObj,'</property>',CHR(13)) )
loOut  = CREATEOBJECT('Empty')
FOR lnI = 1 TO lnProp
  lvVal  = .NULL.
  lcName = .NULL.
  TRY 
    lcType = UPPER( SUBSTR(laProp[lnI], ATC('type="',laProp[lnI])+6) )
    lcName =        SUBSTR(laProp[lnI], ATC('name="',laProp[lnI])+6)
    IF NOT '"'$lcName
      lcName = .NULL.
      THROW "bad name"
    ENDIF
    lcName = LEFT(lcName,AT('"',lcName)-1)
    lcVal  =        SUBSTR(laProp[lnI], ATC('>',     laProp[lnI])+1)
    DO CASE 
      CASE lcType='DATETIME' AND TYPE(lcVal)='T'
        lvVal = EVALUATE(lcVal)
      CASE lcType='C'        AND TYPE('['+lcVal+']')='C'
        lvVal = EVALUATE('['+lcVal+']')
      CASE TYPE(lcVal)=LEFT(lcType,1)
        lvVal = EVALUATE(lcVal)
    ENDCASE
  CATCH TO oExc
    * Ignore errors... lvVal=.NULL. will signify errors.
  ENDTRY
  
  IF NOT ISNULL(lcName)
    TRY
      ADDPROPERTY(loOut, lcName, lvVal)
    CATCH TO oExc
      * Ignore errors
    ENDTRY
  ENDIF 
ENDFOR
RETURN loOut
 
wgcs,

Very nice. I've thought that the serialization available in java and .NET has always been a pretty useful feature. Not only for passing them, but also for saving state. Couldn't the same be done using SAVE TO and RESTORE FROM given that you are not trying to deserialize the object in a different language or anything. The XML is nice, but really serves very little purpose and adds overhead I would think. Just some thoughts, as I have not really ever tried this sort of thing in VFP before... I like the concept and just want to get in on the discussion.

boyd.gif

craig1442@mchsi.com
&quot;Whom computers would destroy, they must first drive mad.&quot; - Anon​
 
You had me excited for a moment there, craig: but looking at the help for SAVE TO and RESTORE FROM:
Remarks
Use RESTORE FROM to place variables and arrays back into memory from a variable file or memo field. Note that object type variables cannot be saved to a variable file or memo field.



I admit that the XML is a bit of a performance slowdown, but notice I don't use MSXML for parsing it, since that would be absurd overhead.

The Serialize functions are adaptations on two similar functions that I've used for some time now: AnyTypeToC and CToAnyType. These were meant to persist any variable type (except for objects) into individual memo fields for a "foxuser-like" settings file.

This allows me to with one function to store and retrieve program settings:
AnyParam( '\description\of\parameter', 'default value', 'put SET here to replace a stored value with the default' )
The common alternative is to have a "SYSTEM.DBF" table with one field per value, and to have only one record in it. Once you get past 255 unique values, this becomes impossible (and it is very irritatating long before that!).

In the "anyType" functions, I didn't use XML, instead storing the type as the first Character, then a ":", then a format of the value that could be EVAL()'d back to the original value. eg: "D:{06/08/2004}" or "L:.F."

This could have been extended to support the multiple properties on an object... say:
Code:
obj.Prop1 = {06/08/2004}
obj.Prop2 = .F.
obj.Prop3 = 'But, Who said that!?'
Serialized could be:
"Prop1=D:{06/08/2004},Prop2=L:.F.,Prop3=C:But$2C$20Who$20said$20that!?"
Which could be parsed pretty easily with ALINES
( I have to admit: my AnyParam function actually supports the above format, but used SetSubVal and GetSubVal functions instead of a parameter object, since VFP6 couldn't instantiate an "empty" class )

However, the XML seemed appropriate to me for representing an object, and really doesn't take much more parsing than any other format string, when multiple properties are involved. It also has these benefits:
[ol][li]It potentially can be extended to store a hierarchy for compound objects[/li]
[li]it enforces (by ideology) using plain-text characters, and has the potential to support unicode. ( "SAVE TO"/"RESTORE FROM" use binary storage, I'd assume, being simply "dumps" of the memory values)[/li]
[li]when communicating over TCP/IP, it makes communication with other languages easy.[/li]
[li]the objects represented in it could be stored in any RDBMS database varchar field (since they are plain text)[/li]
[/ol]

The above example would be in XML much more readable:
Code:
<? version="1.0" encoding="UTF-8" ?>
<object class="empty">
  <property name="Prop1" TYPE="DATE">{06/08/2004}</property>
  <property name="Prop2" TYPE="LOGICAL">.F.</property>
  <property name="Prop3" TYPE="CHAR">But$2C$20Who$20said$20that!?</property>
</object>
 
ok, I can see your points and since the save to/restore from won't work for objects...I'm thinking how we can expand on this idea...I wondering about using an xml parser (like what you are doing but one that is in general use) and what compression would do with this idea. Perhaps a generic way to serialize/deserialize any variables regardless of type could be had and also using compression would optimize it for sending the information across the wire. What makes me think of it is that I a couple years ago I wrote a VFP data server and a java frontend (client was deployed on multiple OS's) and I used XML as the go between...worked really good. The parser and compression made it really fast across the wire. I used SAX instead of DOM...I found the events raised really useful and overall it was quite a bit faster/sleeker in my tests...
wgcs said:
notice I don't use MSXML for parsing it, since that would be absurd overhead
...it might be worth some time to find out just how much overhead would constitute too much here...I mean there really does appear to be some real possibilities with your idea and if a straight-forward way could be constructed to serialize/deserialize objects/vairables/you name it in VFP (not hard at all to get the data type of the variable and then if an object to grab all the properties and such, amembers and many other functions come to mind) using statndard XML technology (parser and compression) this might turn out to be quite a thing. There could even be some use for it in regards to sending information back and forth between apps created in other languages perhaps. I am of course still thinking outloud and have yet to get my hands dirty, but that's not unusual for me. [smile]

boyd.gif

craig1442@mchsi.com
&quot;Whom computers would destroy, they must first drive mad.&quot; - Anon​
 
Hi Craig,
When you say that you used a SAX parser instead of a DOM parser, were you working in Java, or VFP? I know both types exist in java, but I only know that MSXML is a DOM parser (I certainly don't know much about MSXML!).... Does MSXML also provide a SAX parser?
 
wgcs,

SAX has been possible in VFP since MSXML3...



...talked about in relation to VFP:



...latest MSXML SDK (read portions on SAX2):


boyd.gif

craig1442@mchsi.com
&quot;Whom computers would destroy, they must first drive mad.&quot; - Anon​
 
Hi wgcs,

I Think you should make a FAQ out of this thread. Very usefull.

Jean


Jean
 
Hi Jean... OK, will-do, when I get time.

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
One rather generic solution could also be to use Christof Wollenhaupt's AKA Christof Lange's scruct.vcx and define structs as a serialization of the object members. Then you'd had a single string and some additional struct infos (like a schema) You could transform the value-string with STRCONV(...,13/14) if you need to limit the ascii characters for http-transfer etc.

It's original intent is to help with Win API calls where structures are need as input or given as output. But you may easily create a wrapper to create a struct for an object you want to serialize and leave the transformation of the values to a serialized string and back to struct.vcx. ftp://ftp.foxpert.com/struct.zip

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top