The previous article gave a basic introduction to Digester, and we have already understood the basic usage of Digester. Next, we will continue to learn its related features. This article mainly involves the following contents:
Rule module binding, complete the pre-binding of rules by defining a RulesModule interface implementation class, and reuse it at runtime
Asynchronous parsing xml
Parse variables in xml, such as ${sys.user}
Use the constructor with parameters to create objects, and the parameters come from xml node data
Before this, our basic process of using Digester was to bind the rules each time the program is running, and then parse it ;
In fact, we can change Digester's parsing process, pre-define the rule set when starting, and then reuse the pre-defined rules when running;
This may be a bit vague, You can take a look at the following Web application scenario, and you should have a deeper understanding;
Anyone who is familiar with web development should know servlet, so I won’t go into details here. Now, suppose there is an EmployeeServlet, as shown below:
Since the servlet is a singleton, and the Digester is not thread-safe, we will issue new every time it is requested. A Dgester object to ensure thread safety, written as follows:
public class EmployeeServlet extends HttpServlet {public void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException { Digester digester = new Digester(); digester.setNamespaceAware( true ); digester.setXIncludeAware( true ); digester.addObjectCreate( "employee", Employee.class ); digester.addCallMethod( "employee/firstName", "setFirstName", 0 ); digester.addCallMethod( "employee/lastName", "setLastName", 0 ); digester.addObjectCreate( "employee/address", Address.class ); digester.addCallMethod( "employee/address/type", "setType", 0 ); digester.addCallMethod( "employee/address/city", "setCity", 0 ); digester.addCallMethod( "employee/address/state", "setState", 0 ); digester.addSetNext( "employee/address", "addAddress" ); Employee employee = digester.parse( openStream( req.getParameter( "employeeId" ) ) ); ... }
We can easily find the shortcomings of the above program: the code is not reused, and each request Rules need to be bound repeatedly;
However, we can use RuleSet to solve the problem of no code reuse, as shown below, define a EmployeeRuleSet rule set implementation RuleSet Interface:
public class EmployeeRuleSet implements RuleSet {public void addRuleInstances( Digester digester ) { digester.addObjectCreate( "employee", Employee.class ); digester.addCallMethod( "employee/firstName", "setFirstName", 0 ); digester.addCallMethod( "employee/lastName", "setLastName", 0 ); digester.addObjectCreate( "employee/address", Address.class ); digester.addCallMethod( "employee/address/type", "setType", 0 ); digester.addCallMethod( "employee/address/city", "setCity", 0 ); digester.addCallMethod( "employee/address/state", "setState", 0 ); digester.addSetNext( "employee/address", "addAddress" ); } }
Then use it like this in the servlet:
public class EmployeeServlet extends HttpServlet {private final RuleSet employeeRuleSet = new EmployeeRuleSet();public void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException { Digester digester = new Digester(); digester.setNamespaceAware( true ); digester.setXIncludeAware( true ); employeeRuleSet.addRuleInstances( digester ); Employee employee = digester.parse( openStream( req.getParameter( "employeeId" ) ) ); ... } }
Obviously there is nothing wrong with doing this (in fact, personally I think it is better to write a private method directly and add rules, haha), but it has the following disadvantages:
RuleSet is not actually a configuration, it just binds rules to digester ;
##digesterThe object is highly coupled with the client and is created directly by the client;
Before each parsing call, you need to repeat the binding rules
When the rules are bound, the semantics are very poor. The readability is not good;
class EmployeeModuleextends AbstractRulesModule { @Overrideprotected void configure() { forPattern( "employee" ).createObject().ofType( Employee.class ); forPattern( "employee/firstName" ).setBeanProperty(); forPattern( "employee/lastName" ).setBeanProperty(); forPattern( "employee/address" ).createObject().ofType( Address.class ).then().setNext( "addAddress"); forPattern( "employee/address/type" ).setBeanProperty(); forPattern( "employee/address/city" ).setBeanProperty(); forPattern( "employee/address/state" ).setBeanProperty(); } }Then use it in the servlet like this:
public class EmployeeServletextends HttpServlet {private final DigesterLoader loader = newLoader( new EmployeeModule() ) .setNamespaceAware( true ) .setXIncludeAware( true );public void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException { Digester digester = loader.newDigester() Employee employee = digester.parse( openStream( req.getParameter("employeeId") ) ); ... } }The benefits are obvious:
RulesModuleThe API for rule binding is very semantic, easy to use, and highly readable;
digester objects are not created by the client. Instead Created by DigesterLoader;
Write your own class to implement RulesModuleIn addition to the interface, digester itself provides a FromXmlRulesModule class, the RulesModule interface has been implemented, we can use like this: ##
DigesterLoader loader = DigesterLoader.newLoader( .getResource( "myrule.xml"
<employee> <firstName>Pi</firstName> <lastName>Chen</lastName> <address> <type>CITY</type> <city>HangZhou</city> <state>2</state> </address> </employee>
package apache.commons.digester3.example.rulesbinder.module;import org.apache.commons.digester3.binder.AbstractRulesModule;import apache.commons.digester3.example.rulesbinder.pojo.Address;import apache.commons.digester3.example.rulesbinder.pojo.Employee;/** * * * @author * @version 2017年6月5日 */public class EmployeeModule extends AbstractRulesModule { @Overrideprotected void configure() { forPattern("employee").createObject().ofType(Employee.class); forPattern("employee/firstName").setBeanProperty(); forPattern("employee/lastName").setBeanProperty(); forPattern("employee/address").createObject().ofType(Address.class).then().setNext("addAddress"); forPattern("employee/address/type").setBeanProperty(); forPattern("employee/address/city").setBeanProperty(); forPattern("employee/address/state").setBeanProperty(); } }
package apache.commons.digester3.example.rulesbinder;import java.io.IOException;import org.apache.commons.digester3.Digester;import org.apache.commons.digester3.binder.DigesterLoader;import org.xml.sax.SAXException;import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;import apache.commons.digester3.example.rulesbinder.pojo.Address;import apache.commons.digester3.example.rulesbinder.pojo.Employee;import apache.commons.digester3.example.simpletest.ExampleMain;/** * * * @author * @version 2017年6月5日 */public class DigesterLoaderMain {private static DigesterLoader dl = DigesterLoader.newLoader(new EmployeeModule()) .setNamespaceAware(false);public static void main(String[] args) {try { Digester digester = dl.newDigester(); Employee employee = digester.parse(ExampleMain.class.getClassLoader().getResourceAsStream("employee.xml")); System.out.print(employee.getFirstName() + " "); System.out.print(employee.getLastName() + ", ");for (Address a : employee.getAddressList()) { System.out.print(a.getType() + ", "); System.out.print(a.getCity() + ", "); System.out.println(a.getState()); } } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } }
Pi Chen, CITY, HangZhou, 2
digester object is not thread-safe. The following is a simple API usage example :Continue the previous example, use the same xml and RulesModule implementation class;
Client class:
package apache.commons.digester3.example.rulesbinder;import java.util.concurrent.ExecutionException;import java.util.concurrent.Executors;import java.util.concurrent.Future;import org.apache.commons.digester3.Digester;import org.apache.commons.digester3.binder.DigesterLoader;import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;import apache.commons.digester3.example.rulesbinder.pojo.Address;import apache.commons.digester3.example.rulesbinder.pojo.Employee;import apache.commons.digester3.example.simpletest.ExampleMain;/** * * @author * @version 2017年6月5日 */public class AsyncParseMain {private static DigesterLoader dl = DigesterLoader.newLoader(new EmployeeModule()) .setNamespaceAware(false).setExecutorService(Executors.newSingleThreadExecutor());public static void main(String[] args) {try { Digester digester = dl.newDigester(); Future<Employee> future = digester.asyncParse(ExampleMain.class.getClassLoader().getResourceAsStream("employee.xml")); Employee employee = future.get(); System.out.print(employee.getFirstName() + " "); System.out.print(employee.getLastName() + ", ");for (Address a : employee.getAddressList()) { System.out.print(a.getType() + ", "); System.out.print(a.getCity() + ", "); System.out.println(a.getState()); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
Assume there is an xml as shown below, (where ${type} is a variable ):
<employee> <firstName>Pi</firstName> <lastName>Chen</lastName> <address> <type>${type}</type> <city>HangZhou</city> <state>2</state> </address> </employee>
那么可以这样解析如上xml:
package apache.commons.digester3.example.rulesbinder;import java.io.IOException;import java.util.HashMap;import java.util.Map;import org.apache.commons.digester3.Digester;import org.apache.commons.digester3.Substitutor;import org.apache.commons.digester3.binder.DigesterLoader;import org.apache.commons.digester3.substitution.MultiVariableExpander;import org.apache.commons.digester3.substitution.VariableSubstitutor;import org.xml.sax.SAXException;import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;import apache.commons.digester3.example.rulesbinder.pojo.Address;import apache.commons.digester3.example.rulesbinder.pojo.Employee;import apache.commons.digester3.example.simpletest.ExampleMain;/** * * * @author * @version 2017年6月5日 */public class SubstitutionMain {private static DigesterLoader dl = DigesterLoader.newLoader(new EmployeeModule()) .setNamespaceAware(false);public static void main(String[] args) {try{// set up the variables the input xml can referenceMap<String, Object> vars = new HashMap<String, Object>(); vars.put("user.name", "me"); vars.put("type", "boss");// map ${varname} to the entries in the var mapMultiVariableExpander expander = new MultiVariableExpander(); expander.addSource("$", vars);// allow expansion in both xml attributes and element textSubstitutor substitutor = new VariableSubstitutor(expander); Digester digester = dl.newDigester(); digester.setSubstitutor(substitutor); Employee employee = digester .parse(ExampleMain.class.getClassLoader().getResourceAsStream("employee$.xml")); System.out.print(employee.getFirstName() + " "); System.out.print(employee.getLastName() + ", ");for (Address a : employee.getAddressList()) { System.out.print(a.getType() + ", "); System.out.print(a.getCity() + ", "); System.out.println(a.getState()); } }catch (IOException e) { e.printStackTrace(); }catch (SAXException e) { e.printStackTrace(); } } }
简单地说,就是在使用ObjectCreateRule规则的时候,能够传递xml中的值(属性值、body值)给构造方法使用;
如下是一个待解析的xml:
<root> <bean super="false"><rate>9.99</rate> </bean></root>
那么可以这样解析:
package apache.commons.digester3.example.rulesbinder;import java.io.IOException;import org.apache.commons.digester3.Digester;import org.apache.commons.digester3.ObjectCreateRule;import org.apache.commons.digester3.binder.DigesterLoader;import org.xml.sax.SAXException;import apache.commons.digester3.example.rulesbinder.module.EmployeeModule;import apache.commons.digester3.example.rulesbinder.pojo.Address;import apache.commons.digester3.example.rulesbinder.pojo.Employee;import apache.commons.digester3.example.rulesbinder.pojo.MyBean;import apache.commons.digester3.example.simpletest.ExampleMain;/** * * * @author * @version 2017年6月5日 */public class ConstructorParamsMain {public static void main(String[] args) {try{ ObjectCreateRule createRule = new ObjectCreateRule(MyBean.class); createRule.setConstructorArgumentTypes(Double.class, Boolean.class); Digester digester = new Digester(); digester.addRule("root/bean", createRule); digester.addCallParam("root/bean", 1, "super"); digester.addCallParam("root/bean/rate", 0); MyBean myBean = digester.parse(ConstructorParamsMain.class.getClassLoader() .getResourceAsStream("constructor-params.xml")); System.out.println(myBean.getRate()); System.out.println(myBean.isSuper_()); }catch (IOException e) { e.printStackTrace(); }catch (SAXException e) { e.printStackTrace(); } } }
结果打印:
The above is the detailed content of Apache Commons Digester. For more information, please follow other related articles on the PHP Chinese website!