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

Ali*_*lix 6 xml binding jaxb xjc

简而言之

是否有任何 JAXB 绑定可以告诉 JAXB 代码生成器生成 Java 类,而abstract 不必像abstractXSD 中那样标记相应的 XML 类型?

描述

情况如下:

  • 我在 XSD 中定义了一个架构:mySchema.xsd
  • 我使用内联 JAXB 绑定(“内联”==“直接在架构中”)来指示应在其中生成 JAXB 类的包 ( my.package.jaxb):

    <xs:annotation>
        <xs:appinfo>
            <jxb:schemaBindings>
                <jxb:package name="my.package.jaxb"/>
            </jxb:schemaBindings>
        </xs:appinfo>
    </xs:annotation>
    
    Run Code Online (Sandbox Code Playgroud)
  • 我使用内联 JAXB 绑定来指示每个复杂类型的实现类的名称(在本例中为my.package.impl.MyAbstractClassmy.package.impl.MyAClassmy.package.impl.MyBClass):

    <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"/>
            </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"/>
            </xs:appinfo>
        </xs:annotation>
        <xs:complexContent>
            <xs:extension base="myAbstractType">
                ...
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
    
    Run Code Online (Sandbox Code Playgroud)
  • 我从架构生成 JAXB 类。这导致:

    my.package.jaxb
       |- MyAbstractType
       |- MyAType (extends MyAbstractClass)
       |- MyBType (extends MyAbstractClass)
    
    Run Code Online (Sandbox Code Playgroud)
  • 我自己写类:

    my.package.impl
       |- MyAbstractClass (extends MyAbstractType)
       |- MyAClass (extends MyAType)
       |- MyBClass (extends MyBType)
    
    Run Code Online (Sandbox Code Playgroud)

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

到目前为止,一切都很好。问题是MyAbstractClass我想定义一个抽象方法......

<xs:annotation>
    <xs:appinfo>
        <jxb:schemaBindings>
            <jxb:package name="my.package.jaxb"/>
        </jxb:schemaBindings>
    </xs:appinfo>
</xs:annotation>
Run Code Online (Sandbox Code Playgroud)

MyAClass...然后由和进行不同的实现MyBClass。但是,生成的MyATypeMyBType类现在存在编译错误,因为它们没有声明为抽象,但它们现在继承了一个抽象方法(请注意,它们都扩展了MyAbstractClass)。

我无法在 XSD ( abstract="true") 中将它们声明为抽象,因为每当我声明类型元素myATypemyBType在 XML 中时,这样做都会导致以下错误:

cvc-type.2: The type definition cannot be abstract for element someElementName.
Run Code Online (Sandbox Code Playgroud)

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

抱歉这么长的解释,并提前致谢。

Ali*_*lix 1

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

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

这里的abstract.xsd,它定义了<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>
Run Code Online (Sandbox Code Playgroud)

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

<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>
Run Code Online (Sandbox Code Playgroud)

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

我希望这有帮助。如果有人想使用此代码,请继续。