Java 使用StAX为XML创建索引以便快速访问

Java 使用StAX为XML创建索引以便快速访问,java,xml,jaxb,stax,random-access,Java,Xml,Jaxb,Stax,Random Access,有没有一种方法可以使用StAX和JAX-B创建索引,然后快速访问XML文件 我有一个很大的XML文件,需要在其中查找信息。这是在桌面应用程序中使用的,因此它应该在RAM很少的系统上工作 所以我的想法是:创建一个索引,然后快速访问大文件中的数据 我不能只是分割文件,因为它是一个官方的联邦数据库,我想使用不变的 使用XMLStreamReader,我可以快速找到一些元素,然后使用JAXB对元素进行解组 final XMLStreamReader r=xf.createXMLStreamReader(

有没有一种方法可以使用StAX和JAX-B创建索引,然后快速访问XML文件

我有一个很大的XML文件,需要在其中查找信息。这是在桌面应用程序中使用的,因此它应该在RAM很少的系统上工作

所以我的想法是:创建一个索引,然后快速访问大文件中的数据

我不能只是分割文件,因为它是一个官方的联邦数据库,我想使用不变的

使用XMLStreamReader,我可以快速找到一些元素,然后使用JAXB对元素进行解组

final XMLStreamReader r=xf.createXMLStreamReader(文件名,新文件输入流(文件名));
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_元素和&r.getLocalName().equals(“foo”)
&&Long.parseLong(r.getAttributeValue(null,“bla”)==bla
) {
//JAX-B工作得很好:
final-JAXBElement-foo=unmarshaller.unmarshal(r,foo.class);
System.out.println(foo.getValue().getName());
//但是我如何得到偏移量呢?
//cache.put(r.getAttributeValue(null,“id”),r.getCursor();/???
打破
}
}
但是我不能得到偏移量。我想用这个来准备一个索引:
(元素id)->(文件中的偏移量)

然后我应该能够使用偏移量从那里解组:打开文件流,跳过那么多字节,解组。 我找不到这样的图书馆。如果不知道文件光标的位置,我不能自己做。上面清楚地指出有一个光标,但我找不到访问它的方法


编辑:
我只是想提供一个可以在旧硬件上工作的解决方案,这样人们就可以实际使用它了。不是每个人都能买得起一台功能强大的新电脑。使用StAX,我可以在大约2秒钟内获得数据,这有点长。但它不需要RAM。仅使用JAX-B就需要300 MB的RAM。对于这样一个简单的任务来说,使用一些嵌入式db系统会有很大的开销。无论如何,我将使用JAX-B。其他任何东西对我来说都是无用的,因为wsimport生成的类已经很完美了。我只是不想在只需要几个的时候加载300 MB的对象


我找不到一个只需要XSD就可以创建内存中的DB的DB,它不需要那么多RAM。它都是为服务器设计的,或者需要定义一个模式并映射XML。所以我认为它根本不存在

您可以使用生成的XML解析器

以下内容在
/20170501/dewiki-20170501-pages-articles-multistream.xml.bz2上非常有效,但我必须使用
-xX6GB
增加堆大小

1.获取XML语法 2.生成解析器 3.将生成的Java文件复制到项目中 4.与侦听器挂钩以收集字符偏移 基本上使用这个代码

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);
        }
    }

查找完整。

我只是必须解决这个问题,而且花了太多时间来解决它。希望下一个来寻找想法的可怜人能从我的痛苦中受益

要解决的第一个问题是,大多数XMLStreamReader实现在您询问它们的当前偏移量时会提供不准确的结果。然而,在这方面似乎坚如磐石

第二个问题是实际使用的偏移类型。如果需要使用多字节字符集,则必须使用字符偏移量,这意味着使用提供的偏移量从文件中进行随机访问检索不会非常有效-您不能只在偏移量处设置指向文件的指针并开始读取,您必须一直读到偏移量(这就是
skip
阅读器
的封面下所做的),然后开始提取。如果您处理的是非常大的文件,这意味着在接近文件结尾处检索内容的速度太慢

最后我编写了一个FilterReader,它在读取文件时保留字节偏移量到字符偏移量映射的缓冲区。当我们需要获取字节偏移量时,我们首先向Woodstox请求字符偏移量,然后让自定义读取器告诉我们字符偏移量的实际字节偏移量。我们可以从元素的开头和结尾获取字节偏移量,通过将元素作为RandomAccessFile打开,为我们提供所需的信息,并通过外科手术从文件中提取元素,这意味着它在文件中的任何一点都非常快速

我为此创建了一个库,它是开着的。如果你只是想得到重要的信息,聚会的诀窍就在后面

有些人评论说这整件事是个坏主意,你为什么要这么做?XML是一种传输机制,你应该把它导入数据库,用更合适的工具处理数据。在大多数情况下,这是正确的,但如果你正在构建通过XML通信的应用程序或集成,你需要一种工具对交换的文件进行分析和操作。我每天都收到验证提要内容的请求,能够从大量文件中快速提取一组特定项目,不仅验证内容,而且验证格式本身


无论如何,希望这可以节省一些时间,或者至少让他们更接近解决方案。

为什么不使用内存中的数据库?XML是一种可怕的信息存储方式。您尝试过
r.getLocation().getCharacterOffset()吗
?内存中使用的内存是未压缩xml文件的3倍。这对于桌面应用程序来说有点太多了。所以我想使用索引访问。真的吗?您尝试了内存中的哪一个数据库?H2?SQLite?xml作为传输格式是可以接受的,但作为存储格式(带操作)这真的是纯粹的垃圾。@jschnasse:是的,但是javadoc stat
cd /tmp/grammars-v4/xml/
mvn clean install
cp -r target/generated-sources/antlr4 /path/to/your/project/gen
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);
    }

}
@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);
        }
    }