Reputation: 11
I've seen different threads that discuss the kind of issue I have. Here are few: http://www.mailinglistarchive.com/[email protected]/msg05061.html, Digester: Extracting node name).
Yet, I still can't find a solution to this issue. Here is my XML data:
<rows>
<row>
<firstname>Paul</firstname>
<lastname>Moris</lastname>
</row>
<row>
<firstname>John</firstname>
<lastname>Aiyer</lastname>
<age>35</age>
</row>
</rows>
What I want is define XML rules that would allow me to map each row into a map. I can't map the elements by referring to their names because not all possible elements are known upfront.
I was hoping that something like this would allow me to do this:
<digester-rules>
<pattern value="rows/row">
<object-create-rule classname="Address"/>
<set-next-rule methodname="add" paramtype="java.lang.Object"/>
<set-properties-rule/>
<pattern value="*">
<call-method-rule methodname="set" paramcount="2"/>
<call-param-rule paramnumber='0'/>
<call-param-rule paramnumber='1'/>
</pattern>
</pattern>
</digester-rules>
the implementation of Address is:
public class Address {
Map<String,String> c= new HashMap<String,String>();
public void set(String name, String value){
c.put(name, value);
}
public String toString(){
return c.toString();
}
}
Unfortunately, when I run this code I get two addresses that are created but with empty underlying map. When I use ExtendedBaseRules, nothing is even matched.
Any help would be much appreciated.
Max.
Upvotes: 1
Views: 1656
Reputation: 2019
I think that Digester does not excel in parsing legacy xml. But, fortunately, it's extensible enough that it can be tweaked to work in some of these cases.
I managed to resolve this problem only by using the java api, not with the xml rules. It's important to use the Digester's ExtendedBaseRules class in order to match the patterns rows/row/* and to subclass the standard CallParamRule in order to pass the tag name as the first argument to the method addAddressLine() of the class Address (I renamed the method set() in your class Address to addAddressLine()).
Digester digester = new Digester();
digester.setRules(new ExtendedBaseRules());
digester.setValidating( false );
digester.addObjectCreate("rows", Addresses.class);
digester.addObjectCreate( "rows/row", Address.class );
digester.addSetNext( "rows/row", "add");
digester.addSetProperties("rows/row");
digester.addCallMethod("rows/row/*", "addAddressLine", 2);
digester.addRule("rows/row/*", new TagNameAwarePathCallParamRule(0));
digester.addCallParam("rows/row/*", 1);
Addresses addresses = (Addresses) digester.parse(new File(FILE_TO_PARSE));
The class TagNameAwarePathCallParamRule I implemented for this purpose:
public class TagNameAwarePathCallParamRule extends CallParamRule {
public TagNameAwarePathCallParamRule(int paramIndex) {
super(paramIndex);
}
public void end(String namespace, String name) {
if (bodyTextStack != null && !bodyTextStack.empty()) {
// what we do now is push one parameter onto the top set of
// parameters
Object parameters[] = (Object[]) digester.peekParams();
parameters[paramIndex] = name;
}
}
}
The classes I used for storing the output of the parsing:
public class Addresses {
private List<Address> addresses = new ArrayList<Address>();
public void add(Address a) {
addresses.add(a);
}
public List<Address> getAddresses() {
return Collections.unmodifiableList(addresses);
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (Address a : addresses) {
sb.append("Address: ").append(a.toString()).append(',');
}
return sb.toString();
}
}
public class Address {
Map<String,String> c= new HashMap<String,String>();
public void addAddressLine(String name, String value){
c.put(name, value);
}
public String toString(){
return c.toString();
}
}
Upvotes: 1