java - 从生成的jaxb类中的抽象Java类实现抽象方法(继承)

标签 java inheritance jaxb generated-code abstract-methods

问题:

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

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

在基类Schema中,我定义了一些方法:


public Sc​​hema static create(Model m),它从
提供的模型。
公共抽象Writer marshal(),它将当前的Schema对象(FixedWidth或Delimited)编组为Writer输出。
public Sc​​hema unmarshal(Reader r),它将已提供的Reader输入解组到Schema对象(input = XML文件)。
公共抽象void validate(),用于验证创建的模式。
公共抽象布尔值isFixedWidth(),它指示创建的模式是否为固定宽度模式。
public abstract boolean isDelimited(),它指示创建的模式是否是带分隔符的模式。


我希望在生成的jaxb类FixedWidthSchema和DelimitedSchema中实现抽象方法(2、4、5和6)。两者都扩展了基类Schema。因此,当我调用Schema.isFixedWidth()时,基础继承的类将应答此调用并告诉调用者:true / false。只有派生类知道他们是谁: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模式包含两个选择:fixedwidth或delimited,都具有语言环境类型。为了清楚起见,省略了该架构的其余部分。

绑定文件如下所示:

<?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的抽象方法实现到JAXB FixedWidthSchema和DelimitedSchema生成的类中?我需要为每个生成的类在每个抽象方法中放入特定的代码。
如何将生成的SchemaType类集成到Schema类中?换句话说:我希望对Schema类进行编组/解组,以在XML文件中生成带分隔符或固定宽度的标记,并在Schema中具有SchemaType的受保护成员。
是否有可能使所有生成的类及其方法包私有?我想对用户屏蔽这些生成的类(不使其成为公共API的一部分)


到目前为止,我还没有找到解决这些问题的方法。可能吗?还是我在寻找无法使用JAXB 2.x创建的不可能的解决方案?

想要的解决方案:

我的解决方案思想朝着进行编组和解组的Schema类的方向发展,Schema知道要编组/解组的方案。派生类实现需要由Schema类定义的方法。

朝正确方向提供的任何帮助/建议都受到高度赞赏。

更新:

抽象的Schema类(这是一个非生成的类)与生成的类FixedWidthSchema和DelimitedSchema有关系。 DelimitedSchema是-架构,FixedWidthSchema是-架构。因此,定义为抽象类的Schema接口是用户需要使用/使用的唯一接口。用户不需要知道架构的内部细节,无论是固定宽度架构还是定界架构,对于用户而言并不重要,仅对于我正在编写的代码而言。该代码可以通过调用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;
}


现在,内部代码可以访问强制转换模式的特定方法,从而解决该特定模式的获取者/设置者。

通常,用户不需要了解架构的内部,因为代码本身将通过内部转换为“正确的”类来处理固定宽度或定界之间的差异。

用户仅对以下方法调用之一感兴趣:

// 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类中。我希望它是基类/超类架构与派生和生成的FixedWidthSchema或DelimitedSchema之间的is-关系。我认为至少在普通的Java代码中是可行的,但问题是:如何使用JAXB生成的类来实现这一点?这是我还没有弄清楚如何执行此操作的部分。

我也很好奇如何编写一个JAXB插件,该插件将类和方法修饰符的访问修饰符从public修改为package-private。

感谢您的所有帮助,因为这对我来说是一个真正的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>


FixedWidth 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和定界模式不一样!我省略了所有其他元素,以使示例XML文件在StackOverflow上的此问题保持在最低限度。

解决方案

AFAIK,我设法在代码和思想中解决了一些怪癖。这些是宝贵的经验教训:


不要创建非生成的抽象Java基类/超类
JAXB生成的类必须从此扩展。所以,没有继承
在手写的Java类和生成的类之间!
代替继承,使用建议的JAXB使用代码注入
如果确实需要,请使用lexicore用户提供的插件!
使用以下设置,代码注入可与Maven一起使用(请参阅
以上为配置上下文):

