树数据结构的实际应用
电子书目录读取
由来
在epub2规范生成的epub电子书中负责目录导航的文件toc.nxc文件,但是在epub3规范生成的电子书中负责目录导航的文件是toc.xhtml文件。公司的移动端的SDK并不支持toc.xhtml文件的电子书目录导航。我这边需要将toc.xhtml文件转换成toc.ncx文件。
工具准备
- 读取电子书文档的jar包 (epublib-core)
- 对html文档进行解析操作的jar (Jsoup)
导入maven依赖
<!-- https://mvnrepository.com/artifact/nl.siegmann.epublib/epublib-core -->
<dependency>
<groupId>nl.siegmann.epublib</groupId>
<artifactId>epublib-core</artifactId>
<version>3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
<dependency>
<groupId>net.sf.kxml</groupId>
<artifactId>kxml2</artifactId>
<version>2.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
</dependency>
代码思路
- 目录结构抽象成Jave中的数据模型。很明显是树数据结构
- 利用递归进行数据解析
代码实现
主要核心代码如下:
// 读取电子书
Book book = epubReader.readEpub(inputStream);
// 获取电子书目录
TableOfContents tableOfContents = book.getTableOfContents();
if (tableOfContents != null && tableOfContents.size() <=0 && !book.getNcxResource().getHref().contains("toc.ncx") ) {
String data = new String(book.getResources().getById("ncx").getData(), "UTF-8");
// 用Jsoup 生成文档格式
Document document = Jsoup.parse(data);
List<TOCReference> tocReferenceList = new ArrayList<>();
for (String resourceId : resourceIds) {
// 获取目录结构
Element element = document.select("nav#toc").select("a[href=" + resourceId + ".xhtml]").parents().first();
Elements elements = new Elements(element);
// 获取该章下得所有目录
List<TOCReference> referenceList = EbookCatalogUtil.getChapterCalog(elements, book.getResources().getById(resourceId));
tocReferenceList.add(referenceList.get(0));
}
// 给目录添加属性
tableOfContents.setTocReferences(tocReferenceList);
}
EbookCatalogUtil工具类:
/**
* @Author ouyangkang
* @Description 获取章节目录
* @Date 10:24 2018/11/2
* @param elements
* @return java.util.List<com.zhihuishu.ebook.util.EbookCatalogUtil.ChapterCatalog>
**/
public static List<TOCReference> getChapterCalog(Elements elements, Resource resource){
List<TOCReference> list = new LinkedList<>();
for (Element element : elements){
// 获取该章节ID
String resourceId = element.attributes().get("id");
// 获取该章节名字
String name = element.select("#"+resourceId+"> a").text();
// 获取孩子 element
Elements elementsTemp = element.select("#" + resourceId + " > ol > li");
TOCReference tocReference = new TOCReference();
tocReference.setTitle(name);
tocReference.setResource(resource);
tocReference.setFragmentId(UUID.randomUUID().toString());
//递归调用设置孩子节点
tocReference.setChildren(getChapterCalog(elementsTemp, resource));
list.add(tocReference);
}
return list;
}