前面我们简单地介绍了如何使用Apache Commons DigesterXML进行解析。Digester是对SAX的封装,用来解决SAX解析XML的一些不足之处。在使用时,我们只需要定义一系列的解析规则,然后便可以创建出对应的对象并进行关联。那么这是怎么实现的呢?

下面,我们通过对一个简单的XML文件进行解析,一步一步的绘制出我们的XML解析器框架。

首先,我们知道SAX是事件驱动的,在使用SAX进行解析时,我们需要专门定义一个事件处理器继承自DefaultHandler,当解析到元素时会触发事件处理器的startElement方法:

1
2
3
4
5
6
public void startElement(String uri, String localName,
String qName, Attributes attributes)
throws SAXException
{
// no op
}

可以看到元素的名称及属性等会作为方法参数传入,这个时候我们通常需要根据不同的元素名称执行不同的逻辑。比如解析到<Foo>我们想创建Foo对象,解析到<Bar>我们想创建Bar对象等等。

最简单的方式,我们用if-else,代码大概是下面这样:

1
2
3
4
5
6
7
8
9
10
11
public void startElement(String uri,
String localName,
String qName,
Attributes attributes) throws SAXException {
if ("Foo".equals(qName)) {
Foo foo = new Foo();
} else if ("Bar".equals(qName)) {
Bar bar = new Bar();
}
...
}

但实际情况基本不会这么简单,对象之间大多都会有关联关系,比如一个Foo会包含多个Bar(就好比一个班级包含多个学生),假设要解析的XML是下面这样:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<Foo>
<Bar name="bar1">
</Bar>
<Bar name="bar2">
</Bar>
<Bar name="bar3">
</Bar>
</Foo>

对应的类定义大致如下:

1
2
3
4
5
6
7
8
9
10
11
public class Foo {
List<Bar> barList = new ArrayList<>();

public void addBar(Bar bar) {
barList.add(bar);
}
}

public class Bar {
String name;
}

由于SAX是逐行进行解析的,如果按照上面的if-else方式,当我们解析到第一行<Foo>时会创建一个Foo对象,解析到第二行<Bar name="bar1">时会创建一个Bar对象,但是Bar对象怎么和Foo对象关联上呢?也就是说怎么调用Foo对象的addBar方法呢?事实上,当解析到第二行创建Bar对象时已经获取不到解析第一行时创建的Foo对象的引用了,我们无法将其关联。

很明显,我们需要有一个地方来保存已经创建过的对象引用,那用什么数据类型来保存呢?我们注意到当解析到结束元素</Bar></Foo>的时候,与之对应的BarFoo对象就不会再被使用了。根据XML格式的特点,先定义的<Foo>标签后结束</Foo>,后定义的<Bar>标签先结束</Bar>,我们很容易联想到应该使用“先进后出”的栈结构来保存对象引用。

根据XML的结构,当我们解析到<Foo>元素时,直接创建Foo对象并压入栈中;当我们解析到<Bar>元素时,先创建好Bar对象,然后获取栈顶元素Foo的对象引用并将Bar对象进行关联,最后将Bar对象压入栈中。而当我们解析到任意结束元素的时候,只需弹出栈顶即可。核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private final Stack<Object> stack = new Stack<>();

public void startElement(String uri,
String localName,
String qName,
Attributes attributes) throws SAXException {
if ("Foo".equals(qName)) {
Foo foo = new Foo();
stack.push(foo);
LOGGER.info("stack push foo:{}", foo);
} else if ("Bar".equals(qName)) {
Bar bar = new Bar();
for (int i = 0; i < attributes.getLength(); i++) {
if ("name".equals(attributes.getQName(i))) {
bar.setName(attributes.getValue(i));
}
}
Foo foo = (Foo) stack.get(0);
foo.addBar(bar);
stack.push(bar);
LOGGER.info("stack push bar:{}", bar);
}
}

