Библиотеки XML в Java — это минное поле. Объем кода, необходимый для манипулирования и чтения XML, ошеломляет, риск возникновения проблем с путями классов в разных библиотеках значительный, а обработка пространств имен открывает много путаницы и ошибок. Хуже всего то, что ситуация, похоже, не улучшается.
Коллега дал мне знать о библиотеке JOOX некоторое время назад. Это очень хорошая попытка исправить эти проблемы. Я обнаружил несколько недостатков в JOOX, которые заставили меня захотеть исследовать альтернативы, и, естественно, я закончил писать свою собственную библиотеку (как и вы). Я хочу, чтобы библиотека позволяла легко манипулировать XML, и в эпизоде недостаточного суждения я назвал библиотеку EAXY. Это действительно плохое имя, поэтому я ценю предложения по улучшению.
Вот что я решил решить:
- Должно быть легко создавать довольно сложные XML-деревья с помощью Java-кода.
- Использование пространств имен должно быть простым и надежным. (Это где JOOX подвел меня)
- Должно легко считываться значение из структуры XML.
- Должно быть легко работать с существующими XML-документами в структуре файла или пути к классам.
- Библиотека должна предпочитать выбрасывать исключение вместо молчаливого сбоя.
- В качестве бонуса я хотел упростить работу с (X) HTML, добавив для этого удобные функции.
1. Создание XML-документа
Документ XML — это просто дерево. Как насчет того, чтобы согласовать дерево с синтаксическим деревом Java. Например — допустим, что вы хотели программно захотеть построить некоторые отзывы об этой статье:
01
02
03
04
05
06
07
08
09
10
11
12
|
Element email = Xml.el( "message" , Xml.el( "recipients" , Xml.el( "recipent" , Xml.attr( "type" , "email" ), Xml.attr( "role" , "To" ), Xml.el( "recipent" , Xml .attr( "type" , "email" ), Xml.attr( "role" , "Cc" ), Xml.el( "subject" , "EAXY feedback" ), Xml.el( "contents" , "I think this is an interesting library" )); |
Каждый элемент (Xml.el) имеет имя тега и может вкладывать другие элементы, атрибуты (Xml.attr) или текст (Xml.text). Если элемент содержит только текст, нам даже не нужно вызывать Xml.text. Синтаксис оптимизирован так, что если вы хотите выполнить статический импорт в Xml. *, Вы можете написать код, подобный следующему:
01
02
03
04
05
06
07
08
09
10
11
12
|
Element email = el( "message" , el( "recipients" , el( "recipent" , attr( "type" , "email" ), attr( "role" , "to" ), el( "recipent" , attr( "type" , "email" ), attr( "role" , "cc" ), el( "subject" , "EAXY feedback" ), el( "content" , "I think this is an interesting library" )); |
2. Чтение XML
Чтение XML с помощью кода Java может быть проблемой. API DOM делает очень многословным делать что-либо вообще. Вы используете XPath, но на компактной стороне это может быть слишком много, и когда вы делаете что-то не так, в результате вы просто получаете пустую коллекцию или нулевое значение. Я думаю, что мы можем улучшить это.
Учтите следующее:
1
|
System.out.println(email.find( "recipients" , "recipient" ).texts()); |
Я опускаю древовидную структуру XML и получаю все адреса электронной почты получателей предыдущего сообщения. Но подождите — выполнение этого кода возвращает пустой список. EAXY позволяет нам не царапать голову над этим:
1
|
System.out.println(email.find( "recipients" , "recipient" ).check().texts()); |
Теперь я получаю следующее исключение:
1
2
3
|
org.eaxy.NonMatchingPathException: Can't find {recipient} below [message, recipients]. Actual elements: [Element{recipent}, Element{recipent}] |
Как видите, мы неправильно написали «получатель» в сообщении. Давайте вернемся к этой проблеме позже, а пока, давайте обойдем ее, чтобы создать что-то значимое:
1
2
3
4
5
|
for (Element recipient : email.find( "recipients" , "recipent" )) { if ( "to" .equals(recipient.attr( "role" ))) { System.out.println(recipient.text()); } } |
Опять же, я думаю, что это настолько свободно, насколько позволяет синтаксис Java.
3. Проверка и пространства имен
Итак, у нас было сообщение, где одно из имен элементов было написано с ошибкой. Если у вас есть документ XSD для используемого вами XML, вы можете проверить документ на соответствие этому. Однако, как вы можете привыкнуть, когда дело доходит до библиотек Java XML, выполнение этой проверки довольно хорошо скрыто за сложными API. Итак, я предоставил небольшую помощь:
1
|
Xml.validatorFromResource( "mailmessage.xsd" ).validate(email); |
Это читает mailmessage.xsd из classpath, что является наиболее распространенным вариантом использования для меня.
Конечно, большинство схем не ссылаются на элементы в пустом пространстве имен. При использовании валидации обычно приходится создавать элементы в определенном пространстве имен. В большинстве библиотек Java для работы с XML сложно и легко ошибиться, особенно когда пространства имен смешаны. Я сделал пространства имен основной функцией библиотеки Eaxy:
1
2
3
4
5
6
7
|
Element email = MSG_NS.el( "message" , MSG_NS.el( "recipients" , MSG_NS.el( "recipient" , MSG_NS.attr( "type" , "email" ), attr( "role" , "cc" ), |
Обратите внимание, что атрибуты «type» и «role» принадлежат разным пространствам имен — сценарий, который особенно трудно реализовать в других библиотеках.
4. Шаблонирование
Чтение XSD из пути к классам вдохновило другое использование: что, если у нас есть XML-документ в качестве шаблона в пути к классам, а затем использовать Java-код для управления этим документом. Это было бы особенно удобно для XHTML:
01
02
03
04
05
06
07
08
09
10
11
|
Document doc = Xml.readResource( "testdocument.html" ); Element peopleElement = doc.select( "#peopleForm" ); peopleElement.add(el( "input" , attr( "type" , "text" ), attr( "name" , "firstName" ), attr( "value" , "Johannes" ))); peopleElement.add(el( "input" , attr( "type" , "text" ), attr( "name" , "lastName" ), attr( "value" , "Brodwall" ))); |
Этот код читает файл testdocument.html из classpath, выбирает элемент с идентификатором peopleForm и добавляет к нему два элемента ввода.
5. Удобство HTML
В приведенном выше коде мы устанавливаем атрибуты типа, имени и значения для элементов ввода HTML. Это одни из наиболее часто используемых атрибутов в манипулировании HTML. Чтобы сделать это проще, я добавил несколько удобных методов в Eaxy:
1
2
3
4
|
peopleElement.add(el( "input" ) .type( "text" ).name( "firstName" ).val( "Johannes" )); peopleElement.add(el( "input" ) .type( "text" ).name( "lastName" ).val( "Brodwall" )); |
Последний случай, для которого я хотел оптимизировать, — это работа с формами в HTML. Вот некоторый код, который манипулирует формой перед тем, как ее можно отправить пользователю.
1
2
3
4
5
|
HtmlForm form = new HtmlForm(peopleElement); form.set( "firstName" , "Johannes" ); form.set( "lastName" , "Brodwall" ); doc.writeTo(req.getWriter()); |
Здесь я устанавливаю содержимое формы напрямую. Код выдаст исключение, если имя параметра написано с ошибкой, поэтому легко убедиться, что вы используете его правильно.
Вывод
У меня есть пять примеров того, как Eaxy может легко сделать то, что трудно сделать с большинством библиотек XML для Java: создать дерево документов с чистым кодом Java, читать и манипулировать отдельными частями дерева XML, использовать пространство имен и проверку, шаблонирование и манипулирование (X) HTML документами и формами.
Библиотека сейчас нестабильна, но нестабильность библиотеки XML может быть не очень рискованной ситуацией, так как большинство ошибок будет легко обнаружить задолго до производства.
Я надеюсь, что вам может пригодиться попытаться использовать эту библиотеку в своем коде для работы с XML и (X) манипулированием HTML. Я надеюсь, что некоторые пользователи помогут мне сгладить ошибки и сделать Eaxy еще более простым в использовании.
Да, и дай мне знать, если ты придумаешь лучшее имя.