【发布时间】:2016-11-04 16:25:57
【问题描述】:
问题:
我有一个名为 Schema 的基类,它是抽象的,是一个非生成类。我有两个继承自 Schema 的生成 JAXB 类:FixedWidthSchema 和 DelimitedSchema。
我使用外部绑定 (xjb) 文件来指定 XSD 和 Java 类之间的映射。
在基类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 类 FixedWidthSchema 和 DelimitedSchema 中实现抽象方法(2、4、5 和 6)。两者都扩展了基类 Schema。因此,当我调用 Schema.isFixedWidth() 时,底层继承类将响应此调用并告诉调用者:真/假。只有派生类知道他们是谁:fixedwidth 或 delimited。
这是 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>
...
XJC 输出以下生成的类:
- DelimitedSchema 扩展了 Schema
- FixedWidthSchema 扩展了架构
- 语言环境
- 对象工厂
- 架构类型
SchemaType 类如下所示:
@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 生成的类中?我需要将特定代码放入每个生成的类的每个抽象方法中。
- 如何将生成的 SchemaType 类集成到 Schema 类中?换句话说:我希望 Schema 类被编组/解组,这会在 XML 文件中生成分隔或固定宽度的标签,并在 Schema 中拥有 SchemaType 的受保护成员。
- 是否可以将所有生成的类及其方法包设为私有?我想对用户屏蔽这些生成的类(而不是让它们成为公共 API 的一部分)
到目前为止,我还没有找到解决这些问题的方法。可能吗?还是我在寻找无法使用 JAXB 2.x 创建的不可能解决方案?
想要的解决方案:
我的解决方案思维朝着进行编组和解组的 Schema 类的方向发展,Schema 知道要编组/解组哪个模式。派生类实现了Schema类定义的需要实现的方法。
非常感谢任何正确方向的帮助/建议。
更新:
抽象 Schema 类(它是一个非生成类)与生成的类 FixedWidthSchema 和 DelimitedSchema 有关系。 DelimitedSchema is-a Schema,FixedWidthSchema is-a Schema。因此,Schema 接口(定义为抽象类)是用户需要拥有/使用的唯一接口。用户不需要知道 Schema 的内部细节,是 FixedWidth 还是 Delimited 模式对用户来说并不重要,只对我正在编写的代码而言。代码可以通过调用 isFixedWidth() 或 isDelimited() 方法来确定它是哪个模式。用户只需要有对接口 Schema 的引用,而不是对任何 FixedWidthSchema 或 DelimitedSchema 实例的引用。从用户的角度来看,这是隐藏的。我面临的问题是我可以从手写 Schema 类扩展生成的类 FixedWidthSchema 或 DelimitedSchema,但是现在手写模式不包含生成的 SchemaType 类,这是编组/解组
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;
}
现在内部代码可以访问转换模式的特定方法,从而处理该特定模式的 getter/setter。
通常,用户不需要知道架构的内部,因为代码本身将通过内部转换为“正确”类来处理固定宽度或定界之间的差异。
用户只对以下方法调用之一感兴趣:
// 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 类以 has-a 关系包含在 Schema 类中。我希望它是基/超类Schema与派生和生成的FixedWidthSchema或DelimitedSchema之间的一种关系。我认为这是可能的,至少在普通的 java 代码中是可能的,但问题是:如何使用 JAXB 生成的类来实现这一点?那是我还没有弄清楚如何做到这一点的部分。
我也很好奇如何编写一个 JAXB 插件,将类和方法修饰符的访问修饰符从公共修改为包私有。
感谢所有帮助,因为这对我来说是一个真正的 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>
fixedwidth 和 delimited schema 不一样!我省略了所有其他元素,以在 StackOverflow 上将示例 XML 文件保持在最低限度。
解决方案
AFAIK,我设法解决了我的代码和思维中的一些怪癖。这些是宝贵的经验教训:
不要创建非生成的抽象 java 基类/超类 JAXB 生成的类必须扩展自。所以,没有继承 在手写的java类和生成的类之间!
使用建议的 JAXB 代替继承,使用代码注入
用户 lexicore 的插件,如果真的需要的话!-
代码注入使用以下设置与 Maven 一起工作(请参阅
上面的配置上下文):-Xinject-code 不要使用继承或代码注入,而是防止使用这些 代码生成的类中尽可能多的机制。他们是 仅将生成的类作为数据查看/使用时不需要 容器。
区分业务数据类和业务逻辑 类。例如:模式类只保存数据 从模型类编组和解组。模型 类保存数据和数据的实际方法 由应用程序操纵。模式类是 JAXB 生成的 类。模型类是手写类。
使生成的类尽可能简单,并修改 生成的类最好使用外部绑定文件 (*.jxb)。 例如:重命名类名,指定 XmlAdapter 类 转换等。
克服不添加@XmlRootElement注解的问题 到基/根生成的类,确保你有 嵌入为匿名的基本/根元素的 complexType XSD 中的 complexType。请注意,@XMLRootElement 只会是 为顶级元素的匿名类型生成,而不是顶级类型!
听取更有经验的开发人员给您的好建议 在该特定领域(JAXB)。当解决方案是时,不要试图变得“聪明” 很简单。
结论:将业务逻辑从生成的代码中移开!
结果很简单(在DataModel类中):
- 在从 DataModel 派生的类中创建构造函数:public FixedWidthDataModel() 或 public DelimitedDataModel();
- public Writer marshal(),它将当前 DataModel 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
- public static DataModel unmarshal(Reader r),将提供的 Reader 输入解组为(FixedWidth 或 Delimited)DataModel 对象(输入 = XML 文件)。
- public void validate(),用于验证 DataModel。
- public boolean isFixedWidth(),表示DataModel是否为fixedwidth DataModel。
- public boolean isDelimited(),表示 DataModel 是否为分隔 DataModel。
所有生成的模式类现在仅在 DataModel 的 marshal() 和 unmarshal() 方法内部使用。这使得 DataModel API 独立于生成的类,因此在开发过程中稍后修改 XML 模式或生成的类时不会出现接口问题。
就是这样。
【问题讨论】:
标签: java inheritance jaxb generated-code abstract-methods