草庐IT

xml - 用于从非抽象 XML 类型生成抽象 Java 类的 JAXB 绑定(bind)

coder 2024-07-02 原文

简而言之

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

描述

情况如下:

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

    <xs:annotation>
        <xs:appinfo>
            <jxb:schemaBindings>
                <jxb:package name="my.package.jaxb"/>
            </jxb:schemaBindings>
        </xs:appinfo>
    </xs:annotation>
    
  • 我使用内联 JAXB 绑定(bind)来指示每个复杂类型的实现类的名称(在此示例中 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 类现在有编译错误,因为它们没有声明为抽象但它们现在继承了一个抽象方法(注意它们都扩展了 MyAbstractClass)。

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

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

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

抱歉解释得太长了,在此先感谢您。

最佳答案

我最终创建了一个 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>

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

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

关于xml - 用于从非抽象 XML 类型生成抽象 Java 类的 JAXB 绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30192253/

有关xml - 用于从非抽象 XML 类型生成抽象 Java 类的 JAXB 绑定(bind)的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  6. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  9. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  10. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

随机推荐