在生成的 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中,我定义了几个方法:

  1. public Schema static create(Model m),它从 提供型号。
  2. public abstract Writer marshal(),它将当前 Schema 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
  3. public Schema unmarshal(Reader r),它将提供的 Reader 输入解组到 Schema 对象(输入 = XML 文件)。
  4. public abstract void validate(),验证创建的Schema。
  5. public abstract boolean isFixedWidth(),指示创建的模式是否为固定宽度模式。
  6. 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 输出:

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] -------------------------------------------------------------

我遇到的问题是:

  1. 如何使用我在其中实现这些方法的外部绑定文件将 Schema 的 abstract 方法实现到 JAXB FixedWidthSchema 和 DelimitedSchema 生成的 classes 中?我需要为每个生成的 class.
  2. 将特定代码放入每个抽象方法中
  3. 如何将生成的 SchemaType class 集成到 Schema class 中?换句话说:我希望 Schema class 为 marshalled/unmarshalled,它在 XML 文件中生成定界或固定宽度的标记,并在 Schema 中拥有 SchemaType 的受保护成员。
  4. 是否可以将所有生成的 classes 及其方法包设为私有?我想对用户屏蔽这些生成的 classes(不让它们成为 public API)
  5. 的一部分

到目前为止,我还没有找到解决这些问题的办法。可能吗?或者我在寻找一个无法使用 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,我设法解决了我的代码和想法中的一些怪癖。这些是宝贵的经验教训:

结论:将业务逻辑从生成的代码中移开!

最终结果很简单(在 DataModel class 中):

  1. 在从 DataModel 派生的 classed 中创建构造函数:public FixedWidthDataModel(),或 public DelimitedDataModel();
  2. public Writer marshal(),它将当前 DataModel 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
  3. public static DataModel unmarshal(Reader r),它将提供的 Reader 输入解组到(固定宽度或定界的)DataModel 对象(输入 = XML 文件)。
  4. public void validate(),用于验证 DataModel。
  5. public boolean isFixedWidth(),它指示 DataModel 是否为固定宽度 DataModel。
  6. 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 DelimitedSchemaFixedWidthSchema 实际实现(或扩展)您的基础 class Schema.那你为什么不这样做呢?使用 xjc:extends(或 JAXB 继承插件),您可以轻松地使 DelimitedSchemaFixedWidthSchema 扩展 Schema。您的 Schema class 可能是一个抽象 class ,它定义了几个只能由特定实现实现的抽象方法。

这可以通过使用代码注入器插件注入代码来完成。您只需将来自 Schema class 的抽象方法的实现注入到您的 DelimitedSchemaFixedWidthSchema class 中。然后这些 classes 的实例可以作为 Schema.

的实现返回给用户

令我疑惑的是,你竟然已经知道了所有这些要素。你知道 xjc:extends、代码注入等等。缺少什么?

最后,提几点建议。

  • 正如我之前提到的,您最好仅将模式派生的 classes 用作 DTO。将模式派生代码与业务逻辑集成通常会导致无法维护的混乱。您最好干净地为您的业务建模 classes 并从 DTO to/from 中复制数据。这可能首先看起来更多的工作,但稍后会得到回报。例如,当您需要并行支持多个版本的交换模式时。您说 "normal code would be a piece of cake" 的事实是一种症状。您正在与代码生成作斗争,试图让它变得智能,但也许它应该保持愚蠢。
  • 最好不要将 unmarshal/marshal 方法移至业务 classes。将序列化与业务模型分开。实现一个单独的 SchemaReader 或类似的东西。