<参数>
-Xinject代码

禁止使用这些而不是继承或代码注入
在代码生成的类中使用尽可能多的机制。他们是
仅查看/使用生成的类作为数据时不需要
容器。
区分业务数据类和业务逻辑
类。例如:模式类仅保存要
在模型类之间进行编组和解组。模型
类保存数据以及数据所使用的实际方法
由应用程序操纵。模式类是JAXB生成的
类。模型类是手写类。
使生成的类尽可能简单,然后修改
生成的类,最好使用外部绑定文件(* .jxb)。
例如:重命名类名称,为XmlAdapter类指定
转换等
克服不添加@XmlRootElement批注的问题
到基/根生成的类,请确保您具有
作为匿名嵌入的base / root元素的complexType
XSD中的complexType。请注意,@ XMLRootElement将仅
为顶级元素的匿名类型而不是顶级类型生成的!
聆听经验丰富的开发人员提供给您的好建议
在该特定字段(JAXB)中。解决方案是时,不要试图变得“聪明”
简单。


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

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


在派生类中从DataModel创建构造函数:public
FixedWidthDataModel()或公共DelimitedDataModel();
public Writer marshal(),它将当前的DataModel对象(FixedWidth或Delimited)编组到Writer输出中。
公共静态DataModel unmarshal(Reader r),它将提供的Reader输入解组到(FixedWidth或Delimited)DataModel
对象(输入= XML文件)。
public void validate(),用于验证DataModel。
public boolean isFixedWidth(),它指示DataModel是否为固定宽度的DataModel。
public boolean isDelimited(),它指示DataModel是否为带分隔符的DataModel。


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

那是所有人。

最佳答案

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

对于2,我无法理解“将生成的SchemaType类集成到Schema类中”的含义。

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

一个建议:将模式派生的类仅用作DTO,不要尝试在其中施加过多的业务逻辑。

更新资料

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

另一方面,整个故事简化为以下问题:

现在生成哪些代码,并且您想生成哪些代码?

那是核心。如果回答了这个问题,您将遇到可以回答的编程问题。现在,您仅描述您的用例,并期望有人为您设计解决方案。

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

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

让我感到困惑的是,您实际上已经知道所有这些要素。您了解xjc:extends,代码注入等。有什么不见了?

最后,一些建议。


如前所述,您最好将模式派生的类仅用作DTO。将模式派生的代码与业务逻辑集成在一起通常会导致无法维护的混乱。您最好对业务类进行干净的建模,并从DTO向它们复制数据。这似乎首先需要更多工作,但稍后会有所收获。例如,当您需要并行支持多个版本的交换架构时。您说“正常代码将是小菜一碟”的事实是一种症状。您正在努力使代码生成变得智能化,但也许应该让它变得愚蠢。
最好不要将unmarshal / marshal方法移至业务类。将序列化与业务模型分开。实现单独的SchemaReader或类似的东西。

关于java - 从生成的jaxb类中的抽象Java类实现抽象方法(继承),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40427492/

相关文章:

java - 使用org.apache.hadoop DistributedFileSystem时,线程 “main” java.lang.NullPointerException中的异常

java - 2 个 POJO 类扩展了一个基类,其中每个类都映射到唯一的表。为什么 hibernate 查询在“字段列表错误”中创建​​未知列 ''?

java - Selenium (Java): ordered list of webelements but with nested divs

java - 如何使整数具有可比性

python - 设计异常继承的标准方法是什么?

c++ - 最佳实践 : Passing a Vector of a Derived Class to a Method on a Vector of a Base Class

jaxb - 如果名称包含 - 则无法使用 JAXB 解析 xml

java - JAXB2 将 xmlns 添加到 XML 输出

java - 日志对象到xml的输出不正确

java - @Pathvariable 找不到编译错误