Добавление подпрограмм доступа в сгенерированный JAXB набор классов, происходящих из XSD
У меня есть вложенная структура XML, основанная на XSD. Я использую JAXB для демаршаллинга (только чтение).
Регулярно мне нужно найти один или несколько элементов где-то в большой структуре. Чтобы избежать обхода структуры каждый раз, когда мне нужно искать, я хотел бы добавить оптимизированную функцию поиска с внутренним кешем.
Какой это оптимальный способ определить это? Каковы плюсы / минусы для разных способов?
Сначала я подумал об использовании фасада или адаптера, где класс adaper обращается к сгенерированному классу и добавляет необходимые методы; Однако я хотел бы попросить рекомендации.
В качестве (слегка) упрощенного примера необходимо выполнить поиск XML на основе этого XSD для элементов типа "step", имеющих определенный элемент "boq":
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="group"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="group">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="step"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="step">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" ref="number"/>
<xs:element ref="name"/>
<xs:element ref="type"/>
<xs:element ref="target"/>
<xs:sequence minOccurs="0">
<xs:element ref="boq"/>
<xs:element ref="remote"/>
</xs:sequence>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="number" type="xs:integer"/>
<xs:element name="name" type="xs:NCName"/>
<xs:element name="type" type="xs:NCName"/>
<xs:element name="target" type="xs:NCName"/>
<xs:element name="boq" type="xs:string"/>
<xs:element name="remote" type="xs:string"/>
</xs:schema>
Эта схема была скомпилирована с использованием JAXB, поэтому я получил несколько классов. Используя функции демаршаллинга, у меня есть структура данных в памяти, которая обращается к моему XML.
Теперь рассмотрим, мне нужна оптимизированная функция поиска, которая обращается ко всем шагам, где определен элемент boq, и возвращает значение boq и remote (если также определено).
HashMap<String,Step> resultMap = new HashMap<>();
test.getGroup().forEach(group ->
group.getStep().forEach(step -> {
if ("searchpattern".equals(step.getBoq()))
resultMap.put("searchpattern", step);
}));
Каков наилучший способ сделать такой поиск? Я мог бы написать второй класс как адаптер, который включает этот метод, или есть лучшие варианты? Наследование? используя параметры самого JAXB? используя сторонний плагин, такой как плагин jaxb-Delegate для Maven?
1 ответ
Существует несколько вариантов решения этой проблемы.
Сервисный класс
Самое простое - просто реализовать маршрутизацию доступа в служебном классе. Так что в основном вы будете называть что-то вроде SearchPatterns.of(foo)
и передать ему экземпляр класса, производного от схемы. Сравнивая это с foo.getSearchPatterns()
(где getSearchPatterns()
каким-то образом добавляется к классу, производному от схемы), разница не так уж и велика. Ну да ладно, не так ООП-инкапсуляция-что угодно, но, честно говоря, кого это волнует.
Используйте плагин Code Injector
Вы можете использовать XJC Code Injector Plugin для внедрения любого кода в сгенерированные классы. Пожалуйста, смотрите следующий вопрос для примера:
Вставка кода с помощью XJC+xsd+jxb с использованием параметров "-Xinject-code -extension"
(Если у вас есть проблемы с этим, пожалуйста, задайте другой вопрос. Я понимаю, что у нас нет вопроса о переходе на XJC Code Injector в jaxb.)
Это относительно легко для вас и позволит вам вводить любой код, который вы хотите.
Недостатком является то, что часть вашего Java-кода будет находиться в странном XML-файле.
Расширить подготовленный абстрактный класс
Другой вариант - подготовить абстрактный класс с нужным вам методом доступа (скажем, getSearchpatterns()
) а также методы, которые он использует (getGroup()
) как абстрактные методы. Затем сделайте, чтобы ваш производный от схемы класс расширял этот подготовленный абстрактный класс. Сгенерированные методы будут реализовывать абстрактные методы, определенные в подготовленном суперклассе. По сути, это шаблон "Шаблонный метод".
Есть много способов заставить класс, производный от схемы, расширить существующий класс. Это один из них:
JAXB Marshalling - расширение существующего класса
Или вы можете использовать плагин Inheritance из JAXB2 Basics. Отказ от ответственности: я автор.
Вместо расширения класса вы можете определить интерфейс с методами шаблона + метод по умолчанию для метода доступа и позволить вашему классу, производному от схемы, реализовать его.
Я не фанат этой опции, поскольку она использует наследование только для добавления служебных методов.
Напишите свой собственный плагин XJC
Может быть какая-то особая логика за аксессорами, которые вы хотите добавить. Так что, возможно, вместо простого введения кода (как с плагином Code Injector) вы можете фактически сгенерировать средство доступа в соответствии с "определенной логикой".
Это, однако, очень сложный подход. Смотрите этот ответ для краткого обзора. Я бы взял это, только если есть действительно "конкретная логика"
Рекомендация
Лично я предпочитаю держать свою бизнес-логику отдельно от классов, полученных из схемы. И я, наверное, один из самых больших поклонников JAXB/XJC там. Я бы определенно написал вспомогательный класс, который предоставляет любые средства доступа, которые вы хотите.
Мне не нравится опция Code Injector, потому что тогда вы будете иметь часть вашего кода в каком-то странном XML-файле. Так что если вы, например, будете что-то реорганизовывать в IDE, этот код не будет затронут.
Расширение подготовленного абстрактного класса или реализация подготовленного интерфейса также не является моим любимым. Я думаю, что это просто неправильное использование конструкций ООП для добавления некоторого служебного кода.
Написание собственного плагина просто слишком сложно для разработчика без особого опыта работы с плагинами XJC. Кроме того, я не могу распознать "конкретную логику", которую можно как-то обобщить, поэтому эта опция, вероятно, даже не имеет смысла.