JAXB 绑定以从非抽象 XML 类型生成抽象 Java class

JAXB binding to generate an abstract Java class from a non-abstract XML type

简而言之

是否有任何 JAXB 绑定可以告诉 JAXB 代码生成器生成 Java class 作为 abstract 而不必标记相应的 XML 在 XSD?

中键入 abstract

描述

情况如下:

我这样做的原因是,使用这 2 个 class 层次结构,这样我就可以将生成的代码 (my.package.jaxb.*) 与手册 (my.package.impl.*) 分开。这样,当 XSD 发生变化时,我可以重新生成 my.package.jaxb.* classes 并在我的手册 my.package.impl.* classes 中进行一些更改以合并新行为。

到目前为止一切顺利。问题是在 MyAbstractClass 中我想定义一个 abstract 方法...

protected abstract void doSomething();

...然后由 MyAClassMyBClass 以不同方式实现。 然而,生成的 MyATypeMyBType classes 现在有编译错误,因为 它们没有被声明为抽象,但它们现在继承了一个抽象方法 (请注意,它们都扩展了 MyAbstractClass).

我不能在 XSD (abstract="true") 中将它们声明为抽象的,因为这样做会在我声明类型为 myAType 或 [=38 的元素时导致以下错误=] 在 XML:

cvc-type.2: The type definition cannot be abstract for element someElementName.

我想要的是使用一些 JAXB 绑定来告诉 JAXB 代码生成器生成 classes MyATypeMyBType 作为 abstract 而不必将 XML 类型标记为 abstract。有这样的绑定吗?至今没找到。

抱歉解释冗长,提前致谢。

我最终创建了一个 XJC plugin。这是代码:

import java.lang.reflect.Method;

import javax.xml.namespace.QName;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

import com.sun.codemodel.JMod;
import com.sun.codemodel.JMods;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.Outline;
import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIDeclaration;
import com.sun.tools.xjc.reader.xmlschema.bindinfo.BindInfo;
import com.sun.xml.xsom.XSAnnotation;

public class AbstractModifierPlugin extends com.sun.tools.xjc.Plugin {

    private static final QName ABSTRACT_QNAME = new QName("http://www.example.com/jaxb/abstract-modifier/1-0", "abstract");

    private static final String SET_FLAG_METHOD_NAME = "setFlag";

    private static final String OPTION_NAME = "Xabstract-modifier";

    @Override
    public String getOptionName() {
        return OPTION_NAME;
    }

    @Override
    public String getUsage() {
        return " -" + OPTION_NAME + " : marks as abstract the generated classes corresponding to XML types marked with "
                + "<xs:annotation><xs:appinfo><" + ABSTRACT_QNAME + "/></xs:appinfo></xs:annotation>";
    }

    @Override
    public boolean run(Outline outline, Options options, ErrorHandler errorHandler) throws SAXException {
        Method setFlagMethod = null;
        try {
            // There is no method to make a class abstract; we can only use setFlag, which is private, so
            // we must get it via reflection and make it accessible.
            setFlagMethod = JMods.class.getDeclaredMethod(SET_FLAG_METHOD_NAME, int.class, boolean.class);
            setFlagMethod.setAccessible(true);
        } catch (Throwable e) {
            System.err.println("There was an error retrieving the " + JMods.class.getName() + "." + SET_FLAG_METHOD_NAME
                    + " method (see below) => it will not be possible to set any class' abstract flag => this plugin will abort");
            e.printStackTrace();
            return false;
        }

        for (ClassOutline classOutline : outline.getClasses()) {
            if (hasAbstractAnnotation(classOutline)) {
                try {
                    setFlagMethod.invoke(classOutline.implClass.mods(), JMod.ABSTRACT, true);
                } catch (Throwable e) {
                    System.err.println("It was not possible to make " + classOutline.implClass.fullName()
                            + " abstract (see below)");
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    protected boolean hasAbstractAnnotation(ClassOutline classOutline) {
        XSAnnotation annotation = classOutline.target.getSchemaComponent().getAnnotation();
        if (annotation != null) {
            Object innerAnnotation = annotation.getAnnotation();
            if (innerAnnotation instanceof BindInfo) {
                for (BIDeclaration bindInfoDeclaration : (BindInfo) innerAnnotation) {
                    if (ABSTRACT_QNAME.equals(bindInfoDeclaration.getName())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

}

这里是abstract.xsd,它定义了你需要用来表示生成的class应该是抽象的<abstract>元素:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.com/jaxb/abstract-modifier/1-0"
    xmlns:tns="http://www.example.com/jaxb/abstract-modifier/1-0"
    elementFormDefault="qualified">

    <element name="abstract"/>

</schema>

用法(按照我原来问题中的例子):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    ...
    xmlns:abstract="http://www.example.com/jaxb/abstract-modifier/1-0"
    ...>

    <xs:complexType name="myAbstractType" abstract="true">
        <xs:annotation>
            <xs:appinfo>
                <jxb:class implClass="my.package.impl.MyAbstractClass"/>
            </xs:appinfo>
        </xs:annotation>
        ...
    </xs:complexType>

    <xs:complexType name="myAType">
        <xs:annotation>
            <xs:appinfo>
                <jxb:class implClass="my.package.impl.MyAClass"/>
                <!-- This tells the AbstractModifierPlugin to make the
                generated class abstract -->
                <abstract:abstract/>
            </xs:appinfo>
        </xs:annotation>
        <xs:complexContent>
            <xs:extension base="myAbstractType">
                ...
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="myBType">
        <xs:annotation>
            <xs:appinfo>
                <jxb:class implClass="my.package.impl.MyBClass"/>
                <!-- This tells the AbstractModifierPlugin to make the
                generated class abstract -->
                <abstract:abstract/>
            </xs:appinfo>
        </xs:annotation>
        <xs:complexContent>
            <xs:extension base="myAbstractType">
                ...
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

我不得不说我发现很难找到所有必要的文档来弄清楚如何做到这一点。如果我有时间,我会 post 在这里进行更长的解释;现在我希望至少代码会有所帮助。 免责声明:我不知道这是否是最好的方法,但我不得不依赖的 classes 的文档记录太松散(恕我直言,设计不是很好),这是我能想到的最好方法.

希望对您有所帮助。如果有人想使用此代码,请继续。