将访问例程添加到 JAXB 生成的 类 集(源自 XSD)
Adding access routines to a JAXB generated set of classes originating from an XSD
我有一个基于 XSD 的嵌套 XML 结构。我使用 JAXB 进行解组(只读)。
我经常需要在大型结构的某处找到一个或多个元素。为了避免每次需要搜索时都遍历结构,我想添加一个优化的带有内部缓存的搜索功能。
定义它的最佳方式是什么? pro/cons 有哪些不同的方式?
我最初想到的是使用外观或适配器,其中适配器 class 访问生成的 class 并根据需要添加方法;但是我想征求建议。
作为一个(稍微)简化的示例,需要搜索基于此 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 编译的,所以我得到了几个 classes。
使用解组功能,我在内存中有访问我的 XML 的数据结构。
现在考虑,我需要一个优化的搜索功能,它访问定义了 boq 元素的所有步骤,以及 return 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);
}));
封装此类搜索的最佳方法是什么?我可以编写第二个 class 作为包含此方法的适配器,或者有更好的选择吗?遗产?使用 JAXB 本身的选项?使用第三方插件,例如 maven 的 jaxb-delegate 插件?
有多种方法可以解决这个问题。
实用程序Class
最简单的方法是在实用程序中简单地实现访问路由 class。所以基本上你会调用类似 SearchPatterns.of(foo)
的东西,并将模式派生的 class 的实例传递给它。将其与 foo.getSearchPatterns()
(其中 getSearchPatterns()
以某种方式添加到模式派生的 class)进行比较,差别不大。好吧,没那么 OOP-incapsulation-whatever,但是,坦率地说,谁在乎。
使用代码注入器插件
您可以使用 XJC 代码注入器插件在生成的 classes 中注入任何代码。请参阅以下问题的示例:
Inserting code with XJC+xsd+jxb using the options " -Xinject-code -extension "
(如果您遇到问题,请提出另一个问题。我知道我们在 jaxb 中没有 XJC 代码注入器问题。)
这对您来说相对容易,并且允许您注入任何您想要的代码。
缺点是您的 Java 代码的一部分将驻留在一个奇怪的 XML 文件中。
扩展准备好的摘要Class
另一种选择是使用您需要的访问器方法(例如,getSearchpatterns()
)以及它使用的方法(getGroup()
)准备一个抽象 class 作为 抽象 方法。然后让你的模式派生 class 扩展这个准备好的摘要 class。生成的方法将实现在准备好的 superclass 中定义的抽象方法。这本质上是一个 "Template method" 模式。
有很多方法可以使模式派生 class 扩展现有的 class。这是其中之一:
JAXB Marshalling - extending an existing class
或者您可以使用 JAXB2 Basics 中的继承插件。 免责声明:我是作者。
而不是扩展 class,您可以定义一个带有模板方法的接口 + 访问器的默认方法,并让您的模式派生 class 实现它。
我不喜欢这个选项,因为它滥用继承来添加实用方法。
编写您自己的 XJC 插件
您要添加的访问器背后可能有一些特定的逻辑。因此,也许您可以根据 "specific logic".
实际上生成访问器,而不是简单地注入代码(例如代码注入器插件)
然而,这是一种非常复杂的方法。有关简短概述,请参阅 this answer。我只会在真的有 "specific logic"
的情况下接受它
推荐
我个人更喜欢将我的业务逻辑与模式派生的 classes 分开。我可能是那里最大的 JAXB/XJC 粉丝之一。我肯定会编写一个实用程序 class 来提供您想要的任何访问器。
我不喜欢 Code Injector 选项,因为这样您的部分代码就会出现在一些奇怪的 XML 文件中。因此,如果您要重构 IDE 中的任何内容,则不会触及该代码。
扩展准备好的抽象 class 或实现准备好的接口也不是我的最爱。我认为这只是滥用 OOP 构造来添加一些实用程序代码。
对于没有太多 XJC 插件经验的开发人员来说,编写自己的插件太复杂了。另外,我无法识别可以以某种方式概括的 "specific logic",所以这个选项甚至可能没有意义。
我有一个基于 XSD 的嵌套 XML 结构。我使用 JAXB 进行解组(只读)。
我经常需要在大型结构的某处找到一个或多个元素。为了避免每次需要搜索时都遍历结构,我想添加一个优化的带有内部缓存的搜索功能。
定义它的最佳方式是什么? pro/cons 有哪些不同的方式?
我最初想到的是使用外观或适配器,其中适配器 class 访问生成的 class 并根据需要添加方法;但是我想征求建议。
作为一个(稍微)简化的示例,需要搜索基于此 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 编译的,所以我得到了几个 classes。 使用解组功能,我在内存中有访问我的 XML 的数据结构。
现在考虑,我需要一个优化的搜索功能,它访问定义了 boq 元素的所有步骤,以及 return 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);
}));
封装此类搜索的最佳方法是什么?我可以编写第二个 class 作为包含此方法的适配器,或者有更好的选择吗?遗产?使用 JAXB 本身的选项?使用第三方插件,例如 maven 的 jaxb-delegate 插件?
有多种方法可以解决这个问题。
实用程序Class
最简单的方法是在实用程序中简单地实现访问路由 class。所以基本上你会调用类似 SearchPatterns.of(foo)
的东西,并将模式派生的 class 的实例传递给它。将其与 foo.getSearchPatterns()
(其中 getSearchPatterns()
以某种方式添加到模式派生的 class)进行比较,差别不大。好吧,没那么 OOP-incapsulation-whatever,但是,坦率地说,谁在乎。
使用代码注入器插件
您可以使用 XJC 代码注入器插件在生成的 classes 中注入任何代码。请参阅以下问题的示例:
Inserting code with XJC+xsd+jxb using the options " -Xinject-code -extension "
(如果您遇到问题,请提出另一个问题。我知道我们在 jaxb 中没有 XJC 代码注入器问题。)
这对您来说相对容易,并且允许您注入任何您想要的代码。
缺点是您的 Java 代码的一部分将驻留在一个奇怪的 XML 文件中。
扩展准备好的摘要Class
另一种选择是使用您需要的访问器方法(例如,getSearchpatterns()
)以及它使用的方法(getGroup()
)准备一个抽象 class 作为 抽象 方法。然后让你的模式派生 class 扩展这个准备好的摘要 class。生成的方法将实现在准备好的 superclass 中定义的抽象方法。这本质上是一个 "Template method" 模式。
有很多方法可以使模式派生 class 扩展现有的 class。这是其中之一:
JAXB Marshalling - extending an existing class
或者您可以使用 JAXB2 Basics 中的继承插件。 免责声明:我是作者。
而不是扩展 class,您可以定义一个带有模板方法的接口 + 访问器的默认方法,并让您的模式派生 class 实现它。
我不喜欢这个选项,因为它滥用继承来添加实用方法。
编写您自己的 XJC 插件
您要添加的访问器背后可能有一些特定的逻辑。因此,也许您可以根据 "specific logic".
实际上生成访问器,而不是简单地注入代码(例如代码注入器插件)然而,这是一种非常复杂的方法。有关简短概述,请参阅 this answer。我只会在真的有 "specific logic"
的情况下接受它推荐
我个人更喜欢将我的业务逻辑与模式派生的 classes 分开。我可能是那里最大的 JAXB/XJC 粉丝之一。我肯定会编写一个实用程序 class 来提供您想要的任何访问器。
我不喜欢 Code Injector 选项,因为这样您的部分代码就会出现在一些奇怪的 XML 文件中。因此,如果您要重构 IDE 中的任何内容,则不会触及该代码。
扩展准备好的抽象 class 或实现准备好的接口也不是我的最爱。我认为这只是滥用 OOP 构造来添加一些实用程序代码。
对于没有太多 XJC 插件经验的开发人员来说,编写自己的插件太复杂了。另外,我无法识别可以以某种方式概括的 "specific logic",所以这个选项甚至可能没有意义。