Board index » cppbuilder » Streaming a TPersistent object

Streaming a TPersistent object


2006-03-07 02:33:28 AM
cppbuilder18
Hello all,
In my application I've created classes inherited
from TPersistent in order to use the streaming
mechanism to load/store their objects.
I've also created a wapper component, TSerialize
which looks like this:
class TSerialize: public TComponent
{
private:
AnsiString objType;
TPersistent* objZ;
AnsiString __fastcall GetObjType()
{
if (objZ)
return objZ->ClassName();
else
return "";
}
TPersistent* __fastcall GetObj()
{
if (!objZ)
objZ = createObject(objType);
return objZ;
}
public:
__fastcall TSerialize(TComponent*
AOwner):TComponent(AOwner)
{
objZ = NULL;
}
__published:
__property AnsiString
objectA={read=GetObjType,write=objType,stored=true};
__property TPersistent*
objectZ={read=GetObj,write=objZ};
};
In GetObj there's a call to createObject which is
a factory class for creating dynamic objects based
on their classname (My TPersistent objects).
Now for my problem - My objects keep a list of
their children, much like a TComponent (please
please don't ask me why I'm not simply using a
TComponent and making my life much easier), I'd
like to save those children as well and all with
one call to WriteComponent(MyRootComponent).
My children property is defined as "public:
TMyPersistentItem* children[int]={...}".
I've used
DefineProperty(children,readChild,writeChild,childrenCount)
and the functions themselves look like this:
void __fastcall WriteChild(TWriter* writer)
{
writer->WriteListBegin();
TSerialize* serialize;
for (int i=0;i<childrenCount;i++)
{
serialize = new TSerialize(NULL);
serialize->objectZ = child[i];
serialize->objectA = child[i]->ClassName();
writer->WriteComponent(serialize);
delete serialize;
}
writer->WriteListEnd();
}
void __fastcall ReadChild(TReader* reader)
{
reader->ReadListBegin();
TSerialize* serialize;
while (!reader->EndOfList())
{
serialize =
(TSerialize*)reader->ReadComponent(NULL);
addChild((TMyPersistentItem*)serialize->objectZ);
}
reader->ReadListEnd();
}
The problem is that the children are written to
the stream like this: (I'm putting this in a
textual way although the data is obviously binary)
objectZ.objectA = "TMyPersistentItem"
objectZ.objectZ.Property1 = property1value
objectZ.objectZ.Property2 = property2value
objectZ.objectZ.Property3 = property3value
etc... although in my opinion it should be written
as:
objectA = "TMyPersistentItem"
objectZ.Property1 = property1value
objectZ.Property2 = property2value
objectZ.Property3 = property3value
If I use the same code logic only using
DefineBinaryProperty instead of DefineProperty
then it works perfectly, keeping the children
levels etc... but I need to use DefineProperty in
my code and it just doesn't work.
I hope I've made myself clear enough and not
forgotten any useful detail. If you're still
reading this I hope that you can help me
understand the problem and how to fix it.
Thank you very much
Dan
 
 

Re:Streaming a TPersistent object

"Dan" < XXXX@XXXXX.COM >wrote in message
Quote
please please don't ask me why I'm not simply using a
TComponent and making my life much easier
I am sorry, but I have to ask. It WOULD make your life much easier, so why
aren't you using TComponent? Despite what the documentation says, the DFM
streaming system IS NOT designed to stream TPersistent objects by
themselves.
Quote
My children property is defined as "public:
TMyPersistentItem* children[int]={...}".
Such a property is not streamable. I would suggest you change it to be a
TCollection instead. Then it will be streamable without any extra effort on
your part at all. Otherwise, you will have to stream all of the children
manually, which obviously is not working for you.
Quote
I've used DefineProperty(children,readChild,writeChild,childrenCount)
Presumbly, you've overriden the virtual DefineProperties() method in your
TPersistent class?
Quote
and the functions themselves look like this:
serialize->objectZ = child[i];
serialize->objectA = child[i]->ClassName();
That will not work as-is. For starters, there is no objectZ and objectZ
properties in your TSerialize class. Second, properties are usually
streamed in alphabetical order, so you have to ensure that the class name is
streamed into the DFM before the object pointer, or else your serializer
class will not know which class to create when streaming the DFM back later.
Quote
The problem is that the children are written to
the stream like this
As they should be.
Quote
I'm putting this in a textual way although the data is obviously binary
You can view the DFM as text in the IDE, or choose to have the DFM saved to
file in a textual format.
Quote
although in my opinion it should be written as:
Regardless of that opinion, that is simply not how the DFM works, and you
cannot make it work that way, either. There is no way to get that kind of
format in the DFM while you continue to use a serializer class. The only
way to get the kind of layout you want to is eliminate the serializer class
altogether.
Quote
If I use the same code logic only using
DefineBinaryProperty instead of DefineProperty
then it works perfectly, keeping the children
levels etc...
You would still have serializer itself appear in the DFM, with the binary
data underneath it. Which, it sounds like, you are trying to get rid of.
Quote
I hope I've made myself clear enough and not
forgotten any useful detail.
You have mixed up your details a little, and they are not very consistent
with the code you have shown.
Gambit
 

