首页 > 解决方案 > 将访问例程添加到源自 XSD 的 JAXB 生成的类集

问题描述

我有一个基于 XSD 的嵌套 XML 结构。我使用 JAXB 进行解组(只读)。

通常,我需要在大型结构的某处找到一个或多个元素。为了避免每次需要搜索时都遍历结构,我想添加一个带有内部缓存的优化搜索功能。

定义它的最佳方式是什么?不同方式的优缺点是什么?

我最初想到的是使用外观或适配器,其中适配器类访问生成的类并根据需要添加方法;但是我想征求建议。


作为一个(稍微)简化的示例,需要在基于此 XSD 的 XML 中搜索具有特定“boq”元素的“step”类型的元素:

<?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 本身的选项?使用第三方插件,例如 maven 的 jaxb-delegate 插件?

标签: javajaxbadapterdelegationfacade

解决方案


有很多选项可以解决这个问题。

实用类

最简单的是在实用程序类中简单地实现访问路由。所以基本上你会调用类似的东西SearchPatterns.of(foo)并将模式派生类的实例传递给它。将此与foo.getSearchPatterns()getSearchPatterns()以某种方式添加到模式派生类中的位置)进行比较,差别不大。好吧,好吧,不是那么 OOP-incapsulation-whatever,但是,坦率地说,谁在乎。

使用代码注入器插件

您可以使用 XJC 代码注入器插件在生成的类中注入任何代码。请参阅以下问题以获取示例:

使用选项“-Xinject-code -extension”插入带有 XJC+xsd+jxb 的代码

(如果您遇到问题,请再问一个问题。我知道我们在中没有 XJC 代码注入器问题。)

这对您来说相对容易,并且允许您注入您想要的任何代码。

缺点是部分 Java 代码将驻留在一个奇怪的 XML 文件中。

扩展一个准备好的抽象类

getSearchpatterns()另一种选择是使用您需要的访问器方法(例如)以及它使用的方法(getGroup())作为抽象方法来准备一个抽象类。然后让你的模式派生类扩展这个准备好的抽象类。生成的方法将实现在准备好的超类中定义的抽象方法。这本质上是一种“模板方法”模式。

有很多方法可以使模式派生类扩展现有类。这是其中之一:

JAXB Marshalling - 扩展现有类

或者,您可以使用JAXB2 Basics中的继承插件。免责声明:我是作者。

您可以使用模板方法+访问器的默认方法定义一个接口,而不是扩展一个类,并让您的模式派生类实现它。

我不喜欢这个选项,因为它滥用继承只是为了添加实用程序方法。

编写你自己的 XJC 插件

您要添加的访问器背后可能有一些特定的逻辑。因此,也许不是简单地注入代码(如使用代码注入器插件),您实际上可以根据“特定逻辑”生成访问器。

然而,这是一种非常复杂的方法。有关简短概述,请参阅此答案。如果真的有“特定逻辑”,我只会接受它

推荐

我个人更喜欢将我的业务逻辑与模式派生类分开。我可能是那里最大的 JAXB/XJC 粉丝之一。我肯定会编写一个实用程序类来提供您想要的任何访问器。

我不喜欢 Code Injector 选项,因为这样你的代码的一部分就会出现在一些奇怪的 XML 文件中。因此,例如,如果您要在 IDE 中重构任何内容,则不会触及该代码。

扩展一个准备好的抽象类或实现一个准备好的接口也不是我最喜欢的。我认为这只是滥用 OOP 结构来添加一些实用程序代码。

对于没有太多 XJC 插件经验的开发人员来说,编写自己的插件实在是太复杂了。此外,我无法识别可以以某种方式概括的“特定逻辑”,所以这个选项可能甚至没有意义。


推荐阅读