1. 本文组合了Lamda和响应式,设计出一个解析xml的自定义组件,目标是练习新型 API的设计
2. 案例如下:
(1)book.xml
<books>
<book id="b01">
<name>john</name>
<price>23.45</price>
</book>
<book id="b02">
<name>tom</name>
<price>98</price>
</book>
</books>
(2) 组件及测试代码:
package cn.johnyu.sax;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
class Book {
private String id;
private String name;
private double price;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
/**
* 这是设计出的核心API,使用了"Lamda+响应式api"
*/
class MyXmlBooksHandler extends DefaultHandler {
private List<Book> books = null;
private Book book = null;
private String info;
private Consumer<List<Book>> bookListConsumer;
private Consumer<Book> bookConsumer;
/**
*
* @param bookConsumer :每本图书的回调接口
* @param bookListConsumer: 所有图书的回调接口
*/
public MyXmlBooksHandler(Consumer<Book> bookConsumer, Consumer<List<Book>> bookListConsumer) {
this.bookListConsumer = bookListConsumer;
this.bookConsumer = bookConsumer;
}
/**
* 遇到文档开始,实例化图书,并设置id
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("books".equals(qName)) {
books = new ArrayList<>();
}
if ("book".equals(qName)) {
book = new Book();
String id = attributes.getValue("id");
book.setId(id);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String info = new String(ch, start, length).trim();
this.info = info.isEmpty() ? "" : info;
}
/**
* 遇到文档结束:
* (1)根据qname,使用info设置图书的各个属性
* (2)如果遇到的book的结尾,则根据情况要么"消费book"或者"加入到集合中"
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("name".equals((qName))) {
book.setName(info);
}
if ("price".equals(qName)) {
try {
double price = Double.parseDouble(info);
book.setPrice(price);
} catch (NumberFormatException e) {
throw new SAXException("价格数据解析失败");
}
}
if ("book".equals(qName)) {
//每本书进行一次消费
if (this.bookConsumer != null) {
bookConsumer.accept(book);
}
if (this.bookListConsumer != null) {
books.add(book);
}
}
}
//全部文档结尾的事件中,处理"全部图书的消费"
@Override
public void endDocument() throws SAXException {
if (bookListConsumer != null) {
bookListConsumer.accept(books);
}
}
}
public class MyApp {
public static void main(String[] args) throws Exception {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
File file = new File("books.xml");
//针对每本书进行消费
MyXmlBooksHandler handler = new MyXmlBooksHandler(System.out::println, null);
parser.parse(file, handler);
//针对所有的书进行消费
MyXmlBooksHandler handler1 = new MyXmlBooksHandler(null, System.out::println);
parser.parse(file, handler1);
}
}
网友评论