Пример парсинга Digester XML
Apache Digester — почтенная библиотека для анализа XML и превращения его в граф Java-объектов. Существует множество библиотек для анализа и написания XML, и Digester не самый производительный во время выполнения, он не поддерживает повторную запись XML и может показаться немного недоступным для новых пользователей. Но мне это очень нравится, и это очень мощный инструмент в моем наборе инструментов.
Конфигурация XML
Существует особый вариант использования XML, где XML используется в качестве редактируемого человеком файла конфигурации. Конечно, файлы свойств просты в использовании для этой цели, но там, где требуется более сложная конфигурация, особенно с естественной иерархией конфигурации, XML просто кажется более логичным. Например, сравните простой log4j.properties
файл с простым log4j.xml
файлом.
log4j.rootLogger=INFO,A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %c{4} - %m%n
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="A1" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out"/> <param name="Threshold" value="INFO"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-4r %-5p [%t] %c{4} - %m%n"/> </layout> </appender> <root> <appender-ref ref="CONSOLE"/> </root> </log4j>
Оба этих файла делают одно и то же, а XML гораздо более многословен. Но его преимущество в том, что он имеет естественную иерархию, которая позволяет избежать ошибок (невозможно применить шаблон к приложению, которое не определено иным образом) и облегчает редактирование (нет необходимости искать в большом файле все места, где appender настроен).
Конечно, с Ruby, Python и подобным синтаксисом у нас есть способы предоставления иерархических данных, которые не столь многословны, как XML. Но, тем не менее, XML быстрый, широко используемый, и легко найти инструменты, которые улавливают очевидные синтаксические ошибки.
В случаях, когда мы используем XML для конфигурации, мы, как правило, не заботимся о возможности его повторной записи, и мы не обязательно хотим согласовать нашу структуру объектов Java с тем, чтобы соответствовать XML. Нам нужно что-то быстрое и то, что требует очень мало строк кода. Дигестер разработан для такого рода использования.
Дайджест для меню
Недавно у меня возникла необходимость предоставить динамическое меню Swing. Пункты меню будут настраиваться после поставки программного обеспечения, и я не хочу, чтобы эта настройка включала язык программирования общего назначения. Вероятно, есть какая-то библиотека для динамических меню Swing, но я не хотел находить и интегрировать еще одну стороннюю библиотеку для чего-то, что будет содержать менее 250 строк кода. Таким образом, XML-меню и Digester были логичным соответствием. В Digester мой подход всегда состоит в том, чтобы сделать пример того, как я хочу, чтобы XML выглядел. Я хотел произвольное вложение меню, и пункты меню просто должны были отправлять сообщение при выборе, поэтому результат был довольно простым, похожим на этот:
<?xml version="1.0"?> <menus> <menu id="1" title="Parent"> <menu id="2" title="Child" /> <menu id="3" title="Second Child" /> </menu> <menu id="4" title="Menu" /> </menus>
Следующий шаг с Digester — предоставить ему правила для сопоставления XML с Java. Digester поддерживает аннотации и предоставляет «свободный» API для создания правил в Java, но я предпочитаю набор правил XML. Вот где светится Digester, так как правила для этого примера очень просты:
<?xml version="1.0"?> <digester-rules> <pattern value="*/menu"> <object-create-rule classname="org.anvard.digester.MenuItem" /> <set-properties-rule/> <set-next-rule methodname="add"/> </pattern> </digester-rules>
Класс MenuItem является POJO с добытчиками и сеттеров для id
и title
атрибутов. Он также хранит список своих дочерних элементов и предоставляет метод add.
Шаблон определяет набор действий, которые происходят всякий раз, когда он находит элемент меню на любом уровне. object-create-rule
Говорит Digester для создания экземпляра MenuItem
. Этот новый объект помещается в стек, поэтому он становится целью по умолчанию для следующих правил. set-properties-rule
Говорит Digester для соответствия атрибутов Java свойств. set-next-rule
Говорит Digester передать текущую вершину стека в качестве параметра объекта , который находится рядом в стеке вызова метода add
. Наконец, когда </pattern>
достигается закрывающий тег, новый объект выталкивается из стека.
Чтобы использовать это из Java это вопрос нескольких строк.
List<MenuItem> menus = new ArrayList<MenuItem>(); File file = new File("/some/directory/menu-config.xml"); Digester digester = DigesterLoader.createDigester(MenuManager.class .getResource("/menu-rules.xml")); digester.push(menus); try { digester.parse(file); } catch (IOException e) { LOG.warn("Could not load file", e); } catch (SAXException e) { LOG.warn("Invalid file", e); }
В этом коде используется тот факт, что в List
классе есть метод add, который принимает один параметр. Поскольку список помещается в стек до того, как Digester анализирует файл, он служит родителем для всех пунктов меню на верхнем уровне. Это избавляет нас от необходимости писать правила Digester для <menus>
элемента.
Окончательная версия, которую я написал, позволила интегрировать предоставляемые XML-меню с существующими жестко запрограммированными меню и, конечно, создавать реальные JMenuItem
экземпляры с прослушивателями действий.
Почему Дигестер, Плюс Альтернативы
Дигестер использует SAX под одеялами. Использование SAX напрямую, даже для этого простого примера, было бы болезненным. Чем меньше об этом сказано, тем лучше.
JAXB — очень правильная альтернатива. С JAXB я мог бы создавать объекты Java с аннотациями для ввода этих данных, и это не было бы слишком сложно. Было бы немного сложнее с необходимостью включить несколько дочерних элементов в каждый объект, но ничего, с чем вы не могли бы справиться Но Дигестер кажется мне более элегантным способом справиться с этим. В реальных примерах часто бывает необходимо изменить макет XML, чтобы соответствовать чьему-либо представлению о том, что имеет наибольшее значение, и очень сложно связываться с JAXB, чтобы убедить его связать произвольную структуру XML с произвольным набором объектов Java. , С Digester это просто изменение правила.
Среда Spring с поддержкой пользовательских пространств имен XML теперь является еще одной хорошей альтернативой Digester. У меня был успех с пользовательским XML для Spring, и если бы конкретный Java-код, с которым я работал, уже использовал Spring, я бы, наверное, выбрал его. Но для проектов, которые еще не используют Spring, кажется, что тяжеловесно тянуть его и писать собственные обработчики пространства имен для чтения пары файлов. Кроме того, если непрограммисты собираются редактировать файл, они должны знать, не трогать шаблон сверху.
С другой стороны, мой опыт работы в команде заключается в том, что новым людям может потребоваться немного времени, чтобы понять, что происходит с Дигестером. Я видел, как некоторые из них продолжают использовать это сами, что, я думаю, говорит о хорошем. Он имеет сходство со средой Spring в том смысле, что вам нужно привыкнуть к размышлениям и самоанализу, чтобы иметь возможность представить, что происходит за кулисами. Насколько мне нравится, язык правил XML также может быть немного сложным для чтения и запоминания.
Digester использовался еще до появления большинства современных инструментов XML, но он все еще поддерживается, и это была очень хорошая библиотека, которую нужно знать.