public void endElement(String uri, String localName, String qName) throws SAXException {
if ("Foo".equals(qName)) {
Foo pop = (Foo) stack.pop();
LOGGER.info("endElement, stack pop Foo:{}", pop);
} else if ("Bar".equals(qName)) {
Bar pop = (Bar) stack.pop();
LOGGER.info("endElement, stack pop Bar:{}", pop);
}
}

以上方法已经能够解决问题,但if-else的写法不够优雅,或者说不够通用,我们需要根据每一个XML的文档结构去实现定制化的逻辑。

下面我们用策略+工厂模式来进行优化。

我们注意到在startElementendElement方法中,对元素名称qName进行了相同模式的equals判断。据此,我们可以抽象出我们的策略接口(或者称为元素处理器接口)Processor

1
2
3
4
5
public interface Processor {
default void processStart(String qName, Attributes attributes) throws Exception {};
default void processEnd(String qName) throws Exception {};
void setMyDigester(MyDigester myDigester);
}

由于我们需要对MyDigester类中的栈结构进行操作,所以我们需要在MyDigester类中提供对成员变量stack的相关操作方法,同时让每一个策略接口的实现类持有一个MyDigester类的引用。据此,我们可以提供出一个抽象的策略实现类AbstractBaseProcessor,相关主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MyDigester extends DefaultHandler {
private final Stack<Object> stack = new Stack<>();

public <T> void push(T obj) {
stack.push(obj);
}

@SuppressWarnings("unchecked")
public <T> T pop() {
return (T) stack.pop();
}

@SuppressWarnings("unchecked")
public <T> T get(int index) {
return (T) stack.get(index);
}

@SuppressWarnings("unchecked")
public <T> T peek() {
return (T) stack.peek();
}

/**
* peek指定位置的栈元素
* @param n 0是栈顶元素,1是栈顶的前一个元素...
* @param <T> 栈中元素类型
* @return 位于n位置的栈中元素
*/
@SuppressWarnings("unchecked")
public <T> T peek(int n) {
int index = (stack.size() - 1) - n;
return (T) stack.get(index);
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/22
*/
public abstract class AbstractBaseProcessor implements Processor {
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractBaseProcessor.class);
private MyDigester myDigester;

public MyDigester getMyDigester() {
return myDigester;
}

@Override
public void setMyDigester(MyDigester myDigester) {
this.myDigester = myDigester;
}
}

下面我们来进行具体的策略实现。对于Foo标签,策略实现类FooProcessor代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import org.xml.sax.Attributes;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/22
*/
public class FooProcessor extends AbstractBaseProcessor {
@Override
public void processStart(String qName, Attributes attributes) throws Exception {
Foo foo = new Foo();
getMyDigester().push(foo);
LOGGER.info("FooProcessor#processStart stack push foo:{}", foo);
}

@Override
public void processEnd(String qName) throws Exception {
Foo pop = getMyDigester().pop();
LOGGER.info("FooProcessor#processEnd endElement, stack pop Foo:{}", pop);
}
}

对于Bar标签,策略实现类BarProcessor代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import org.xml.sax.Attributes;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/22
*/
public class BarProcessor extends AbstractBaseProcessor {
@Override
public void processStart(String qName, Attributes attributes) {
Bar bar = new Bar();
for (int i = 0; i < attributes.getLength(); i++) {
if ("name".equals(attributes.getQName(i))) {
bar.setName(attributes.getValue(i));
}
}
Foo foo = getMyDigester().get(0);
foo.addBar(bar);
getMyDigester().push(bar);
LOGGER.info("BarProcessor#processStart stack push bar:{}", bar);
}

@Override
public void processEnd(String qName) {
Bar pop = getMyDigester().pop();
LOGGER.info("BarProcessor#processStart endElement, stack pop Bar:{}", pop);
}
}

接下来,我们定义策略工厂ProcessorFactory,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import com.google.common.collect.Maps;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/22
*/
public class ProcessorFactory {
private final Map<String, List<Processor>> importHfMap = Maps.newHashMap();

public void addProcessor(String qName, Processor processor) {
List<Processor> processorList = matchProcessor(qName);
if (processorList == null || processorList.size() == 0) {
processorList = new ArrayList<>();
}
processorList.add(processor);
importHfMap.put(qName, processorList);
}

public void addProcessorList(String qName, List<Processor> processorList) {
List<Processor> mpl = matchProcessor(qName);
if (mpl == null || mpl.size() == 0) {
mpl = new ArrayList<>();
}
mpl.addAll(processorList);
importHfMap.put(qName, mpl);
}

public List<Processor> matchProcessor(String qName) {
return importHfMap.get(qName);
}

public static ProcessorFactory newInstance() {
return new ProcessorFactory();
}
}

将不同的qName和对应的元素处理器Processor进行一次映射,在使用时我们只需要根据qName从工厂中获取对应的元素处理器集合,然后执行即可。核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MyDigester extends DefaultHandler {
...
private ProcessorFactory processorFactory = null;

public ProcessorFactory getProcessorFactory() {
return processorFactory;
}

public void setProcessorFactory(ProcessorFactory processorFactory) {
this.processorFactory = processorFactory;
}

...

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
List<Processor> processorList = processorFactory.matchProcessor(qName);
for (Processor processor : processorList) {
try {
processor.processStart(qName, attributes);
} catch (Exception e) {
e.printStackTrace();
}
}
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
List<Processor> processorList = processorFactory.matchProcessor(qName);
for (Processor processor : processorList) {
try {
processor.processEnd(qName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
...
}

一切准备就绪,我们可以进行一个简单的测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/19
*/
public class MyDigesterParseTest {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
InputStream is = MyDigesterParseTest.class.getResourceAsStream("/xml/FooBar.xml");
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();

MyDigester myDigester = new MyDigester();

// 策略工厂
ProcessorFactory pf = ProcessorFactory.newInstance();

// Foo策略
FooProcessor fooProcessor = new FooProcessor();
fooProcessor.setMyDigester(myDigester);
pf.addProcessor("Foo", fooProcessor);

// Bar策略
BarProcessor barProcessor = new BarProcessor();
barProcessor.setMyDigester(myDigester);
pf.addProcessor("Bar", barProcessor);

// 设置策略工厂
myDigester.setProcessorFactory(pf);

// 进行解析
saxParser.parse(is, myDigester);
}
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
01:12:17.890 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.MyDigester - MyDigester.startDocument
01:12:17.896 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - FooProcessor#processStart stack push foo:Foo{barList=[]}
01:12:17.898 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - BarProcessor#processStart stack push bar:Bar{name='bar1'}
01:12:17.898 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - BarProcessor#processEnd endElement, stack pop Bar:Bar{name='bar1'}
01:12:17.898 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - BarProcessor#processStart stack push bar:Bar{name='bar2'}
01:12:17.898 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - BarProcessor#processEnd endElement, stack pop Bar:Bar{name='bar2'}
01:12:17.898 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - BarProcessor#processStart stack push bar:Bar{name='bar3'}
01:12:17.898 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - BarProcessor#processEnd endElement, stack pop Bar:Bar{name='bar3'}
01:12:17.898 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - FooProcessor#processEnd endElement, stack pop Foo:Foo{barList=[Bar{name='bar1'}, Bar{name='bar2'}, Bar{name='bar3'}]}
01:12:17.899 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.MyDigester - MyDigester.endDocument

可以看到,一切运作正常。但好像还是有那么一点不完美,对于不同的XML文件,我们还是需要去写定制化的Processor实现类,同时我们注意到在BarProcessor中,对于Bar对象的属性,我们是通过遍历attributes参数进行设置的,另外,addBar方法也是我们主动调用的。怎么实现更加通用的Processor呢?

想要变得通用,我们就不该去关注具体的XML元素FooBar,而是应该关注我们要做什么。我们要创建对象,要给对象的属性赋值,要调用对象的某个方法。而这些,在Java语言中都可以通过反射来实现。于是我们的Processor实现类应该是:对象创建实现类ObjectCreateProcessor、属性赋值实现类SetPropertiesProcessor和方法调用实现类InvokeMethodProcessor。下面我们来依次进行实现:

对象创建实现类ObjectCreateProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import org.xml.sax.Attributes;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/22
*/
public class ObjectCreateProcessor extends AbstractBaseProcessor {
private final String className;

public ObjectCreateProcessor(String className) {
this.className = className;
}

@Override
public void processStart(String qName, Attributes attributes) throws Exception {
Object instance = this.getMyDigester().getClass().getClassLoader().loadClass(className).newInstance();
getMyDigester().push(instance);
LOGGER.info("ObjectCreateProcessor#processStart stack push:{}", instance);
}

@Override
public void processEnd(String qName) throws Exception {
Object pop = getMyDigester().pop();
LOGGER.info("ObjectCreateProcessor#processEnd stack pop:{}", pop);
}
}

属性赋值实现类SetPropertiesProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import com.google.common.collect.Maps;
import org.apache.commons.beanutils.BeanUtils;
import org.xml.sax.Attributes;

import java.util.Map;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/22
*/
public class SetPropertiesProcessor extends AbstractBaseProcessor {
@Override
public void processStart(String qName, Attributes attributes) throws Exception {
Map<String, String> properties = Maps.newHashMap();
for (int i = 0; i < attributes.getLength(); i++) {
properties.put(attributes.getQName(i), attributes.getValue(i));
}
Object top = getMyDigester().peek();
LOGGER.info("SetPropertiesProcessor#processStart stack peek:{}", top);
BeanUtils.populate(top, properties);
}
}

方法调用实现类InvokeMethodProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.sunchaser.sparrow.javase.base.xml.mydigester;

import org.apache.commons.beanutils.MethodUtils;
import org.xml.sax.Attributes;

/**
* @author sunchaser admin@lilu.org.cn
* @since JDK8 2021/8/22
*/
public class InvokeMethodProcessor extends AbstractBaseProcessor {
private final String methodName;
private final String paramType;

public InvokeMethodProcessor(String methodName, String paramType) {
this.methodName = methodName;
this.paramType = paramType;
}

@Override
public void processStart(String qName, Attributes attributes) throws Exception {
Class<?>[] paramTypes = new Class<?>[1];
paramTypes[0] = getMyDigester().getClass().getClassLoader().loadClass(paramType);
Object arg = getMyDigester().peek(0);
Object obj = getMyDigester().peek(1);
LOGGER.info("InvokeMethodProcessor#processStart invoke obj:{} method:{} with args:{}", obj, methodName, arg);
MethodUtils.invokeMethod(obj, methodName, new Object[]{ arg }, paramTypes);
}
}

接下来我们就可以进行简单的测试了,在这之前,为了避免对每一个Processor实现类都调用setMyDigester方法,我们可以在MyDigester中封装一下对Processor实现类的添加方法,核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
public void addProcessor(String qName, Processor processor) {
processor.setMyDigester(this);
getProcessorFactory().addProcessor(qName, processor);
}

public void addObjectCreate(String qName, String className) {
addProcessor(qName, new ObjectCreateProcessor(className));
}

public void addSetProperties(String qName) {
addProcessor(qName, new SetPropertiesProcessor());
}

public void addInvokeMethod(String qName, String methodName, String paramType) {
addProcessor(qName, new InvokeMethodProcessor(methodName, paramType));
}
...

接下来我们就可以进行测试了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
InputStream is = MyDigesterParseTest.class.getResourceAsStream("/xml/FooBar.xml");
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();

MyDigester myDigester = new MyDigester();

// 策略工厂
ProcessorFactory pf = ProcessorFactory.newInstance();

// 设置策略工厂
myDigester.setProcessorFactory(pf);

// 指定策略
myDigester.addObjectCreate("Foo", "com.sunchaser.sparrow.javase.base.xml.mydigester.Foo");
myDigester.addObjectCreate("Bar", "com.sunchaser.sparrow.javase.base.xml.mydigester.Bar");
myDigester.addSetProperties("Bar");
myDigester.addInvokeMethod("Bar", "addBar", "com.sunchaser.sparrow.javase.base.xml.mydigester.Bar");

// 进行解析
saxParser.parse(is, myDigester);
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
01:13:10.167 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.MyDigester - MyDigester.startDocument
01:13:10.172 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processStart stack push:Foo{barList=[]}
01:13:10.175 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processStart stack push:Bar{name='null'}
01:13:10.175 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - SetPropertiesProcessor#processStart stack peek:Bar{name='null'}
......
01:13:10.223 [main] DEBUG org.apache.commons.beanutils.BeanUtils - BeanUtils.populate(Bar{name='null'}, {name=bar1})
01:13:10.230 [main] DEBUG org.apache.commons.beanutils.ConvertUtils - Convert string 'bar1' to class 'java.lang.String'
01:13:10.230 [main] DEBUG org.apache.commons.beanutils.converters.StringConverter - Converting 'String' value 'bar1' to type 'String'
01:13:10.231 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - InvokeMethodProcessor#processStart invoke obj:Foo{barList=[]} method:addBar with args:Bar{name='bar1'}
01:13:10.232 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processEnd stack pop:Bar{name='bar1'}
01:13:10.232 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processStart stack push:Bar{name='null'}
01:13:10.232 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - SetPropertiesProcessor#processStart stack peek:Bar{name='null'}
01:13:10.232 [main] DEBUG org.apache.commons.beanutils.BeanUtils - BeanUtils.populate(Bar{name='null'}, {name=bar2})
01:13:10.232 [main] DEBUG org.apache.commons.beanutils.ConvertUtils - Convert string 'bar2' to class 'java.lang.String'
01:13:10.232 [main] DEBUG org.apache.commons.beanutils.converters.StringConverter - Converting 'String' value 'bar2' to type 'String'
01:13:10.232 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - InvokeMethodProcessor#processStart invoke obj:Foo{barList=[Bar{name='bar1'}]} method:addBar with args:Bar{name='bar2'}
01:13:10.233 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processEnd stack pop:Bar{name='bar2'}
01:13:10.233 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processStart stack push:Bar{name='null'}
01:13:10.233 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - SetPropertiesProcessor#processStart stack peek:Bar{name='null'}
01:13:10.233 [main] DEBUG org.apache.commons.beanutils.BeanUtils - BeanUtils.populate(Bar{name='null'}, {name=bar3})
01:13:10.233 [main] DEBUG org.apache.commons.beanutils.ConvertUtils - Convert string 'bar3' to class 'java.lang.String'
01:13:10.233 [main] DEBUG org.apache.commons.beanutils.converters.StringConverter - Converting 'String' value 'bar3' to type 'String'
01:13:10.233 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - InvokeMethodProcessor#processStart invoke obj:Foo{barList=[Bar{name='bar1'}, Bar{name='bar2'}]} method:addBar with args:Bar{name='bar3'}
01:13:10.233 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processEnd stack pop:Bar{name='bar3'}
01:13:10.233 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.AbstractBaseProcessor - ObjectCreateProcessor#processEnd stack pop:Foo{barList=[Bar{name='bar1'}, Bar{name='bar2'}, Bar{name='bar3'}]}
01:13:10.233 [main] INFO com.sunchaser.sparrow.javase.base.xml.mydigester.MyDigester - MyDigester.endDocument

至此,我们就实现了一个较为通用的XML解析器了,对于不同的XML文件,我们不再需要去写定制化的Processor实现类。

当我们要创建对象时,只需调用MyDigester#addObjectCreate方法,传入指定qName和要创建的对象的全限定类名即可;同样地,当我们要给对象设置属性时,我们只需调用MyDigester#addSetProperties方法指定qName即可;当我们要调用对象的指定方法时,只需调用MyDigester#addInvokeMethod方法传入指定qName方法名和方法参数类型即可。