commons-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Martin Kersten" <Martin.Kers...@Student.Uni-Magdeburg.DE>
Subject Re: [Digester] Parsing XML to a hashtable
Date Tue, 02 Mar 2004 19:46:02 GMT
> 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...
Well now I know what you are up to :) Don't you have a Data object for
your Contact element? By the way you only need to do setter methods
then.

like
Contact.setMyField(String value) {
   fields.set("myField",value);
}

but you will have 30 of it. Its sad. Maybe you should use to create
a private subclass to not expose these setters to users of Contact.

Contact {

    private class XMLContact {
       Contact.setMyField(String value) {fields.set("myField",value); }
    }

    populate(String xml) {
         digester.push(new XMLContact()); //instead of contact
         digester.parse();
     }
}

The digester configuration should also become a part of XMLContact.
And 30+ methods without javadoc and stuff should be easier to add.
(poor boy you have to test all of those :)

I wish you luck.

Martin (Kersten)

P.S. I thought you collect all simple contacts in a map mapping an rowid
to a given last-name :). No wonder why I was offroad :)

>
> 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
>


---------------------------------------------------------------------
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