将访问例程添加到JAXB生成的来自XSD

时间:2018-07-14 14:54:26

标签: java jaxb adapter delegation facade

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

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

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

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


作为(略微)简化的示例,需要搜索基于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插件)?

1 个答案:

答案 0 :(得分:1)

有很多解决方案。

实用程序类

最简单的方法是简单地在实用程序类中实现访问路由。因此,基本上,您将调用类似$(document).ready(function(){ $(document).on("submit", "#form1", function (event) { event.preventDefault(); event.stopImmediatePropagation(); var formData = new FormData(this); var url = this[0].action; $.ajax({ url: url, type: 'POST', data: formData, success: function (response) { if (response) { //do necessary work with response } }, error: function() { //handle error here }, cache: false, contentType: false, processData: false }); return false; }); }); 之类的东西,并将架构派生类的实例传递给它。将其与SearchPatterns.of(foo)(其中foo.getSearchPatterns()以某种方式添加到模式派生的类)进行比较,差别不大。好吧,好吧,不是OOP封装之类的,而是坦率地说谁在乎。

使用代码注入器插件

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

Inserting code with XJC+xsd+jxb using the options " -Xinject-code -extension "

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

这对您来说相对容易,并且可以让您注入所需的任何代码。

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

扩展一个准备好的抽象类

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

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

JAXB Marshalling - extending an existing class

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

代替扩展类,您可以使用访问者的模板方法+默认方法定义一个接口,并让您的基于模式的类实现该接口。

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

编写您自己的XJC插件

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

但是,这是一种非常复杂的方法。简要概述,请参见this answer。我只会在确实存在“特定逻辑”的情况下接受它

建议

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

我不喜欢Code Injector选项,因为那样的话,您会将部分代码存储在一些奇怪的XML文件中。因此,例如,如果您将在IDE中重构任何内容,那么该代码将不会被触及。

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

对于没有XJC插件经验的开发人员来说,编写自己的插件太简单了。另外,我无法识别可以以某种方式概括的“特定逻辑”,因此该选项甚至可能没有任何意义。