SAX或XML的简单API已经存在了很多年,据我所知,它最初是在千年之交之前由David Megginson领导的开发。 在那些日子里,您必须从David的个人网站下载Java版本的SAX。 在最终添加到Java Standard Edition 1.4中之前,它已发展为SAX项目 。
SAX是XML的流接口,这意味着使用SAX的应用程序从文档的顶部开始,以顺序的时间接收到有关正在处理的XML文档的事件通知的元素和属性,并以关闭文档的结尾结束。根元素。 这意味着它在线性时间内处理XML的效率非常高,而对系统内存没有太多要求。
回到Pete ,您将努力工作,并提出以下基于SAX解析器的类:
public class PizzaParser {
public List<PizzaOrder> order(InputStream xml) {
PizzaContentHandler handler = new PizzaContentHandler();
// do the parsing
try {
// Construct the parser by bolting together an XMLReader
// and the ContentHandler
XMLReader parser = XMLReaderFactory.createXMLReader();
parser.setContentHandler(handler);
// create an input source from the XML input stream
InputSource source = new InputSource(xml);
// Do the actual work
parser.parse(source);
return handler.getPizzaOrder();
} catch (Exception ex) {
throw new RuntimeException('Exception parsing xml message. Message: ' + ex.getMessage(), ex);
}
}
static class PizzaOrder {
private final String pizzaName;
private final String base;
private final String quantity;
PizzaOrder(String pizzaName, String base, String quantity) {
this.pizzaName = pizzaName;
this.base = base;
this.quantity = quantity;
}
public String getPizzaName() {
return pizzaName;
}
public String getBase() {
return base;
}
public String getQuantity() {
return quantity;
}
}
/**
* Use this class the handle the SAX events
*/
class PizzaContentHandler extends DefaultHandler {
private String[] pizzaInfo;
private int index;
private List<PizzaOrder> outList;
private boolean capture;
/**
* Set things up at the start of the document.
*/
@Override
public void startDocument() {
outList = new ArrayList<PizzaOrder>();
}
/**
* Handle the startElement event
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
capture = true;
if ('pizzas'.equals(qName)) {
capture = false;
} else if ('pizza'.equals(qName)) {
pizzaInfo = new String[3];
capture = false;
} else if ('name'.equals(qName)) {
index = 0;
} else if ('base'.equals(qName)) {
index = 1;
} else if ('quantity'.equals(qName)) {
index = 2;
}
}
/**
* Handle the endElement event
*/
@Override
public void endElement(String uri, String localName, String qName) {
if ('pizza'.equals(qName)) {
outList.add(new PizzaOrder(pizzaInfo[0], pizzaInfo[1], pizzaInfo[2]));
}
}
/**
* Grab hold of incoming character data
*/
@Override
public void characters(char[] ch, int start, int length) {
if (capture) {
pizzaInfo[index] = new String(ch, start, length);
capture = false;
}
}
List<PizzaOrder> getPizzaOrder() {
return outList;
}
}
}
该博客不是在这里演示如何使用SAX,如果您看看周围有很多可用的示例,但是让我们仔细看一下代码,首先要注意的是order(...)方法现在将输入流而不是字符串作为适合基于流的API:
public List<PizzaOrder> order(InputStream xml)
接下来要注意的是, PizzaParser使用嵌套类PizzaContentHandler ,该类扩展了SAX帮助程序类DefaultHandler 。 PizzaContentHandler类将捕获PizzaOrder bean的列表,并将它们传递回封闭类,以返回给调用者。 这意味着要掌握SAX事件,您需要做的就是重写处理程序方法,例如startElement(...) , endElement(...)等。
如果仔细看一下代码,您会发现它非常复杂。 它要做的就是创建一个输出列表,但是有多个if()语句,临时数组和布尔开关用于从文档的正确位置获取正确的信息。 这是SAX的缺点:它的复杂性给程序员带来了更多负担,并使您的代码更容易出错。
但是,它比以前的基于字符串的尝试更具弹性,如下面的单元测试所示:
public class PizzaParserTest {
private static final String ORDER_XML = //
'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n' + //
'<pizza>\n' + // 8
' <name>Capricciosa</name>\n' + //
' <base>thin</base>\n' + //
' <quantity>2</quantity>\n' + //
'</pizza>\n';
private static final String ORDER_XML_2 = //
'<?xml version=\'1.0\' encoding=\'UTF-8\'?><pizza><name>Capricciosa</name><base>thin</base><quantity>2</quantity></pizza>';
private static final String ORDER_XML_3 = //
'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n' + //
'<pizzas>\n' + //
' <pizza>\n' + //
' <name>Capricciosa</name>\n' + //
' <base>thin</base>\n' + //
' <quantity>2</quantity>\n' + //
' </pizza>\n' + //
' <pizza>\n' + //
' <name>Margherita</name>\n' + //
' <base>thin</base>\n' + //
' <quantity>1</quantity>\n' + //
' </pizza>\n' + //
'</pizzas>';
private PizzaParser instance;
@Before
public void setUp() {
instance = new PizzaParser();
}
@Test
public void readOrderFromXML() {
List<PizzaOrder> results = instance.order(new ByteArrayInputStream(ORDER_XML.getBytes()));
assertEquals(1, results.size());
PizzaOrder result = results.get(0);
assertEquals('Capricciosa', result.getPizzaName());
assertEquals('thin', result.getBase());
assertEquals('2', result.getQuantity());
}
@Test
public void readOrderFromModifiedXML() {
List<PizzaOrder> results = instance.order(new ByteArrayInputStream(ORDER_XML_2.getBytes()));
assertEquals(1, results.size());
PizzaOrder result = results.get(0);
assertEquals('Capricciosa', result.getPizzaName());
assertEquals('thin', result.getBase());
assertEquals('2', result.getQuantity());
}
@Test
public void readOrderForMultiplePizza() {
List<PizzaOrder> results = instance.order(new ByteArrayInputStream(ORDER_XML_3.getBytes()));
PizzaOrder result = results.get(0);
assertEquals('Capricciosa', result.getPizzaName());
assertEquals('thin', result.getBase());
assertEquals('2', result.getQuantity());
result = results.get(1);
assertEquals('Margherita', result.getPizzaName());
assertEquals('thin', result.getBase());
assertEquals('1', result.getQuantity());
}
}
这些测试演示了处理带有和不带有空格(解决昨天的问题)的XML消息以及包含多个比萨饼订单的消息的场景。
一切都很好,但是Pete的宏伟构想正在实现。 现在,他通过遍布世界各地的多个厨房和在线业务,已扩展到全球范围。 皮特(Pete)聘请了一些时髦的业务顾问,他们创建了一个新的比萨订单XML模式并将其与现有客户模式结合起来。 这已放入您的电子邮件收件箱,您想知道下一步该怎么做...
1其他搜索引擎可用。
可从GitHub上获得源代码:
git://github.com/roghughe/captaindebug.git
继续阅读本系列的第3部分 。
参考: XML的方法–第2部分– SAX呢? 来自我们的JCG合作伙伴 Roger Hughes,来自Captain Debug的Blog博客。
翻译自: https://www.javacodegeeks.com/2012/07/approaches-to-xml-part-2-what-about-sax.html