在生成的 jaxb class 中从 abstract java class 实现抽象方法(继承)
Implementing abstract methods from abstract java class in generated jaxb class (inheritance)
问题:
我有一个名为 Schema 的基础 class,它是抽象的并且是非生成的 class。我有两个生成的 JAXB classes 继承自 Schema:FixedWidthSchema 和 DelimitedSchema。
我使用外部绑定 (xjb) 文件来指定 XSD 和 Java classes 之间的映射。
在基础class Schema中,我定义了几个方法:
- public Schema static create(Model m),它从
提供型号。
- public abstract Writer marshal(),它将当前 Schema 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
- public Schema unmarshal(Reader r),它将提供的 Reader 输入解组到 Schema 对象(输入 = XML 文件)。
- public abstract void validate(),验证创建的Schema。
- public abstract boolean isFixedWidth(),指示创建的模式是否为固定宽度模式。
- public abstract boolean isDelimited(),指示创建的模式是否为定界模式。
我希望在生成的 jaxb classes FixedWidthSchema 和 DelimitedSchema 中实现抽象方法(2、4、5 和 6)。两者都扩展了基础 class 模式。因此,当我调用 Schema.isFixedWidth() 时,底层继承的 class 将应答此调用并告诉调用者:true/false。只有派生的 classes 知道它们是谁:固定宽度或定界。
这里是 XSD:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="schema" type="SchemaType"/>
<xs:complexType name="SchemaType">
<xs:choice>
<xs:element minOccurs="1" maxOccurs="1" name="delimited" type="DelimitedSchemaType"/>
<xs:element minOccurs="1" maxOccurs="1" name="fixedwidth" type="FixedWidthSchemaType"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="DelimitedSchemaType">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="locale" type="LocaleType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FixedWidthSchemaType">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="locale" type="LocaleType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="LocaleType">
<xs:attribute name="language" type="languageType" use="required"/>
<xs:attribute name="country" type="countryType" use="required"/>
<xs:attribute name="variant" type="variantType" use="optional"/>
</xs:complexType>
</xs:schema>
XML 模式包含两种选择:固定宽度或定界,两者都有语言环境类型。为清楚起见,省略了架构的其余部分。
绑定文件如下所示:
<?xml version="1.0" encoding="utf-8"?>
<jaxb:bindings jaxb:version="2.2" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://jaxb2-commons.dev.java.net/basic/inheritance"
schemaLocation="myschema.xsd" node="/xs:schema">
<jaxb:schemaBindings>
<jaxb:package name="org.mylib.schema"/>
</jaxb:schemaBindings>
<jaxb:bindings node="//xs:complexType[@name='DelimitedSchemaType']">
<jaxb:class name="DelimitedSchema"/>
<xjc:extends>org.mylib.schema.Schema</xjc:extends>
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='FixedWidthSchemaType']">
<jaxb:class name="FixedWidthSchema"/>
<xjc:extends>org.mylib.schema.Schema</xjc:extends>
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='LocaleType']">
<jaxb:class name="locale" />
</jaxb:bindings>
</jaxb:bindings>
我的 Maven pom.xml 文件如下所示:
...
<build>
<resources>
<resource>
<directory>${pom.basedir}/src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.13.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<specVersion>2.2</specVersion>
<schemaDirectory>src/main/resources</schemaDirectory>
<schemaIncludes>
<schemaInclude>myschema.xsd</schemaInclude>
</schemaIncludes>
<bindingDirectory>src/main/resources</bindingDirectory>
<bindingIncludes>
<bindingInclude>dataschema.xjb</bindingInclude>
</bindingIncludes>
<generateDirectory>${project.build.directory}/generated-sources/jaxb2</generateDirectory>
<extension>true</extension>
<args>
<arg>-Xinheritance</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>1.11.1</version>
</plugin>
</plugins>
<!-- Generate lots of output -->
<verbose>true</verbose>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
以下生成的 classes 由 XJC 输出:
- DelimitedSchema 扩展架构
- FixedWidthSchema 扩展架构
- 语言环境
- 对象工厂
- 架构类型
SchemaType class 看起来像这样:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SchemaType", propOrder = { "delimited", "fixedwidth"})
public class SchemaType {
protected DelimitedSchema delimited;
protected FixedWidthSchema fixedwidth;
public DelimitedSchema getDelimited() {
return delimited;
}
public void setDelimited(DelimitedSchema value) {
this.delimited = value;
}
public FixedWidthSchema getFixedwidth() {
return fixedwidth;
}
public void setFixedwidth(FixedWidthSchema value) {
this.fixedwidth = value;
}
}
现在不好的部分是,编译器 (XJC) 抱怨:
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] \mylib\target\generated-sources\jaxb2\org\mylib\schema\DelimitedSchema.java:[51,7] error: DelimitedSchema is not abstract and does not override abstract method isFixedWidth() in Schema
[ERROR] \mylib\target\generated-sources\jaxb2\org\mylib\schema\FixedWidthSchema.java:[51,7] error: FixedWidthSchema is not abstract and does not override abstract method isFixedWidth() in Schema
[INFO] 2 errors
[INFO] -------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] -------------------------------------------------------------
我遇到的问题是:
- 如何使用我在其中实现这些方法的外部绑定文件将 Schema 的 abstract 方法实现到 JAXB FixedWidthSchema 和 DelimitedSchema 生成的 classes 中?我需要为每个生成的 class.
将特定代码放入每个抽象方法中
- 如何将生成的 SchemaType class 集成到 Schema class 中?换句话说:我希望 Schema class 为 marshalled/unmarshalled,它在 XML 文件中生成定界或固定宽度的标记,并在 Schema 中拥有 SchemaType 的受保护成员。
- 是否可以将所有生成的 classes 及其方法包设为私有?我想对用户屏蔽这些生成的 classes(不让它们成为 public API)
的一部分
到目前为止,我还没有找到解决这些问题的办法。可能吗?或者我在寻找一个无法使用 JAXB 创建的不可能的解决方案 2.x?
想要的解决方案:
我的解决方案思路朝着执行编组和解组的架构 class 的方向发展,架构知道 marshal/unmarshal 哪个架构。 Derivedclasses实现了Schemaclass.
定义的需要实现的方法
任何方向正确的 help/suggestion 都将受到高度赞赏。
更新:
抽象架构class(这是一个非生成的class)与生成的classes FixedWidthSchema 和DelimitedSchema 有关系。 DelimitedSchema 是一个 Schema,FixedWidthSchema 是一个 Schema。因此,Schema 接口 - 定义为抽象 class - 是用户需要 have/work 的唯一接口。用户不需要知道 Schema 的内部细节,是 FixedWidth 还是 Delimited schema 对用户来说并不重要,只对我正在编写的代码而言。代码可以通过调用 isFixedWidth() 或 isDelimited() 方法来确定它是哪个架构。用户只需引用接口架构,而不是任何 FixedWidthSchema 或 DelimitedSchema 实例。从用户的角度来看,这是隐藏的。我面临的问题是,我可以从手写模式 class 扩展生成的 classes FixedWidthSchema 或 DelimitedSchema,但现在手写模式不包含生成的 SchemaType class,这是需要 marshal/unmarshal XML 源中的 或 元素。没有生成的 SchemaType class,我无法 marshal/unmarshal xml 数据。这就是为什么我正在寻找一种解决方案,将 SchemaType class "integrates" 转换为手写 Schema class,以便我可以这样做:
File f = new File("path/to/my/xmlfile.xml");
Reader r = new FileReader(f);
Schema sch = Schema.unmarshal(r);
// somewhere in other code parts:
if (sch.isDelimited()) {
DelimitedSchema delSch = (DelimitedSchema)sch;
}
// or...:
if (sch.isFixedWidth()) {
FixedWidthSchema fwSch = (FixedWidthSchema)sch;
}
现在内部代码可以获取转换模式的特定方法,从而解决该特定模式的 getters/setters。
通常,用户不需要了解架构的内部结构,因为代码本身会通过内部转换为 'right' class 来处理固定宽度或分隔宽度之间的差异。
用户只对这些方法调用之一感兴趣:
// To get a Schema instance (either fixedwidth or delimited) by unmarshalling a XML file.
Schema sch = Schema.unmarshal(aReader);
// Marshal the current schema instance to a XML file, using a Writer.
Writer wtr = sch.marshal();
// Create a schema instance by providing a Model on which the schema is based. This can be a fixedwidth or delimited Model.
Schema sch = Schema.create(m);
// Validate the schema if there are no errors.
sch.validate();
注意:我不希望 SchemaType class 以 has-a 关系包含在 Schema class 中。我希望它是 base/superclass 架构与派生和生成的 FixedWidthSchema 或 DelimitedSchema 之间的 is-a 关系。我认为这是可能的,至少在正常的 java 代码中是这样,但问题是:如何使用 JAXB 生成的 classes 来完成此操作?这是我还没有想出如何做到这一点的部分。
我也很好奇如何编写一个 JAXB 插件,将访问修饰符从 public 修改为包私有的 class 和方法修饰符。
感谢所有帮助,因为这对我来说是一个真正的 JAXB 难题。在普通(非生成的)代码中,这是小菜一碟。
额外:
分隔和固定宽度 XML 文件的内容。
分隔 XML:
<?xml version="1.0" encoding="utf-8"?>
<dataschema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<delimited> <-- should be handled in Schema.java and not in SchemaType.java
<locale language="en" country="en" />
</delimited>
</dataschema>
固定宽度XML:
<?xml version="1.0" encoding="utf-8"?>
<dataschema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fixedwidth> <-- should be handled in Schema.java and not in SchemaType.java
<locale language="en" country="en" />
</fixedwidth>
</dataschema>
固定宽度和定界模式不一样!我省略了所有其他元素,以将示例 XML 文件保持在 Whosebug 上这个问题的最低限度。
解决方案
AFAIK,我设法解决了我的代码和想法中的一些怪癖。这些是宝贵的经验教训:
不要创建非生成的摘要java base/superclass 其中
JAXB 生成的 classes 必须扩展自。所以,没有继承
在手写 java classes 和生成 classes!
之间
使用推荐的 JAXB 代码注入而不是继承
用户 lexicore 的插件,如果真的需要的话!
代码注入使用以下设置与 Maven 一起工作(参见
以上配置上下文):
<参数>
-Xinject-code
而不是继承或者代码注入,防止使用这些
代码生成的 classes 中尽可能多的机制。他们是
当 seeing/using 生成的 class 仅作为数据时不需要
容器.
区分业务数据classes和业务逻辑
classes。例如:schema classes ONLY hold the data to be
从模型编组和解组 classes。模型
classes 保存数据和数据的实际方法
由应用程序操纵。模式 classes 是 JAXB 生成的
classes。型号classes手写classes.
生成的class尽量简单,修改
最好使用外部绑定文件 (*.jxb) 生成 classes。
例如:重命名 class 名称,指定 XmlAdapter classes 为
转换等
克服没有添加@XmlRootElement注解的问题
到 base/root 生成的 class,确保你有
作为匿名嵌入的 base/root 元素的复杂类型
XSD 中的复杂类型。请注意,@XMLRootElement 只会是
为顶级元素的匿名类型生成,而不是顶级类型!
听听更有经验的开发人员给你的好建议
在那个特定领域(JAXB)。不要试图成为 'smart',当解决方案是
简单.
结论:将业务逻辑从生成的代码中移开!
最终结果很简单(在 DataModel class 中):
- 在从 DataModel 派生的 classed 中创建构造函数:public
FixedWidthDataModel(),或 public DelimitedDataModel();
- public Writer marshal(),它将当前 DataModel 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
- public static DataModel unmarshal(Reader r),它将提供的 Reader 输入解组到(固定宽度或定界的)DataModel
对象(输入 = XML 文件)。
- public void validate(),用于验证 DataModel。
- public boolean isFixedWidth(),它指示 DataModel 是否为固定宽度 DataModel。
- public boolean isDelimited(),指示 DataModel 是否为定界数据模型。
所有生成的模式 classes 现在仅在 DataModel 的 marshal() 和 unmarshal() 方法内部使用。这使得 DataModel API 独立于生成的 classes,因此当稍后在开发过程中修改 XML 模式或生成的 classes 时没有接口问题。
这就是所有人。
对于1,可以使用Code Injector插件。参见 this question
对于 2,我未能理解你所说的 "integrate the generated SchemaType class into the Schema class" 的意思。
对于 3 - 是的,使用您自己的 XJC 插件,但这可能有点困难。 :)
建议:仅将模式派生的 classes 用作 DTO,不要尝试在其中推送太多业务逻辑。
更新
你想要达到的目的还是有点难以理解。你用所有这些 "is-a" 和 "has-a" 解释了你想为你的用户做什么,但仍然不清楚你所说的 "integrate".
是什么意思
另一方面,整个故事归结为以下问题:
现在生成哪个代码,你想生成哪个代码?
这是核心。如果你回答这个问题,你就会得到可以回答的编程问题。现在您只需描述您的用例并期望有人为您设计解决方案。
据我了解,您只希望您的架构派生 classes DelimitedSchema
和 FixedWidthSchema
实际实现(或扩展)您的基础 class Schema
.那你为什么不这样做呢?使用 xjc:extends
(或 JAXB 继承插件),您可以轻松地使 DelimitedSchema
和 FixedWidthSchema
扩展 Schema
。您的 Schema
class 可能是一个抽象 class ,它定义了几个只能由特定实现实现的抽象方法。
这可以通过使用代码注入器插件注入代码来完成。您只需将来自 Schema
class 的抽象方法的实现注入到您的 DelimitedSchema
和 FixedWidthSchema
class 中。然后这些 classes 的实例可以作为 Schema
.
的实现返回给用户
令我疑惑的是,你竟然已经知道了所有这些要素。你知道 xjc:extends
、代码注入等等。缺少什么?
最后,提几点建议。
- 正如我之前提到的,您最好仅将模式派生的 classes 用作 DTO。将模式派生代码与业务逻辑集成通常会导致无法维护的混乱。您最好干净地为您的业务建模 classes 并从 DTO to/from 中复制数据。这可能首先看起来更多的工作,但稍后会得到回报。例如,当您需要并行支持多个版本的交换模式时。您说 "normal code would be a piece of cake" 的事实是一种症状。您正在与代码生成作斗争,试图让它变得智能,但也许它应该保持愚蠢。
- 最好不要将
unmarshal
/marshal
方法移至业务 classes。将序列化与业务模型分开。实现一个单独的 SchemaReader
或类似的东西。
问题:
我有一个名为 Schema 的基础 class,它是抽象的并且是非生成的 class。我有两个生成的 JAXB classes 继承自 Schema:FixedWidthSchema 和 DelimitedSchema。
我使用外部绑定 (xjb) 文件来指定 XSD 和 Java classes 之间的映射。
在基础class Schema中,我定义了几个方法:
- public Schema static create(Model m),它从 提供型号。
- public abstract Writer marshal(),它将当前 Schema 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
- public Schema unmarshal(Reader r),它将提供的 Reader 输入解组到 Schema 对象(输入 = XML 文件)。
- public abstract void validate(),验证创建的Schema。
- public abstract boolean isFixedWidth(),指示创建的模式是否为固定宽度模式。
- public abstract boolean isDelimited(),指示创建的模式是否为定界模式。
我希望在生成的 jaxb classes FixedWidthSchema 和 DelimitedSchema 中实现抽象方法(2、4、5 和 6)。两者都扩展了基础 class 模式。因此,当我调用 Schema.isFixedWidth() 时,底层继承的 class 将应答此调用并告诉调用者:true/false。只有派生的 classes 知道它们是谁:固定宽度或定界。
这里是 XSD:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="schema" type="SchemaType"/>
<xs:complexType name="SchemaType">
<xs:choice>
<xs:element minOccurs="1" maxOccurs="1" name="delimited" type="DelimitedSchemaType"/>
<xs:element minOccurs="1" maxOccurs="1" name="fixedwidth" type="FixedWidthSchemaType"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="DelimitedSchemaType">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="locale" type="LocaleType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FixedWidthSchemaType">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="locale" type="LocaleType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="LocaleType">
<xs:attribute name="language" type="languageType" use="required"/>
<xs:attribute name="country" type="countryType" use="required"/>
<xs:attribute name="variant" type="variantType" use="optional"/>
</xs:complexType>
</xs:schema>
XML 模式包含两种选择:固定宽度或定界,两者都有语言环境类型。为清楚起见,省略了架构的其余部分。
绑定文件如下所示:
<?xml version="1.0" encoding="utf-8"?>
<jaxb:bindings jaxb:version="2.2" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://jaxb2-commons.dev.java.net/basic/inheritance"
schemaLocation="myschema.xsd" node="/xs:schema">
<jaxb:schemaBindings>
<jaxb:package name="org.mylib.schema"/>
</jaxb:schemaBindings>
<jaxb:bindings node="//xs:complexType[@name='DelimitedSchemaType']">
<jaxb:class name="DelimitedSchema"/>
<xjc:extends>org.mylib.schema.Schema</xjc:extends>
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='FixedWidthSchemaType']">
<jaxb:class name="FixedWidthSchema"/>
<xjc:extends>org.mylib.schema.Schema</xjc:extends>
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='LocaleType']">
<jaxb:class name="locale" />
</jaxb:bindings>
</jaxb:bindings>
我的 Maven pom.xml 文件如下所示:
...
<build>
<resources>
<resource>
<directory>${pom.basedir}/src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.13.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<specVersion>2.2</specVersion>
<schemaDirectory>src/main/resources</schemaDirectory>
<schemaIncludes>
<schemaInclude>myschema.xsd</schemaInclude>
</schemaIncludes>
<bindingDirectory>src/main/resources</bindingDirectory>
<bindingIncludes>
<bindingInclude>dataschema.xjb</bindingInclude>
</bindingIncludes>
<generateDirectory>${project.build.directory}/generated-sources/jaxb2</generateDirectory>
<extension>true</extension>
<args>
<arg>-Xinheritance</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>1.11.1</version>
</plugin>
</plugins>
<!-- Generate lots of output -->
<verbose>true</verbose>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
以下生成的 classes 由 XJC 输出:
- DelimitedSchema 扩展架构
- FixedWidthSchema 扩展架构
- 语言环境
- 对象工厂
- 架构类型
SchemaType class 看起来像这样:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SchemaType", propOrder = { "delimited", "fixedwidth"})
public class SchemaType {
protected DelimitedSchema delimited;
protected FixedWidthSchema fixedwidth;
public DelimitedSchema getDelimited() {
return delimited;
}
public void setDelimited(DelimitedSchema value) {
this.delimited = value;
}
public FixedWidthSchema getFixedwidth() {
return fixedwidth;
}
public void setFixedwidth(FixedWidthSchema value) {
this.fixedwidth = value;
}
}
现在不好的部分是,编译器 (XJC) 抱怨:
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] \mylib\target\generated-sources\jaxb2\org\mylib\schema\DelimitedSchema.java:[51,7] error: DelimitedSchema is not abstract and does not override abstract method isFixedWidth() in Schema
[ERROR] \mylib\target\generated-sources\jaxb2\org\mylib\schema\FixedWidthSchema.java:[51,7] error: FixedWidthSchema is not abstract and does not override abstract method isFixedWidth() in Schema
[INFO] 2 errors
[INFO] -------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] -------------------------------------------------------------
我遇到的问题是:
- 如何使用我在其中实现这些方法的外部绑定文件将 Schema 的 abstract 方法实现到 JAXB FixedWidthSchema 和 DelimitedSchema 生成的 classes 中?我需要为每个生成的 class. 将特定代码放入每个抽象方法中
- 如何将生成的 SchemaType class 集成到 Schema class 中?换句话说:我希望 Schema class 为 marshalled/unmarshalled,它在 XML 文件中生成定界或固定宽度的标记,并在 Schema 中拥有 SchemaType 的受保护成员。
- 是否可以将所有生成的 classes 及其方法包设为私有?我想对用户屏蔽这些生成的 classes(不让它们成为 public API) 的一部分
到目前为止,我还没有找到解决这些问题的办法。可能吗?或者我在寻找一个无法使用 JAXB 创建的不可能的解决方案 2.x?
想要的解决方案:
我的解决方案思路朝着执行编组和解组的架构 class 的方向发展,架构知道 marshal/unmarshal 哪个架构。 Derivedclasses实现了Schemaclass.
定义的需要实现的方法任何方向正确的 help/suggestion 都将受到高度赞赏。
更新:
抽象架构class(这是一个非生成的class)与生成的classes FixedWidthSchema 和DelimitedSchema 有关系。 DelimitedSchema 是一个 Schema,FixedWidthSchema 是一个 Schema。因此,Schema 接口 - 定义为抽象 class - 是用户需要 have/work 的唯一接口。用户不需要知道 Schema 的内部细节,是 FixedWidth 还是 Delimited schema 对用户来说并不重要,只对我正在编写的代码而言。代码可以通过调用 isFixedWidth() 或 isDelimited() 方法来确定它是哪个架构。用户只需引用接口架构,而不是任何 FixedWidthSchema 或 DelimitedSchema 实例。从用户的角度来看,这是隐藏的。我面临的问题是,我可以从手写模式 class 扩展生成的 classes FixedWidthSchema 或 DelimitedSchema,但现在手写模式不包含生成的 SchemaType class,这是需要 marshal/unmarshal XML 源中的
File f = new File("path/to/my/xmlfile.xml");
Reader r = new FileReader(f);
Schema sch = Schema.unmarshal(r);
// somewhere in other code parts:
if (sch.isDelimited()) {
DelimitedSchema delSch = (DelimitedSchema)sch;
}
// or...:
if (sch.isFixedWidth()) {
FixedWidthSchema fwSch = (FixedWidthSchema)sch;
}
现在内部代码可以获取转换模式的特定方法,从而解决该特定模式的 getters/setters。
通常,用户不需要了解架构的内部结构,因为代码本身会通过内部转换为 'right' class 来处理固定宽度或分隔宽度之间的差异。
用户只对这些方法调用之一感兴趣:
// To get a Schema instance (either fixedwidth or delimited) by unmarshalling a XML file.
Schema sch = Schema.unmarshal(aReader);
// Marshal the current schema instance to a XML file, using a Writer.
Writer wtr = sch.marshal();
// Create a schema instance by providing a Model on which the schema is based. This can be a fixedwidth or delimited Model.
Schema sch = Schema.create(m);
// Validate the schema if there are no errors.
sch.validate();
注意:我不希望 SchemaType class 以 has-a 关系包含在 Schema class 中。我希望它是 base/superclass 架构与派生和生成的 FixedWidthSchema 或 DelimitedSchema 之间的 is-a 关系。我认为这是可能的,至少在正常的 java 代码中是这样,但问题是:如何使用 JAXB 生成的 classes 来完成此操作?这是我还没有想出如何做到这一点的部分。
我也很好奇如何编写一个 JAXB 插件,将访问修饰符从 public 修改为包私有的 class 和方法修饰符。
感谢所有帮助,因为这对我来说是一个真正的 JAXB 难题。在普通(非生成的)代码中,这是小菜一碟。
额外:
分隔和固定宽度 XML 文件的内容。
分隔 XML:
<?xml version="1.0" encoding="utf-8"?>
<dataschema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<delimited> <-- should be handled in Schema.java and not in SchemaType.java
<locale language="en" country="en" />
</delimited>
</dataschema>
固定宽度XML:
<?xml version="1.0" encoding="utf-8"?>
<dataschema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fixedwidth> <-- should be handled in Schema.java and not in SchemaType.java
<locale language="en" country="en" />
</fixedwidth>
</dataschema>
固定宽度和定界模式不一样!我省略了所有其他元素,以将示例 XML 文件保持在 Whosebug 上这个问题的最低限度。
解决方案
AFAIK,我设法解决了我的代码和想法中的一些怪癖。这些是宝贵的经验教训:
不要创建非生成的摘要java base/superclass 其中 JAXB 生成的 classes 必须扩展自。所以,没有继承 在手写 java classes 和生成 classes!
之间
使用推荐的 JAXB 代码注入而不是继承
用户 lexicore 的插件,如果真的需要的话!代码注入使用以下设置与 Maven 一起工作(参见
以上配置上下文):<参数>
-Xinject-code
而不是继承或者代码注入,防止使用这些 代码生成的 classes 中尽可能多的机制。他们是 当 seeing/using 生成的 class 仅作为数据时不需要 容器.
区分业务数据classes和业务逻辑 classes。例如:schema classes ONLY hold the data to be 从模型编组和解组 classes。模型 classes 保存数据和数据的实际方法 由应用程序操纵。模式 classes 是 JAXB 生成的 classes。型号classes手写classes.
生成的class尽量简单,修改 最好使用外部绑定文件 (*.jxb) 生成 classes。 例如:重命名 class 名称,指定 XmlAdapter classes 为 转换等
克服没有添加@XmlRootElement注解的问题 到 base/root 生成的 class,确保你有 作为匿名嵌入的 base/root 元素的复杂类型 XSD 中的复杂类型。请注意,@XMLRootElement 只会是 为顶级元素的匿名类型生成,而不是顶级类型!
听听更有经验的开发人员给你的好建议 在那个特定领域(JAXB)。不要试图成为 'smart',当解决方案是 简单.
结论:将业务逻辑从生成的代码中移开!
最终结果很简单(在 DataModel class 中):
- 在从 DataModel 派生的 classed 中创建构造函数:public FixedWidthDataModel(),或 public DelimitedDataModel();
- public Writer marshal(),它将当前 DataModel 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
- public static DataModel unmarshal(Reader r),它将提供的 Reader 输入解组到(固定宽度或定界的)DataModel 对象(输入 = XML 文件)。
- public void validate(),用于验证 DataModel。
- public boolean isFixedWidth(),它指示 DataModel 是否为固定宽度 DataModel。
- public boolean isDelimited(),指示 DataModel 是否为定界数据模型。
所有生成的模式 classes 现在仅在 DataModel 的 marshal() 和 unmarshal() 方法内部使用。这使得 DataModel API 独立于生成的 classes,因此当稍后在开发过程中修改 XML 模式或生成的 classes 时没有接口问题。
这就是所有人。
对于1,可以使用Code Injector插件。参见 this question
对于 2,我未能理解你所说的 "integrate the generated SchemaType class into the Schema class" 的意思。
对于 3 - 是的,使用您自己的 XJC 插件,但这可能有点困难。 :)
建议:仅将模式派生的 classes 用作 DTO,不要尝试在其中推送太多业务逻辑。
更新
你想要达到的目的还是有点难以理解。你用所有这些 "is-a" 和 "has-a" 解释了你想为你的用户做什么,但仍然不清楚你所说的 "integrate".
是什么意思另一方面,整个故事归结为以下问题:
现在生成哪个代码,你想生成哪个代码?
这是核心。如果你回答这个问题,你就会得到可以回答的编程问题。现在您只需描述您的用例并期望有人为您设计解决方案。
据我了解,您只希望您的架构派生 classes DelimitedSchema
和 FixedWidthSchema
实际实现(或扩展)您的基础 class Schema
.那你为什么不这样做呢?使用 xjc:extends
(或 JAXB 继承插件),您可以轻松地使 DelimitedSchema
和 FixedWidthSchema
扩展 Schema
。您的 Schema
class 可能是一个抽象 class ,它定义了几个只能由特定实现实现的抽象方法。
这可以通过使用代码注入器插件注入代码来完成。您只需将来自 Schema
class 的抽象方法的实现注入到您的 DelimitedSchema
和 FixedWidthSchema
class 中。然后这些 classes 的实例可以作为 Schema
.
令我疑惑的是,你竟然已经知道了所有这些要素。你知道 xjc:extends
、代码注入等等。缺少什么?
最后,提几点建议。
- 正如我之前提到的,您最好仅将模式派生的 classes 用作 DTO。将模式派生代码与业务逻辑集成通常会导致无法维护的混乱。您最好干净地为您的业务建模 classes 并从 DTO to/from 中复制数据。这可能首先看起来更多的工作,但稍后会得到回报。例如,当您需要并行支持多个版本的交换模式时。您说 "normal code would be a piece of cake" 的事实是一种症状。您正在与代码生成作斗争,试图让它变得智能,但也许它应该保持愚蠢。
- 最好不要将
unmarshal
/marshal
方法移至业务 classes。将序列化与业务模型分开。实现一个单独的SchemaReader
或类似的东西。