Добавление подпрограмм доступа в сгенерированный 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. Кроме того, я не могу распознать "конкретную логику", которую можно как-то обобщить, поэтому эта опция, вероятно, даже не имеет смысла.

Другие вопросы по тегам