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

时间:2015-05-12 13:24:45

标签: xml binding jaxb xjc

简而言之

是否有任何JAXB绑定可以告诉JAXB代码生成器生成一个Java类abstract 而无需在XSD中将相应的XML类型标记为abstract

描述

情况如下:

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

    <xs:annotation>
        <xs:appinfo>
            <jxb:schemaBindings>
                <jxb:package name="my.package.jaxb"/>
            </jxb:schemaBindings>
        </xs:appinfo>
    </xs:annotation>
    
  • 我使用内联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>
    
  • 我从架构生成JAXB类。这导致:

    my.package.jaxb
       |- MyAbstractType
       |- MyAType (extends MyAbstractClass)
       |- MyBType (extends MyAbstractClass)
    
  • 我自己编写课程:

    my.package.impl
       |- MyAbstractClass (extends MyAbstractType)
       |- MyAClass (extends MyAType)
       |- MyBClass (extends MyBType)
    

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

到目前为止一切顺利。问题在于MyAbstractClass我想要定义一个抽象方法......

protected abstract void doSomething();

...然后由MyAClassMyBClass以不同方式实施。 但是,生成的MyATypeMyBType类现在有编译错误,因为它们未被声明为抽象,但它们现在继承了抽象方法(请注意它们都扩展{{1 }})。

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

myBType

我想要的是使用一些JAXB绑定来告诉JAXB代码生成器生成类cvc-type.2: The type definition cannot be abstract for element someElementName. MyAType作为MyBType ,而不必标记XML类型为abstract 。有这样的约束吗?到目前为止我还没找到它。

对于长时间的解释感到抱歉,并提前感谢。

1 个答案:

答案 0 :(得分:0)

我最终创建了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,它定义了您需要用来表示生成的类应该是抽象的<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>

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

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