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:[EMAIL PROTECTED] 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:[EMAIL PROTECTED] > 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:[EMAIL PROTECTED] > > 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: [EMAIL PROTECTED] > > For additional commands, e-mail: [EMAIL PROTECTED] > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: [EMAIL PROTECTED] > > For additional commands, e-mail: [EMAIL PROTECTED] > > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
