So, I started working on a blog post last evening to document the XML
encoding/decoding features, and stumbled upon a bug. The bug is really
indicative of a bigger issue surrounding the design of the XML decoding.
The idea was to have a single arg constructor with a parameter of type
XMLDecoder to start the decoding, much like the way WebObjects does it.
Somewhere along the line things started getting mangled. I've never been
fond of this constructor idea, so I think I moved away from it in the code,
but not in the design. The result is that with the way I wrote the code,
something like the following works:
Reader xml = new FileReader(XML_DATA_DIR + "encoded-object.xml");
XMLDecoder decoder = new XMLDecoder;
TestObject object = (TestObject) decoder.decode(xml);
Compare the above with the following:
Reader xml = new FileReader(XML_DATA_DIR + "encoded-object.xml");
XMLDecoder decoder = new XMLDecoder(xml);
TestObject object = new TestObject(decoder);
The first case really prods at the XML to fill in property values as best it
can. It requires a default no-arg constructor however, and the decoded
values may not quite be what the user wants. The latter should not be much
of an issue though, assuming the generated XML really models what it's
supposed to (in this case, the generated XML is coming from an object's
implementation of encodeAsXML()). Another problem I just came to realize is
that it assumes in encodeAsXML() that one is only encoding public read/write
properties, which is wrong, and thus the code is broken.
The second case really drills home the idea that the XML is driving the
object creation -- everything is done in the constructor. This of course
requires an appropriate constructor. It also requires more work on the part
of the user.
Of the two, I have been inclined to use the first one, trying to make things
easier for the user. Part of this is driven by the fact that
CayenneDataObject has a default implementation for encoding, but no default
for decoding. The first way of decoding will take care of this case, but
really fails with others.
So, I started thinking about it a bit more. Originally, I really wanted to
make this part of the XMLSerializable interface so there was a clear method
for decoding. Unfortunately, org.objectstyle.cayenne.util.XMLSerializable,
which I had built my code around, was an existing interface and I didn't
want to break the contract provided by it. However, Andrus created a new
package and a new XMLSerializable interface in it. So, I think it my be a
better idea to add a decodeXml() method to the interface. In
CayenneDataObject, I can provide a default implementation as I have with
encodeAsXml(). And in other cases, the author will have control over the
decoding, which ought to fix a lot of the problems I've been seeing.
This approach, of course, does not enforce the decoding at construction, so
I don't know if that's an issue. It isn't really one for me. Additionally,
there are cases where one may wish to encode or decode XML, but not both.
In this case, the code would have to have an empty method implementation for
the one not being used. Once again, I don't really see this being a
problem, but I wanted to get thoughts on this before I move ahead with the
changes.
If you got this far, thanks. I know it was a bit long-winded, but I wanted
to provide context with the problem and a possible solution. Any thoughts?
--
Kevin
This archive was generated by hypermail 2.0.0 : Wed Mar 09 2005 - 08:44:46 EST