【问题标题】:JAXB binding to generate an abstract Java class from a non-abstract XML type用于从非抽象 XML 类型生成抽象 Java 类的 JAXB 绑定
【发布时间】:2015-07-23 09:48:18
【问题描述】:

总之

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

说明

情况如下:

  • 我在 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>
    
  • 我使用内联 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 我想定义一个 abstract 方法...

protected abstract void doSomething();

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

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

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

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

抱歉,解释太长了,提前致谢。

【问题讨论】:

    标签: xml binding jaxb xjc


    【解决方案1】:

    我最终创建了一个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,它定义了你需要使用的&lt;abstract&gt;元素来指示生成的类应该是抽象的:

    <?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>
    

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

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

    【讨论】:

    • 除了反射没有别的办法吗?
    • @roded 如上面代码中我的 cmets 所示: 没有方法可以使类抽象;我们只能使用 setFlag,它是私有的,所以我们必须通过反射获取它并使其可访问。 所以 AFAIK 没有其他选择,但如果你找到了,我很乐意听到。跨度>
    猜你喜欢
    • 1970-01-01
    • 2016-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多