【问题标题】:Implementing abstract methods from abstract java class in generated jaxb class (inheritance)在生成的 jaxb 类中实现抽象 java 类的抽象方法(继承)
【发布时间】:2016-11-04 16:25:57
【问题描述】:

问题:

我有一个名为 Schema 的基类,它是抽象的,是一个非生成类。我有两个继承自 Schema 的生成 JAXB 类:FixedWidthSchema 和 DelimitedSchema。

我使用外部绑定 (xjb) 文件来指定 XSD 和 Java 类之间的映射。

在基类Schema中,我定义了几个方法:

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

我的问题是:

  1. 如何使用实现这些方法的外部绑定文件将 Schema 的 abstract 方法实现到 JAXB FixedWidthSchema 和 DelimitedSchema 生成的类中?我需要将特定代码放入每个生成的类的每个抽象方法中。
  2. 如何将生成的 SchemaType 类集成到 Schema 类中?换句话说:我希望 Schema 类被编组/解组,这会在 XML 文件中生成分隔或固定宽度的标签,并在 Schema 中拥有 SchemaType 的受保护成员。
  3. 是否可以将所有生成的类及其方法包设为私有?我想对用户屏蔽这些生成的类(而不是让它们成为公共 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 类,这是编组/解组 所必需的XML 源中的元素。如果没有生成的 SchemaType 类,我无法编组/解组 xml 数据。这就是为什么我正在寻找一种将 SchemaType 类“集成”到手写 Schema 类中的解决方案,以便我可以这样做:

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类中):

  1. 在从 DataModel 派生的类中创建构造函数:public FixedWidthDataModel() 或 public DelimitedDataModel();
  2. public Writer marshal(),它将当前 DataModel 对象(FixedWidth 或 Delimited)编组到 Writer 输出中。
  3. public static DataModel unmarshal(Reader r),将提供的 Reader 输入解组为(FixedWidth 或 Delimited)DataModel 对象(输入 = XML 文件)。
  4. public void validate(),用于验证 DataModel。
  5. public boolean isFixedWidth(),表示DataModel是否为fixedwidth DataModel。
  6. public boolean isDelimited(),表示 DataModel 是否为分隔 DataModel。

所有生成的模式类现在仅在 DataModel 的 marshal() 和 unmarshal() 方法内部使用。这使得 DataModel API 独立于生成的类,因此在开发过程中稍后修改 XML 模式或生成的类时不会出现接口问题。

就是这样。

【问题讨论】:

    标签: java inheritance jaxb generated-code abstract-methods


    【解决方案1】:

    对于 1,您可以使用 Code Injector 插件。见this question

    对于2,我没明白你所说的“将生成的SchemaType类集成到Schema类中”是什么意思。

    对于 3 - 是的,使用您自己的 XJC 插件,但可能有点难。 :)

    建议:仅将模式派生类用作 DTO,不要尝试在其中推送太多业务逻辑。

    更新

    仍然有点难以理解您想要实现的目标。你用所有这些“is-a”和“has-a”来解释你想为你的用户做什么,但仍然不清楚你所说的“集成”是什么意思。

    另一方面,整个故事归结为以下问题:

    现在生成了哪些代码,你想生成哪些代码?

    这是核心。如果你回答了这个问题,你就会得到可以回答的编程问题。现在您只需描述您的用例并期望有人为您设计解决方案。

    据我了解,您只希望您的架构派生类DelimitedSchemaFixedWidthSchema 实际实现(或扩展)您的基类Schema。那你为什么不这样做呢?使用xjc:extends(或JAXB 继承插件),您可以轻松地使DelimitedSchemaFixedWidthSchema 扩展Schema。您的Schema 类可能是一个抽象类,它定义了一些只能由特定实现实现的抽象方法。

    这可以通过使用代码注入器插件注入代码来完成。您只需将抽象方法的实现从 Schema 类注入到您的 DelimitedSchemaFixedWidthSchema 类中。然后这些类的实例可以作为Schema的实现返回给用户。

    让我感到困惑的是,您实际上已经知道所有这些元素。你知道xjc:extends,代码注入等等。缺少什么?

    最后,有几点建议。

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

    【讨论】:

    • 对于#2:我的意思是将生成的 SchemaType 类(表示分隔和固定宽度的 xml 标记)与我自己的“普通”java 类 Schema 集成,这样我就可以在这个上调用 marshall() -object 并调用静态 unmarshall() 为我提供从 Schema 类中创建的 Schema,该类包含分隔或固定宽度派生类。我希望我让你更清楚我的大脑在发生什么:)
    • @user504342 不,抱歉。显示你想要工作的代码。
    • 我刚刚更详细地更新了这个问题。我希望这可以更容易地理解我在非生成类和生成类之间的目标:将它们混合在一起,以便获得上述解决方案。
    • 我正在按照我的想法创建一个可行的解决方案......
    • @user504342 抱歉,我不打算进行冗长的无偿咨询。您不断地用更多细节来扩展您的问题,并且不费心接受/支持答案。
    猜你喜欢
    • 2013-08-25
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多