SAXParser.parse: примеры (JAVA)
SAXParser.parse(File f, DefaultHandler dh): voidОбщее описание метода SAXParser.parse()
Класс javax.xml.parsers.SAXParser предоставляет несколько перегруженных версий метода parse() для последовательного (SAX) чтения XML-документов. Метод сам по себе возвращает void и при разборе вызывает методы зарегистрированного обработчика событий (например, org.xml.sax.helpers.DefaultHandler). Используется при обработке больших XML-потоков, когда важна экономия памяти и требуется реакция на события в процессе чтения.
Основные сигнатуры (обобщённо):
void parse(File f, DefaultHandler handler)void parse(InputStream is, DefaultHandler handler)void parse(InputStream is, DefaultHandler handler, String systemId)void parse(InputSource input, DefaultHandler handler)void parse(String uri, DefaultHandler handler)
Аргументы:
- File f - файл с XML; путь к ресурсу, читаемый средствами файловой системы.
- InputStream is - байтовый поток; полезен при чтении из сети или архива. Кодировка должна быть указана в XML-прологе или через InputSource.
- InputSource input - более гибкий источник; позволяет передать
Reader,InputStreamи системный идентификатор (systemId) для корректного разрешения относительных URI. - String uri - системный идентификатор; метод сам откроет поток по указанному URI.
- DefaultHandler handler - обработчик событий SAX, реализующий
ContentHandler,ErrorHandler,EntityResolverиDTDHandler. Можно передать свою реализацию для обработки элементов, текста и ошибок. - String systemId (в перегрузке) - значение для разрешения относительных ссылок в случае использования InputStream без явного systemId.
Возвращаемое значение: void. Результат работы выражается через побочные эффекты в обработчике событий (вызовы методов startElement, characters, endElement и т. п.).
Типичные исключения и поведение при ошибках:
SAXException- ошибки разбора XML (содержит часто объектSAXParseExceptionс номером строки и колонки).IOException- проблемы ввода/вывода при чтении источника.
Дополнительные возможности через SAXParser.getXMLReader() для установки фич и пропертей, например, включение пространств имён, валидация или отключение внешних сущностей для защиты от XXE.
Короткие примеры использования
Пример 1: чтение из файла с выводом событий
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
public class SimpleSax {
public static void main(String[] args) throws Exception {
SAXParserFactory f = SAXParserFactory.newInstance();
SAXParser p = f.newSAXParser();
DefaultHandler h = new DefaultHandler() {
public void startElement(String uri, String localName, String qName, Attributes atts) {
System.out.println("start:" + qName);
}
public void characters(char[] ch, int start, int length) {
System.out.println("text:" + new String(ch, start, length).trim());
}
public void endElement(String uri, String localName, String qName) {
System.out.println("end:" + qName);
}
};
p.parse(new java.io.File("example.xml"), h);
}
}
start:root text: start:item text:Hello end:item start:item text:World end:item end:root
Пример 2: разбор из InputStream с указанием systemId (важно для относительных ссылок)
InputStream is = new FileInputStream("/data/doc.xml");
SAXParserFactory f = SAXParserFactory.newInstance();
SAXParser p = f.newSAXParser();
DefaultHandler h = new MyHandler();
p.parse(is, h, "file:///data/doc.xml");
is.close();
(обработчик получает события; если в документе есть внешние ссылки, они будут разрешаться относительно file:///data/doc.xml)
Пример 3: разбор из InputSource с указанием Reader и кодировки
Reader r = new InputStreamReader(new FileInputStream("u.xml"), "UTF-8");
InputSource src = new InputSource(r);
src.setSystemId("http://example.com/u.xml");
SAXParserFactory f = SAXParserFactory.newInstance();
SAXParser p = f.newSAXParser();
p.parse(src, new MyHandler());
(разбор с корректным учетом кодировки и разрешением относительных ссылок)
Похожие API в Java и отличия
Краткое сравнение основных подходов к XML в Java:
- DOM (DocumentBuilder.parse) - загружает весь документ в память в виде дерева. Удобен при частых обратных обращениях к разным частям документа, не подходит для больших файлов.
- StAX (XMLStreamReader / XMLEventReader) - pull-парсер, дающий управление чтением потока. Часто удобнее для сложной логики обработки и более гибкого управления ресурсами по сравнению с SAX.
- JAXB - привязка XML к объектам Java (аннотации). Подходит при строгой схеме и желании работать с объектной моделью, скрывает низкоуровневые события SAX.
- XMLFilterImpl / XMLReader - возможность фильтрации/модификации потоковых событий SAX. Используется при промежуточной обработке перед конечным обработчиком.
Когда предпочесть:
- SAX - при работе с большими потоками, низкой памяти и событийной модели.
- StAX - при необходимости более детерминированного управления чтением и возможности «просить» следующий токен.
- DOM - при удобстве манипуляции деревом и небольших размерах документов.
- JAXB - при наличии XSD и желании получать объекты напрямую.
Аналоги в других языках и отличительные особенности
Короткие примеры и комментарии по другим платформам:
- Python (xml.sax)
import xml.sax class H(xml.sax.ContentHandler): def startElement(self, name, attrs): print('start:'+name) def characters(self, ch): print('text:'+ch.strip()) def endElement(self, name): print('end:'+name) xml.sax.parse('example.xml', H())start:root text: start:item text:Hello end:item start:item text:World end:item end:root
Комментарий: API близок по модели событий к Java SAX. - JavaScript (Node.js, sax)
const sax = require('sax'); const fs = require('fs'); const parser = sax.createStream(true); parser.on('opentag', t=>console.log('start:'+t.name)); parser.on('text', t=>console.log('text:'+t.trim())); parser.on('closetag', t=>console.log('end:'+t)); fs.createReadStream('example.xml').pipe(parser);start:root text: start:item text:Hello end:item start:item text:World end:item end:root
Комментарий: sax-js обеспечивает SAX-подобный потоковый парсер для Node. - PHP (XMLReader)
$r = new XMLReader(); $r->open('example.xml'); while ($r->read()) { if ($r->nodeType == XMLReader::ELEMENT) echo "start:{$r->name}\n"; if ($r->nodeType == XMLReader::TEXT) echo "text:{$r->value}\n"; if ($r->nodeType == XMLReader::END_ELEMENT) echo "end:{$r->name}\n"; } $r->close();start:root text: start:item text:Hello end:item start:item text:World end:item end:root
Комментарий: XMLReader - pull-парсер, похож на StAX. - C# (XmlReader)
using (var r = XmlReader.Create("example.xml")) { while (r.Read()) { if (r.NodeType == XmlNodeType.Element) Console.WriteLine("start:"+r.Name); if (r.NodeType == XmlNodeType.Text) Console.WriteLine("text:"+r.Value.Trim()); if (r.NodeType == XmlNodeType.EndElement) Console.WriteLine("end:"+r.Name); } }start:root text: start:item text:Hello end:item start:item text:World end:item end:root
Комментарий: XmlReader - pull-парсер, быстрый и низкоуровневый. - Go (encoding/xml.Decoder)
dec := xml.NewDecoder(bytes.NewReader(data)) for { t, _ := dec.Token() if t == nil { break } switch v := t.(type) { case xml.StartElement: fmt.Println("start:"+v.Name.Local) case xml.CharData: fmt.Println("text:"+strings.TrimSpace(string(v))) case xml.EndElement: fmt.Println("end:"+v.Name.Local) } }start:root text: start:item text:Hello end:item start:item text:World end:item end:root
Комментарий: Decoder предоставляет токенизированный поток, похожий на StAX. - Kotlin - в большинстве случаев используется Java-реализация SAXParser или сокращённые Kotlin-обёртки над ней; синтаксис более компактный, поведение совпадает с Java.
- Lua (LuaExpat / lxp)} - lxp предоставляет SAX-подобный интерфейс с callback-функциями для начала/конца элементов и текста.
Типичные ошибки и их симптомы
Список распространённых проблем с примерами и пояснениями:
- SAXParseException при некорректном XML
// example_bad.xml содержит: <root><item>Hello</root> SAXParser p = SAXParserFactory.newInstance().newSAXParser(); p.parse(new File("example_bad.xml"), new DefaultHandler());org.xml.sax.SAXParseException: The element type "item" must be terminated by the matching end-tag "</item>". at ... (line:1 column:...)
Пояснение: сообщается строка и колонка, где обнаружена ошибка. - IOException при отсутствии файла или проблемах с доступом
p.parse(new File("missing.xml"), handler);java.io.FileNotFoundException: missing.xml (No such file or directory)
Пояснение: проверка существования и прав доступа до вызова parse актуальна. - Внешние сущности и XXE (уязвимость)
SAXParserFactory f = SAXParserFactory.newInstance(); SAXParser p = f.newSAXParser(); p.parse(new File("xxe.xml"), handler);(возможна утечка локальных файлов или выполнение удалённых запросов при наличии DOCTYPE)
Пояснение: по умолчанию поведение зависит от реализации; рекомендуется явно отключать внешние сущности и включать secure processing. - Фрагментация текста
public void characters(char[] ch, int start, int length) { System.out.println(new String(ch, start, length)); }(текст может приходить несколькими вызовами characters; строки могут быть разделены)
Пояснение: объединение текста с помощью StringBuilder требуется для корректного получения полного содержимого элемента. - Проблемы потокобезопасности
SAXParser p = factory.newSAXParser(); // повторное использование одного обработчика и парсера из разных потоков(непредсказуемое поведение, ConcurrentModification и т.д.)
Пояснение: парсер и обработчики не гарантированно потокобезопасны; использовать отдельные экземпляры в потоках.
Изменения и рекомендации по безопасности в последних версиях
API SAXParser как часть JAXP остаётся стабильным длительное время. Основные моменты, на которые стоит обратить внимание:
- Модульная структура JDK (с Java 9+) распределила пакеты по модулям; SAX остаётся в модуле
java.xml, что обычно не требует дополнительных шагов при обычном использовании. - Рост внимания к безопасности: рекомендуется явно включать
XMLConstants.FEATURE_SECURE_PROCESSINGи отключать разрешение внешних сущностей через установки фич XMLReader, чтобы защититься от XXE. - Для валидации по XSD предпочтение отдаётся использованию
SchemaFactoryи установки схемы вSAXParserFactory.setSchema(...), вместо старой DTD-валидации. - Сам API не претерпел значительных семантических изменений; основные улучшения связаны с безопасностью, совместимостью модулей и рекомендациями по использованию Schema для валидации.
Расширенные и редкие сценарии использования
Примеры с пояснениями для сложных случаев.
1) Валидация по XSD через SAXParserFactory с Schema
SchemaFactory sf = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = sf.newSchema(new File("schema.xsd"));
SAXParserFactory f = SAXParserFactory.newInstance();
f.setNamespaceAware(true);
f.setSchema(schema);
SAXParser p = f.newSAXParser();
p.parse(new File("doc.xml"), new MyValidationHandler());
(при нарушении схемы вызывается SAXException с информацией о несоответствии)
Пояснение: использование Schema даёт проверку по XSD. Необходимо setNamespaceAware(true).
2) Отключение внешних сущностей и включение secure processing
SAXParserFactory f = SAXParserFactory.newInstance();
f.setFeature("http://xml.org/sax/features/external-general-entities", false);
f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
SAXParser p = f.newSAXParser();
p.parse(new File("possibly_malicious.xml"), handler);
(внешние сущности игнорируются, риск XXE снижен)
Пояснение: рекомендуется для обработки внешнего или непроверенного XML.
3) Кастомный EntityResolver для локального разрешения DTD
DefaultHandler h = new DefaultHandler() {
public InputSource resolveEntity(String publicId, String systemId) {
if (systemId.endsWith("my.dtd")) {
return new InputSource(new StringReader(""));
}
return null; // дефолтное поведение
}
};
SAXParserFactory f = SAXParserFactory.newInstance();
SAXParser p = f.newSAXParser();
p.parse(new File("doc_with_dtd.xml"), h);
(DTD разрешается локально, внешние запросы не выполняются)
Пояснение: полезно при контроле и кэшировании внешних ресурсов.
4) Обработка больших XML-потоков и агрегирование данных
// обработчик собирает записи и периодически записывает в БД
class BatchHandler extends DefaultHandler {
StringBuilder cur = new StringBuilder();
List<String> items = new ArrayList<>();
public void startElement(String uri, String localName, String qName, Attributes atts) { if (qName.equals("item")) cur.setLength(0); }
public void characters(char[] ch, int start, int length) { cur.append(ch, start, length); }
public void endElement(String uri, String localName, String qName) {
if (qName.equals("item")) {
items.add(cur.toString().trim());
if (items.size() >= 1000) { saveToDb(items); items.clear(); }
}
}
}
// p.parse(stream, new BatchHandler());
(партии по 1000 записей сохраняются в БД, память ограничена)
Пояснение: SAX хорош при потоковой обработке без загрузки всего файла в память.
5) Использование XMLFilterImpl для изменения потока событий
XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
XMLFilterImpl filter = new XMLFilterImpl(reader) {
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
if (qName.equals("oldName")) qName = "newName"; // пример изменения
super.startElement(uri, localName, qName, atts);
}
};
filter.setContentHandler(new MyHandler());
filter.parse(new InputSource(new FileInputStream("in.xml")));
(в событиях обработчика встречается уже изменённое имя элемента)
Пояснение: XMLFilterImpl позволяет вставлять промежуточную логику между парсером и конечным обработчиком.
6) Интеграция с XSLT как SAXSource
SAXSource source = new SAXSource(new InputSource(new FileInputStream("in.xml")));
Transformer t = TransformerFactory.newInstance().newTransformer(new StreamSource(new File("style.xsl")));
StringWriter out = new StringWriter();
t.transform(source, new StreamResult(out));
System.out.println(out.toString());
(результат применения XSLT к поточному XML)
Пояснение: SAX-поток может служить источником для XSLT без предварительной загрузки в DOM.