使用StAX为XML创建索引以便快速访问
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用StAX为XML创建索引以便快速访问相关的知识,希望对你有一定的参考价值。
有没有办法使用StAX和JAX-B创建索引,然后快速访问XML文件?
我有一个大型XML文件,我需要在其中查找信息。这用于桌面应用程序,因此它应该在RAM很少的系统上运行。
所以我的想法是:创建一个索引然后快速访问大文件中的数据。
我不能只拆分文件,因为它是一个我希望不加改变地使用的官方联邦数据库。
使用XMLStreamReader我可以快速找到一些元素,然后使用JAXB来解组元素。
final XMLStreamReader r = xf.createXMLStreamReader(filename, new FileInputStream(filename));
final JAXBContext ucontext = JAXBContext.newInstance(Foo.class);
final Unmarshaller unmarshaller = ucontext.createUnmarshaller();
r.nextTag();
while (r.hasNext()) {
final int eventType = r.next();
if (eventType == XMLStreamConstants.START_ELEMENT && r.getLocalName().equals("foo")
&& Long.parseLong(r.getAttributeValue(null, "bla")) == bla
) {
// JAX-B works just fine:
final JAXBElement<Foo> foo = unmarshaller.unmarshal(r,Foo.class);
System.out.println(foo.getValue().getName());
// But how do I get the offset?
// cache.put(r.getAttributeValue(null, "id"), r.getCursor()); // ???
break;
}
}
但我无法得到抵消。我想用它来准备索引:
(id of element) -> (offset in file)
然后我应该能够使用偏移从那里取消编组:打开文件流,跳过那么多字节,取消编组。我找不到这样做的图书馆。如果不知道文件光标的位置,我无法自己完成。 javadoc明确指出有一个光标,但我找不到访问它的方法。
编辑: 我只是想提供一种可以在旧硬件上运行的解决方案,以便人们可以实际使用它。不是每个人都能买得起新的强大电脑。使用StAX我可以在大约2秒内获得数据,这有点长。但它不需要RAM。它需要300 MB的RAM才能使用JAX-B。对于这样一个简单的任务,使用一些嵌入式数据库系统只需要很多开销。无论如何我会使用JAX-B。由于wsimport生成的类已经很完美,因此对我来说其他任何东西都是无用的。当我只需要一些时,我只是不想加载300 MB的对象。
我找不到一个只需要XSD来创建内存数据库的数据库,它不会使用那么多的RAM。它全部用于服务器,或者需要定义模式并映射XML。所以我认为它不存在。
您可以使用ANTLR4处理生成的XML解析器。
以下在~17GB Wikipedia dump /20170501/dewiki-20170501-pages-articles-multistream.xml.bz2
上工作得很好,但我不得不使用-xX6GB
增加堆大小。
1. Get XML Grammar
cd /tmp
git clone https://github.com/antlr/grammars-v4
2. Generate Parser
cd /tmp/grammars-v4/xml/
mvn clean install
3. Copy Generated Java files to your Project
cp -r target/generated-sources/antlr4 /path/to/your/project/gen
4. Hook in with a Listener to collect character offsets
package stack43366566;
import java.util.ArrayList;
import java.util.List;
import org.antlr.v4.runtime.ANTLRFileStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import stack43366566.gen.XMLLexer;
import stack43366566.gen.XMLParser;
import stack43366566.gen.XMLParser.DocumentContext;
import stack43366566.gen.XMLParserBaseListener;
public class FindXmlOffset {
List<Integer> offsets = null;
String searchForElement = null;
public class MyXMLListener extends XMLParserBaseListener {
public void enterElement(XMLParser.ElementContext ctx) {
String name = ctx.Name().get(0).getText();
if (searchForElement.equals(name)) {
offsets.add(ctx.start.getStartIndex());
}
}
}
public List<Integer> createOffsets(String file, String elementName) {
searchForElement = elementName;
offsets = new ArrayList<>();
try {
XMLLexer lexer = new XMLLexer(new ANTLRFileStream(file));
CommonTokenStream tokens = new CommonTokenStream(lexer);
XMLParser parser = new XMLParser(tokens);
DocumentContext ctx = parser.document();
ParseTreeWalker walker = new ParseTreeWalker();
MyXMLListener listener = new MyXMLListener();
walker.walk(listener, ctx);
return offsets;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] arg) {
System.out.println("Search for offsets.");
List<Integer> offsets = new FindXmlOffset().createOffsets("/tmp/dewiki-20170501-pages-articles-multistream.xml",
"page");
System.out.println("Offsets: " + offsets);
}
}
5. Result
打印:
抵消:[2441,10854,30257,51419 ....
6. Read from Offset Position
为了测试代码,我编写了在每个维基百科页面中读取一个java对象的类
@JacksonXmlRootElement
class Page {
public Page(){};
public String title;
}
基本上使用此代码
private Page readPage(Integer offset, String filename) {
try (Reader in = new FileReader(filename)) {
in.skip(offset);
ObjectMapper mapper = new XmlMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Page object = mapper.readValue(in, Page.class);
return object;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
找到完整的example on github。
以上是关于使用StAX为XML创建索引以便快速访问的主要内容,如果未能解决你的问题,请参考以下文章
Android上的Spring OAuth2异常:在路径上找不到类“javax.xml.transform.stax.StAXSource”:DexPathList