commons-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Savitsky, Alex" <alex.savit...@Kraft.com>
Subject RE: [Digester] Parsing XML to a hashtable
Date Tue, 02 Mar 2004 19:27:27 GMT
Uhh... now I am confused too...

So let me get it straight - your suggestion is to create a helper class
XMLContact with getters/setters for each field in Contact, that would
capture XML data (instead of Contact doing it) and then push it to Contact
(using existing hashmap-like interface) at every </Contact> occurrence?
Well, that's exactly what I was trying to avoid - creating accessors for
each of 30+ fields (and that number will undoubtely grow in the nearest
future) separately :( Well, that helper class does simplify it a bit, I'll
try it...

Another option that I'm going to try is to create my own extension to
CallParam rule, which would allow passing tag name as a parameter.

Anyway, thanks for help.

Alex


-----Original Message-----
From: Martin Kersten [mailto:Martin.Kersten@Student.Uni-Magdeburg.DE]
Sent: Tuesday, March 02, 2004 12:40 PM
To: Jakarta Commons Users List
Subject: Re: [Digester] Parsing XML to a hashtable



> The <Contact> is a data structure, and its child tags (<RowId> and the
like)
> are the data fields. I'm trying to get these data fields into the
> hashtable... here's the line-by-line explanation of what I require from
the
> digester:

Ok slowly I get it. And you want to process more then one <contact> tag
right?
Ok lets face it we are doing the most simple thing first. Here is what I
would
like to suggest. (And how I would implement it).

[snip]

> <Contact>
Lets create a XMLContact (or how ever you would like to call it).

> <RowId>1</RowId>
digester calls xmlContact.setRowId('value');

> <LastName>Smith</LastName>
digester calls xmlContact.setLastName('smith');

> </Contact>
contact=pop();
digester calls peek().addContact(contact);

The required rules:
digester.addCreateObject("*/contact",XMLContact.class);
digester.addBeanPropertySetter("*/contact/row-id","rowId");
digester.addBeanPropertySetter("*/contact/last-name","lastName");
digester.addSetNext("*/contact","addContact");

Now this should do it but it is likely to create a helper instance for
every contact. If you dislike this, you can recycle the helper instances
using a lightweight factory using addFactoryCreate(...).

The factory may handle a list of contact instances to recycle and stuff.
After a addContact is performed the addContact method may pass
the Contact instance to the factory though the Contact instance can
be reused if the Factory needs to pass a new instance of Contact
to the callee.

This solution should be rubust and meets your requirements.

Another option is:
remove the addCreateObject rule. Then the addBeanPropertySetter
is passed to the top object. But now you have to notice your map
if </contact> was meet. You can use a rule for that. Check the
source code about how the addSetNext is implemented (how the
rule is implemented and stuff). You should use a own rule which
works the same way SetNext works but which is peeking on the
stack rather than poping (cause there is no Contact to pop from the stack).

This would not suite me, because you will mix code. Another idea
which comes to my mind is simply using a custom rule to substitue
the ObjectCreate rule. Image we are placing a lovly rule asking our
topmost stack object (which is the Contacts map we are trying to feed).

So lets look at this:

class Contacts { //our mapping object.
  [...]
  XMLContact singletonInstance;
  Contacts() {
      singletonInstance=new XMLContact();
  }
  public XMLContact getXMLContact() {
     return singletonInstance;
  }

  public class XMLContact {
       ...
  }
}

Now we can use our substitued create rule asking the
top Contacts instance for its XMLContact helper.
(Note: we may not use a factory here because one
singletonInstance of XMLContact is created per
every Contacts instance and not one for all.)

Now while digester processes the <contact> tag
and it subtags XMLContact stores the rowId and
the lastName informations.

So if digester reaches </contact> the SetNext rule
substitute is called, which itself pops our XMLContact
instance from within the stack and calls a method like
xmlContact.execute() or perform() which allows
XMLContact to check if it's containing valid informations
and calls something like contacts.set(rowId,lastName);

Since digester is running in a singeThread we can
reuse our singleton XMLContact instance everytime.

So only one instance of our simple helper is created.

To summarize:
1. If you don't care about memory footprint you
should use a simple helper + Contacts.addContact().
(which would also provide easier tests and may also
be usefull for processing non-xml resources and also
for setting up testing scenarios)

2. Use a factory (maybe use Contacts as factory
providing a static creation method). Once addContact
was called the Contact instance is pooled by the
factory (pool method called by Contacts.addContact).
This will reduce the footprint down to one instance
per run but also would allow processing of more then
one thread (while the threads are feeding the
same Contacts instance).

3. Implement two rules for reusing a single instance
per every Contacts instance. But this is a bit overdoing
but may be usefull once the XMLContact (or whatever
your helper is called) gets some additional logic and is
worth being a toplevel class.


Hope this didn't confused you much. Just use the first
approach look what the profiler is saying about this
solution and refactor the first solution.

But you may also reinvent the wheel and provide
a custom ruleset calling Contacts directly. Like
Contacts.configure(Digester digester) {
    digester.setRuleSet(...) //not sure about the method name
}


Hope this helps,

Martin (Kersten)

PS: Maybe there is a simplier way but I think the first
and second solution is very good.

> -----Original Message-----
> From: Martin Kersten [mailto:Martin.Kersten@Student.Uni-Magdeburg.DE]
> Sent: Tuesday, March 02, 2004 11:34 AM
> To: Jakarta Commons Users List
> Subject: Re: [Digester] Parsing XML to a hashtable
>
>
> > I think I wasn't very clear about my problem - the Contact bean itself
has
> a
> > hashmap, and "RowId" and "LastName" act as fields there. Here's the full
> > source just to eliminate all confusion:
> Well I am still confused :) What should happen when the xml gots parsed?
> Should the contact do what? I think you are thinking to complex and try
> to do two steps at once.
>
> What is put? Please can you use your example xml and explain on
> every tag, what digester should do at this certain point? You shouldn't
> be afraid to create helper objects.
>
> Martin (Kersten)
>
> >
> > public class Contact {
> >
> > // Actual bean contains 30+ fields,
> > // omitted for simplicity sake
> > private Map fields = new HashMap();
> >
> > // Field name constants to use with get()
> > public static final String ROW_ID = "RowId";
> > public static final String LAST_NAME = "LastName";
> >
> > public String get(String name) {
> > return (String) fields.get(name);
> > }
> >
> > public void set(String name, String value) {
> > fields.put(name, value);
> > }
> >
> > public void populate(String xml) throws Exception {
> >
> > Digester d = new Digester();
> >
> > // *********************************
> > // Now what to put there for rules??
> > // *********************************
> >
> > digester.push(this);
> >
> > digester.parse(new StringReader(xml));
> >
> > }
> >
> > public static void main(String[] args) throws Exception {
> >
> > String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
> > "<Contact>" +
> > "<RowId>1</RowId>" +
> > "<LastName>1</LastName>" +
> > "</Contact>";
> >
> > Contact c = new Contact();
> >
> > c.populate(xml);
> >
> > // What I want to get is two entries in "fields" hashmap,
> > // ("RowId", "1") and
> > // ("LastName", "Smith")
> > }
> >
> > }
> >
> > Now, the SetProperty rule doesn't work there, because it operates on tag
> > attributes, not on the tag itself - again, unless I'm missing some
obscure
> > parameter combination that make it work in a different way...
> >
> > Tried CallMethod rule, too, and CallParam seem to be unable to pick up
the
> > tag name (e.g., "RowId") as a parameter value
> >
> > Any other ideas?
> >
> > Thanks,
> >
> > Alex
> >
> > -----Original Message-----
> > From: Martin Kersten [mailto:Martin.Kersten@Student.Uni-Magdeburg.DE]
> > Sent: Tuesday, March 02, 2004 9:19 AM
> > To: Jakarta Commons Users List
> > Subject: Re: [Digester] Parsing XML to a hashtable
> >
> >
> > Hi,
> >
> > > I have a HashMap-based java bean that I'm trying to populate from an
XML
> > > file. Now, the only accessors java bean exposes are get(String) and
> > > set(String, String), and XML file contains its data in body text, like
> > that:
> > >
> > > <Contact>
> > > <RowId>1</RowId>
> > > <LastName>Smith</LastName>
> > > </Contact>
> > >
> > > Now, while the whole setup looks fairly common, it doesn't look like
> > there's
> > > an easy way to parse it... I tried the CallMethod rule, but apparently
> it
> > > can accept parameters from pretty much anywhere - from body text, tag
> > > attribute, even the tag node up the stack - except from the tag name
> > itself!
> > > Am I missing something there?
> > >
> > > Thanks in advance,
> > Check the addSetProperty method. Should help you, I guess. If not
> > compose the contact using a bean and add it to your map represented
> > by the next xml level tag (like <contacts>) using addSetNext(..).
> >
> > Example:
> >
> >    addCreateObject("*/contact", Contact.class);
> >    ... (initialize the contact rowId and lastName properties using
> > addSetProperties)
> >    addSetNext("*/contact","addContact");
> >
> >    + top level (or next higher level).
> >    addCreateObject("contacts", ContactMap.class);
> >
> > //add method
> > contacts.addContact(Contact contact) {
> >     if(contact.isValid())
> >        contactMap.set(contact.getRowId(), contact.getLastName());
> > }
> >
> > I think you can guess the meaning of it.
> >
> > Summary: Try addSetProperty rule first. If it is not working try the
> > second approach.
> >
> >
> > Bye,
> >
> > Martin (Kersten)
> >
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail: commons-user-help@jakarta.apache.org
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail: commons-user-help@jakarta.apache.org
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-user-help@jakarta.apache.org
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-user-help@jakarta.apache.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-user-help@jakarta.apache.org

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-user-help@jakarta.apache.org


Mime
View raw message