代码之家  ›  专栏  ›  技术社区  ›  Walter Kuhn

向JAXB生成的一组源自XSD的类添加访问例程

  •  0
  • Walter Kuhn  · 技术社区  · 7 年前

    我有一个基于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委托插件?

    1 回复  |  直到 7 年前
        1
  •  1
  •   lexicore    7 年前

    有很多方法可以解决这个问题。

    实用程序类

    最简单的方法是在实用程序类中简单地实现访问路由。所以基本上你会说 SearchPatterns.of(foo) 并将模式派生类的实例传递给它。将此与 foo.getSearchPatterns() (何处) getSearchPatterns() 以某种方式添加到模式派生类中)这并不是很大的区别。好吧,好吧,没什么好的,不过,坦率地说,谁在乎。

    使用代码注入器插件

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

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

    (如果你有问题,请再问一个问题。我意识到我们没有进入XJC代码注入器的问题 )

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

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

    扩展准备好的抽象类

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

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

    JAXB Marshalling - extending an existing class

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

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

    我不喜欢这个选项,因为它错误地使用继承只添加实用方法。

    编写自己的XJC插件

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

    然而,这是一个非常复杂的方法。见 this answer 简要概述。如果真的有“特定的逻辑”,我就接受它。

    建议

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

    我不喜欢代码注入器选项,因为这样您就可以将部分代码保存在一些奇怪的XML文件中。因此,例如,如果您在一个IDE中重构任何东西,那么代码就不会被触及。

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

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