Re:Streaming a TPersistent object

Hi Remy and thank you for your answer (Terribly
sorry that you got this to your private email,
Outlook express will be punished)
Quote
I am sorry, but I have to ask. It WOULD make
your life much easier, so
why
aren't you using TComponent? Despite what the
documentation says, the DFM
streaming system IS NOT designed to stream
TPersistent objects by
themselves.
I know. The reason I'm doing this is because my
application has an "Object
Inspector" like component and I don't want to show
TComponent's properties
(Name and Tag). I believe that I can tell the
object inspector component not
to show those properties but I'm too far ahead in
the project already to
change it AND now I'm just too curious to leave my
issue alone.
Quote
>My children property is defined as "public:
>TMyPersistentItem* children[int]={...}".

>I've used
>DefineProperty(children,readChild,writeChild,childrenCount)

Presumbly, you've overriden the virtual
DefineProperties() method in your
TPersistent class?
Ofcourse, DefineProperty is being called from the
overriden DefineProperties
Quote
>and the functions themselves look like this:

>serialize->objectZ = child[i];
>serialize->objectA =
>child[i]->ClassName();

That will not work as-is. For starters, there
is no objectZ and objectZ
properties in your TSerialize class. Second,
properties are usually
streamed in alphabetical order, so you have to
ensure that the class name
is
streamed into the DFM before the object pointer,
or else your serializer
class will not know which class to create when
streaming the DFM back
later.
Hmmm those properties exist... Please look again
at my class' definition.
objectZ holds the TPersistent object
(TMyPersistent in this example) and
objectA holds the class name (TSerialize can hold
any descendent of
TMyPersistent and there are quite a few). You're
right that they're streamed
in alphabetical order, that's why objectA holds
the text and objectZ holds
the object itself.
Quote
>The problem is that the children are written to
>the stream like this

As they should be.

>I'm putting this in a textual way although the
>data is obviously binary

You can view the DFM as text in the IDE, or
choose to have the DFM saved
to
file in a textual format.

>although in my opinion it should be written as:
Remy, could you please check again and tell me if
you're right about the
fact the each child gets the prefix of its parent,
this doesn't sound right
to me and further more, if I stream a regular
TForm to a file, its
children's properties won't be prefixed with their
owner's property name.
Let me know if I should post an example of such a
file.Also, when I use
DefineBinaryProperty, it's written exactly as I
expect and like a TForm is
streamed, the only problem is that
BinaryObjectToText will only show me
binary data instead of text for the component's
children.
Quote
You would still have serializer itself appear in
the DFM, with the binary
data underneath it. Which, it sounds like, you
are trying to get rid of.
Yes, it'll appear in the DFM but I don't really
care about that fact since
I'm converting everything to XML and just ignoring
TSerialize.
Quote
>I hope I've made myself clear enough and not
>forgotten any useful detail.

You have mixed up your details a little, and
they are not very consistent
with the code you have shown.

Please review my remarks again, especially the
part about objectA and
objectZ. I feel very close since
DefineBinaryProperty works and I don't see
anything wrong with my logic (I was hoping someone
here could tell me what's
wrong with it). Maybe I'm not supposed to stream
TPersistent for some
strange borland reason but since I'm dealing with
a wrapper here, everything
should work, I think.
Thank you
Dan
"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote
in message
Quote

"Dan" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>please please don't ask me why I'm not simply
>using a
>TComponent and making my life much easier

I am sorry, but I have to ask. It WOULD make
your life much easier, so why
aren't you using TComponent? Despite what the
documentation says, the DFM
streaming system IS NOT designed to stream
TPersistent objects by
themselves.

>My children property is defined as "public:
>TMyPersistentItem* children[int]={...}".

Such a property is not streamable. I would
suggest you change it to be a
TCollection instead. Then it will be streamable
without any extra effort on
your part at all. Otherwise, you will have to
stream all of the children
manually, which obviously is not working for
you.

>I've used
>DefineProperty(children,readChild,writeChild,childrenCount)

Presumbly, you've overriden the virtual
DefineProperties() method in your
TPersistent class?

>and the functions themselves look like this:

>serialize->objectZ = child[i];
>serialize->objectA =
>child[i]->ClassName();

That will not work as-is. For starters, there
is no objectZ and objectZ
properties in your TSerialize class. Second,
properties are usually
streamed in alphabetical order, so you have to
ensure that the class name is
streamed into the DFM before the object pointer,
or else your serializer
class will not know which class to create when
streaming the DFM back later.

>The problem is that the children are written to
>the stream like this

As they should be.

>I'm putting this in a textual way although the
>data is obviously binary

You can view the DFM as text in the IDE, or
choose to have the DFM saved to
file in a textual format.

>although in my opinion it should be written as:

Regardless of that opinion, that is simply not
how the DFM works, and you
cannot make it work that way, either. There is
no way to get that kind of
format in the DFM while you continue to use a
serializer class. The only
way to get the kind of layout you want to is
eliminate the serializer class
altogether.

