当前位置: 首页 > 知识库问答 >
问题:

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

姜正初
2023-03-14

问题是:

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

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

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

  1. 公共架构静态创建(Model m),它从提供的模型创建架构。
  2. 公共抽象Writer marshal(),它将当前架构对象(FixedWidth或Delimited)封送到Writer输出中。
  3. 公共架构反封送(Reader r),它将提供的读取器输入反封送到架构对象(input=XML文件)。
  4. 公共抽象void validate(),用于验证创建的架构。
  5. 公共抽象布尔值isFixedWidth(),它指示创建的架构是否为fixedwidth架构。
  6. 公共抽象布尔值isDelimited(),它指示创建的架构是否为分隔架构。

我希望抽象方法(2,4,5和6)在生成的jaxb类FixedWidthSchema和DelimitedSchema中实现。两者都扩展了基类架构。因此,当我调用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扩展架构
  • 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的抽象方法实现到JAXB FixedWidthSchema和DelimitedSchema生成的类中?我需要将特定的代码放入每个生成类的每个抽象方法中。
  2. 如何将生成的SchemaType类集成到Schema类中?换句话说:我希望对Schema类进行封送/反封送,这样会在XML文件中生成delimited或fixedwidth标记,并在Schema中使用SchemaType的受保护成员。
  3. 是否可能使所有生成的类及其方法包私有?我想屏蔽这些生成的类,不让用户知道(而不是让它们成为公共API的一部分)

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

所需的解决方案:

我的解决方案--考虑的方向是执行封送和解封的Schema类,模式知道要封送/解封哪个模式。派生类实现了Schema类定义的需要实现的方法。

任何方向正确的帮助/建议都非常感谢。

更新:

抽象模式类(它是非生成的类)与生成的类FixedWidthSchema和DelimitedSchema有关系。DelimitedSchema是一个模式,FixedWidthSchema是一个模式。因此,模式接口--定义为抽象类--是用户需要拥有/使用的唯一接口。用户不需要知道模式的内部细节,它是FixedWidth还是Delimited模式对用户并不重要,只对我正在编写的代码重要。代码可以通过调用isFixedWidth()或isDelimited()方法来确定它是哪个模式。用户只需要引用接口架构,而不需要引用任何FixedWidthSchema或DelimitedSchema实例。在用户视点中隐藏的。我面临的问题是,我可以从手写的Schema类扩展生成的类FixedWidthSchema或DelimitedSchema,但现在手写的Schema不包含生成的SchemaType类,封送/解封送XML源中的 元素所需的SchemaType类。如果没有生成的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;
}

现在,内部代码可以获得所转换模式的特定方法,从而寻址该特定模式的getters/setters。

通常,用户不需要知道模式的内部内容,因为代码本身将通过内部转换为“Right”类来处理fixedwidth或delimited之间的差异。

用户只对这些方法调用中的一个感兴趣:

// 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-a关系。我认为这是可能的,至少在正常的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和delimited架构不相同!我省略了所有其他元素,以使示例XML文件在StackOverflow中的问题达到最低限度。

解决方案

我设法解决了代码和思维中的一些怪癖。这些是吸取的宝贵经验教训:

>

  • 不要创建JAXB生成的类必须从其扩展的非生成的抽象Java基/超类。所以,手写的Java类和生成的类之间没有遗传!

    如果真的需要,可以使用建议的JAXB
    插件由user lexicore进行代码注入,而不是继承

    代码注入使用以下设置与Maven一起工作(有关配置上下文,请参阅上面的
    ):


     · ·  -xinject-code

    而不是继承或代码注入,在代码生成的类中尽可能地阻止使用这些机制。当看到/使用生成的类仅作为数据容器时,就不需要它们了。

    对业务数据类和业务逻辑类进行区分。例如:模式类只保存要从模型类封送和解封的数据。模型类保存数据和应用程序操作数据的实际方法。模式类是JAXB生成的类。模型类是手写的类。

    使生成的类尽可能简单,最好用外部绑定文件(*.jxb)修改生成的类。例如:重命名类名、指定用于转换的XmlAdapter类等。

    为了克服不能将@XmlRootElement注释添加到生成的基/根类中的问题,请确保将基/根元素的complexType作为匿名complexType嵌入到XSD中。注意,@XmlRootElement将只为顶级元素的匿名类型生成,而不是顶级类型!

    在特定领域(JAXB)中更有经验的开发人员提供给您的建议时,请听取good advise(好建议)。当解决方法很简单的时候,不要试图变得“聪明”。

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

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

    1. 在从DataModel派生类中创建构造函数:public FixedWidthDataModel()或public DelimitedDataModel();
    2. 公共Writer marshal(),它将当前DataModel对象(FixedWidth或Delimited)封送到Writer输出中。
    3. 公共静态数据模型反封送(Reader r),它将提供的读取器输入反封送到(FixedWidth或Delimited)数据模型对象(input=XML文件)。
    4. public void validate(),用于验证数据模型。
    5. 公共布尔值isFixedWidth(),它指示数据模型是否为fixedwidth数据模型。
    6. 公共布尔值isDelimited(),它指示数据模型是否为已定界的数据模型。

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

    那都是人。

  • 共有1个答案

    红经亘
    2023-03-14

    对于1,您可以使用代码注入器插件。请参阅此问题

    对于2,我没能理解您所说的“将生成的SchemaType类集成到Schema类中”是什么意思。

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

    一个建议:只将模式派生的类用作DTO,不要试图在那里推进太多的业务逻辑。

    更新

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

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

    现在生成哪些代码,您希望生成哪些代码?

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

    根据我的理解,您只是希望您的模式派生的类delimitedschemafixedwidthschema实际实现(或扩展)您的基类schema。那你为什么不这么做呢?通过xjc:extends(或JAXB继承插件),您可以轻松地使delimitedschemafixedwidthschema扩展schema。您的schema类可能是一个抽象类,它定义了几个只能由特定实现来实现的抽象方法。

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

    令我不解的是,你其实已经知道了所有这些元素。您了解xjc:extends、代码注入等等。少了什么?

    最后提出几点建议。

    • 正如我前面提到的,您最好只将模式派生的类用作DTO。将架构派生的代码与业务逻辑集成常常会导致不可维护的混乱。您最好清晰地为业务类建模,并将数据从DTO复制到DTO。这可能看起来是先做更多的工作,但以后会有回报。例如,当您需要并行支持多个版本的exchange架构时。你说“普通代码是小菜一碟”的事实就是一个征兆。您正在与代码生成作斗争,试图使其智能化,但也许它应该是哑的。
    • 最好不要将unmarshal/marshal方法移动到业务类。将序列化与业务模型分开。实现单独的SchemaReader或类似的东西。
     类似资料:
    • 是否有任何JAXB绑定可以告诉JAXB代码生成器将Java类生成为,而不必在XSD中将相应的XML类型标记为? 情况如下: > 我在xsd中定义架构: 我使用内联JAXB绑定(“inline”==“直接在模式中”)来指示应该生成JAXB类的包(): 我使用内联JAXB绑定为我的每个复杂类型(在本例中、和)指示实现类的名称: 我从模式生成JAXB类。这导致: 我自己编写类: 使用这两个类层次结构这样

    • 我目前正在尝试使用JaxB,但对于一个相对简单的示例,我并不是很成功。我的示例如下: 我所有的尝试(甚至编组)都失败了。我浏览了BlaiseDoughan的博客,包括http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html但他们似乎对我的例子都没有帮助。我很可能误用了他的例子。在我看来,我的示例应该是JaxB中

    • 继承一个可以实现的普通超类有什么不同吗? atm我有一个名为的抽象类,我有/扩展这个。 abstractcar中的一个字段设置为"私有int容量" 但是在子类“小型汽车”中,当我输入“容量”作为构造函数中使用的变量时,它说“容量在抽象汽车中有私有访问” 我想: > 子类继承超类的所有字段和方法? 我现在该怎么进行?

    • 我有一个抽象类,这个类是在她的子类中扩展的: < li >我在这个抽象类上实现了一个方法,并抽象了另一个方法 < li >实现的方法是每个子类对象都必须访问的通用方法。所以我决定在抽象类上实现它,避免在每个子类上实现相同的方法。 小例子: 我想听听你对这种实施方式的看法, 问候。

    • 问题内容: 我正在尝试使用JAXB解组一些XML,但出现“无法创建…的实例”异常。我知道为什么- 它试图建立一个抽象类的实例。我想要的是让它成为特定实现类的实例。我的目标是对setter方法进行特定于类的检查。对于BarImpl,也许“ qux”是有效的baz值,但BarImpl2想要做其他事情。 我没有通过注释Foo来实现这一目标,但是如果我不注释bar,事情就会变得很丑。 问题答案: 您可以执

    • 本文向大家介绍php中的抽象方法和抽象类,包括了php中的抽象方法和抽象类的使用技巧和注意事项,需要的朋友参考一下 1、什么是抽象方法? 我们在类里面定义的没有方法提的方法就是抽象方法。所谓的没有方法体指的是,在声明的时候没有大括号以及其中的内容,而是直接在声明时在方法名后加上分号结束,另外在声明抽象方法时方法还要加一个关键字"abstract"来修饰。 例如: 2、什么是抽象类? 只要一个类里面