(Все нижеследующее должно быть написано на Java)
Мне нужно создать приложение, которое будет принимать в качестве входных XML-документов потенциально очень большие документы. Документ зашифрован - не с помощью XMLsec, а с помощью уже существующего алгоритма шифрования моего клиента - будет обрабатываться в три этапа:
Сначала поток будет расшифрован согласно вышеупомянутому алгоритму.
Во-вторых, класс расширения (написанный третьей стороной для API, который я предоставляю) будет читать некоторую часть файла. Объем чтения непредсказуем - в частности, не гарантируется, что он будет в заголовке файла, но может произойти в любой точке XML.
Наконец, другой класс расширения (такая же сделка) разделит входной XML на 1..n подмножеств документов. Вполне возможно, что они будут частично перекрывать часть документа, обрабатываемую второй операцией, то есть: я считаю, что мне нужно будет перемотать любой механизм, который я использую для работы с этим объектом.
Вот мой вопрос:
Есть ли способ сделать это, никогда не считывая данные в память целиком? Очевидно, я могу реализовать дешифрование как фильтр входного потока, но я не уверен, можно ли анализировать XML так, как я описываю; пройдя по документу, необходимому для сбора информации на втором этапе, а затем перемотав документ и перемотав его снова, чтобы разделить его на задания, в идеале освободив все части документа, которые больше не используются после они прошли.




Вы можете использовать BufferedInputStream с очень большим размером буфера и использовать mark() до того, как сработает класс расширения, а затем reset().
Однако, если части, необходимые классу расширения, находятся очень далеко в файле, это может стать чрезвычайно интенсивным по памяти ».
Более общим решением было бы написать свой собственный BufferedInputStream-аналог, который буферизует диск, если данные, которые должны быть буферизованы, превышают некоторый заранее установленный порог.
Вас может заинтересовать XOM:
XOM is fairly unique in that it is a dual streaming/tree-based API. Individual nodes in the tree can be processed while the document is still being built. The enables XOM programs to operate almost as fast as the underlying parser can supply data. You don't need to wait for the document to be completely parsed before you can start working with it.
XOM is very memory efficient. If you read an entire document into memory, XOM uses as little memory as possible. More importantly, XOM allows you to filter documents as they're built so you don't have to build the parts of the tree you aren't interested in. For instance, you can skip building text nodes that only represent boundary white space, if such white space is not significant in your application. You can even process a document piece by piece and throw away each piece when you're done with it. XOM has been used to process documents that are gigabytes in size.
Похоже, это работа для StAX (JSR 173). StAX - это анализатор вывода, что означает, что он работает более или менее как синтаксический анализатор на основе событий, такой как SAX, но у вас больше контроля над тем, когда прекратить чтение, какие элементы вытащить, ...
Удобство использования этого решения будет во многом зависеть от того, что на самом деле делают ваши классы расширения, есть ли у вас контроль над их реализацией и т. д.
Суть в том, что если документ очень большой, вы, вероятно, захотите использовать анализатор на основе событий, а не на основе дерева, поэтому вы не будете использовать много памяти.
Реализации StAX можно найти у SUN (SJSXP), Codehaus или у нескольких других поставщиков.
Это выглядит многообещающе, если я могу эффективно к нему подключиться. Похоже, мне придется предоставить StAX клиентам моего API, что далеко не идеально, но, по крайней мере, похоже, что возможности есть. Можете ли вы изменить свой пост, указав рекомендуемую реализацию вместо списка?
Я знаю, что это старый ответ / комментарий, но есть некоторые библиотеки, которые могут добавить немного большего удобства поверх stax (и изолировать некоторые детали более низкого уровня), например StaxMate [staxmate.codehaus.org/Tutorial]. Это по-прежнему позволяет выполнять инкрементный синтаксический анализ / запись, но сокращает объем написанного кода.
Stax - правильный путь. Я бы порекомендовал посмотреть Woodstox
Я бы написал собственную реализацию InputStream, которая расшифровывает байты в файле, а затем использует SAX для анализа результирующего XML по мере его выхода из потока.
SAXParserFactory.newInstance().newSAXParser().parse(
new DecryptingInputStream(),
new MyHandler()
);
Посмотрите библиотеку XOM. Пример, который вы ищете, - это StreamingExampleExtractor.java в каталоге примеров исходного дистрибутива. Здесь показан метод выполнения потокового анализа большого XML-документа, построенный только на определенных узлах, их обработка и отбрасывание. Он очень похож на подход sax, но имеет гораздо больше встроенных возможностей синтаксического анализа, поэтому потоковый синтаксический анализ может быть выполнен довольно легко.
Если вы хотите работать на более высоком уровне, посмотрите NUX. Это обеспечивает высокоуровневый потоковый API xpath, который считывает в память только тот объем данных, который необходим для оценки xpath.
Это выглядит интересным и потенциально полезным подходом, но нигде в документации он не предлагает способ управления синтаксическим анализом документа описанным вами способом. Я считаю, что это может, но возможность не задокументирована таким образом, чтобы ее можно было обнаружить.