>If I use the same code logic only using
>DefineBinaryProperty instead of DefineProperty
>then it works perfectly, keeping the children
>levels etc...

You would still have serializer itself appear in
the DFM, with the binary
data underneath it. Which, it sounds like, you
are trying to get rid of.

>I hope I've made myself clear enough and not
>forgotten any useful detail.

You have mixed up your details a little, and
they are not very consistent
with the code you have shown.


Gambit


 

{smallsort}

Re:Streaming a TPersistent object

"Dan" < XXXX@XXXXX.COM >wrote in message
Quote
Please look again at my class' definition.
I stand corrected. I was looking at the getter/setter names, not the
property names.
Quote
objectZ holds the TPersistent object
(TMyPersistent in this example) and
objectA holds the class name (TSerialize can hold
any descendent of
TMyPersistent and there are quite a few).
Why are you trying to serialize non-TComponent classes, anyway? In what way
exactly are you using those classes? When someone tries to circumvent the
existing architecture, my first instinct is that they are not using the
architecture properly to begin with.
Quote
Remy, could you please check again and tell me
if you're right about the fact the each child gets the
prefix of its parent,
Yes, it should, because you are creating un-named, un-owned TSerialize
instances, so the DFM doesn't know how to store them any other way, because
there are no object names available, so it has to keep drilling down the
property chains instead. And worse, you are writing individual class
instances in the context of a single property. You should not be doing
that, either. If you look at the VCL's source code for streaming
collections, for example, you will see that the individual collection items
are not being streamed as objects at all, only as property values within
nested lists. Try this instead (untested):
class TWriterAccess : public TWriter
{
public
virtual void __fastcall WriteProperties(TPersistent *Instance)
{
TWriter::WriteProperties(Instance);
}
};
void __fastcall WriteChild(TWriter *writer)
{
TPersistent *OldAncestor = Ancestor;
Ancestor = NULL;
try
{
writer->WriteListBegin();
for(int i = 0; i < ChildCount; ++i)
{
writer->WriteString(children[i]->ClassName());
writer->WriteListBegin();
((TWriterAccess*)writer)->WriteProperties(children[i]);
writer->WriteListEnd();
}
writer->WriteListEnd();
}
__finally {
Ancestor = OldAncestor;
}
}
class TReaderAccess : public TReader
{
public:
virtual void __fastcall ReadProperty(TPersistent *Instance)
{
TReader::ReadProperty(Instance);
}
};
void __fastcall ReadChild(TReader* reader)
{
// clear the children list as needed, then...
reader->ReadListBegin();
while( !reader->EndOfList() )
{
TMyPersistentItem *Item = createObject(reader->ReadString());
try
{
reader->ReadListBegin();
while( !reader->EndOfList() )
((TReaderAccess*)reader)->ReadProperty(Item);
reader->ReadListEnd();
addChild(Item);
}
catch(const Exception &)
{
delete Item;
throw;
}
}
reader->ReadListEnd();
}
Quote
this doesn't sound right to me and further more, if I stream a
regular TForm to a file, its children's properties won't be prefixed
with their owner's property name.
That is because a TForm and its children have names and owners assigned.
Your objects do not.
Quote
Also, when I use DefineBinaryProperty, it's written exactly as I
expect and like a TForm is streamed, the only problem is that
BinaryObjectToText will only show me binary data instead of text
for the component's children.
Why are you so concerned with the textual layout of the DFM? The DFM should
be used for streaming, not displaying. You shouldn't care what the layout
of the DFM looks like, just as long as the data is actually being streamed
properly.
Gambit
 

Re:Streaming a TPersistent object

Hello Remy, Sorry for not answering for so long,
I've been away
Quote
Why are you trying to serialize non-TComponent
classes, anyway? In what way
exactly are you using those classes? When
someone tries to circumvent the
existing architecture, my first instinct is that
they are not using the
architecture properly to begin with.
Well, That's an argument. I didn't see any point
to derive my classes from TComponent at the
beginning of the project. Maybe it was a mistake
and maybe not but right now I don't see a reason
to go all the way back to the beginning of the
project and fix something which isn't broken. I'm
pretty sure that I can achieve what I want without
doing this change and this why I'm here on this
newsgroup.
... Your code snippet ...
Your code doesn't work for me, the main reason is
that using WriteProperties solves my initial need
for a wrapper component (except for the root
object/s) but the output is basically the same:
it writes objectZ.child followed by
objectZ.Property1, objectZ.Property2 etc... The
problem is that my parent object doesn't have
objectZ ,the parent object's wrapper has it, so
when reading, it just can't find property objectZ
for TMyPersistentItem. Any idea how to solve this?
Quote
Why are you so concerned with the textual layout
of the DFM? The DFM should
be used for streaming, not displaying. You
shouldn't care what the layout
of the DFM looks like, just as long as the data
is actually being streamed
properly.

Originally I thought about converting the objects
tree to xml, then I realized that I can use the
VCL's serialization mechanism to store my objects
easily but I still want to be able to edit my
objects properties outside of the project (That's
why I wanted to use XML in the beginning), exactly
like I can change a form's dfm in a textual way
when viewing as text in the IDE.
Thank you