Home  >  Article  >  Java  >  Apache Commons Digester

Apache Commons Digester

巴扎黑
巴扎黑Original
2017-06-26 09:17:071468browse

Preface

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:

  1. Rule module binding, complete the pre-binding of rules by defining a RulesModule interface implementation class, and reuse it at runtime

  2. Asynchronous parsing xml

  3. Parse variables in xml, such as ${sys.user}

  4. Use the constructor with parameters to create objects, and the parameters come from xml node data

Rule module pre-binding - RulesModule interface

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;

servlet scenario example

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:

  1. RuleSet is not actually a configuration, it just binds rules to digester ;

  2. ##digesterThe object is highly coupled with the client and is created directly by the client;

  3. Before each parsing call, you need to repeat the binding rules

  4. When the rules are bound, the semantics are very poor. The readability is not good;

#So, what is the best practice? The answer is to use the RulesModule interface to help us pre-bind rules when starting, and then when running, Just reuse the pre-bound rules, as shown below:

Define a RulesModule interface implementation class:

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:

  1. RulesModuleThe API for rule binding is very semantic, easy to use, and highly readable;

  2. The configuration of rule binding is moved to the startup phase to complete;

  3. digester objects are not created by the client. Instead Created by DigesterLoader;

FromXmlRulesModule

except

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"
Complete example

Suppose there is an xml as follows, to be parsed

<employee>
    <firstName>Pi</firstName>
    <lastName>Chen</lastName>
    <address>
        <type>CITY</type>
        <city>HangZhou</city>
        <state>2</state>
    </address>
</employee>
Start coding. First, define a RulesModule interface implementation class:

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();
    }

}
Write client class:

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();
        }
    }
}
Result printing:

Pi Chen, CITY, HangZhou, 2

Asynchronous parsing XML

For asynchronous parsing, you can directly call the asyncParse method, but special attention is required because the

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();
        }
    }
}
xml variable parsing-Substitutor abstraction Class

This is relatively simple. Define a VariableSubstitutor implementation class, and the user converts attributes and variable values ​​defined in the body;

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();
        }
    }
}

结果打印:

9.99
false

参考资料


代码参考


The above is the detailed content of Apache Commons Digester. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn