简体   繁体   English

使用XmlAdapter在JAXB生成的类中使用java.util.Locale

[英]Using java.util.Locale in JAXB generated classes using XmlAdapter

The problem: 问题:

Based on the following documentation provided by Oracle about the use of the java.util.Locale: [Internationalization: Understanding Locale in the Java Platform] , I have the following question related to JAXB and the Locale. 基于Oracle提供的有关使用java.util.Locale的以下文档: [国际化:了解Java平台中的语言环境] ,我有以下与JAXB和语言环境有关的问题。

I have an XML file that looks like this: 我有一个看起来像这样的XML文件:

<?xml version="1.0" encoding="utf-8"?>
<dataschema>
  <delimited>
    <locale language="en" country="US" variant="SiliconValley" />
  </delimited>
</dataschema>

Which is based on the following XML schema: 它基于以下XML模式:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="dataschema">
        <xs:complexType>
            <xs:choice>
                <xs:element minOccurs="1" maxOccurs="1" name="delimited" type="DelimitedSchemaType"/>
                <xs:element minOccurs="1" maxOccurs="1" name="fixedwidth" type="FixedWidthSchemaType"/>
            </xs:choice>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="DelimitedSchemaType">
        <xs:sequence>
            <xs:element minOccurs="1" maxOccurs="1" name="locale" type="LocaleType"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="FixedWidthSchemaType">
        <xs:sequence>
            <xs:element minOccurs="1" maxOccurs="1" name="locale" type="LocaleType"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="LocaleType">
        <xs:attribute name="language" use="required">
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:pattern value="[a-z]{2,3}"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
        <xs:attribute name="country" use="required">
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:pattern value="[A-Z]{2}"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
        <xs:attribute name="variant" use="optional">
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:pattern value="[A-Z]{2}"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
    </xs:complexType>
</xs:schema>

Now the problem is that I get the following generated classes for the LocaleType xml complexType which does not seem to reflect the actual java.util.Locale datatype within the generated DelimitedDataSchema class. 现在的问题是,我为LocaleType xml complexType获取了以下生成的类,该类似乎无法反映生成的DelimitedDataSchema类中的实际java.util.Locale数据类型。 I would have expected this to be of type java.util.Locale and NOT of type org.mylib.schema.LocaleType? 我曾希望这是java.util.Locale类型,而不是org.mylib.schema.LocaleType类型?

The generated classes by JAXB 2.x are: JAXB 2.x生成的类是:

Dataschema.java: Dataschema.java:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "delimited",
    "fixedwidth"
})
@XmlRootElement(name = "dataschema")
public class Dataschema {

    protected DelimitedDataSchema delimited;
    protected FixedWidthDataSchema fixedwidth;

    public DelimitedDataSchema getDelimited() {
        return delimited;
    }

    public void setDelimited(DelimitedDataSchema value) {
        this.delimited = value;
    }

    public FixedWidthDataSchema getFixedwidth() {
        return fixedwidth;
    }

    public void setFixedwidth(FixedWidthDataSchema value) {
        this.fixedwidth = value;
    }
}

DelimitedDataSchema.java: DelimitedDataSchema.java:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "DelimitedSchemaType", propOrder = {
    "localeType"
})
public class DelimitedDataSchema {

    @XmlElement(required = true)
    protected LocaleType locale;

    public LocaleType getLocale() {
        return locale;
    }

    public void setLocale(LocaleType value) {
        this.locale = value;
    }
}

LocaleType: LocaleType:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "LocaleType")
public class LocaleType {

    @XmlAttribute(name = "language", required = true)
    protected String language;
    @XmlAttribute(name = "country", required = true)
    protected String country;
    @XmlAttribute(name = "variant")
    protected String variant;

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String value) {
        this.language = value;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String value) {
        this.country = value;
    }

    public String getVariant() {
        return variant;
    }

    public void setVariant(String value) {
        this.variant = value;
    }
}

I bravely followed the instructions in the following blog posts from Blaise Doughan about JAXB XmlAdapters: JAXB and Package Level XmlAdapters and also XmlAdapter - JAXB's Secret Weapon 我勇敢地遵循了Blaise Doughan在以下博客文章中有关JAXB XmlAdapters的指示: JAXB和程序包级别XmlAdapters以及XmlAdapter-JAXB的秘密武器

So I created an XmlAdapter myself, hoping that the generated class (DelimitedDataSchema) would contain the java.util.Locale return datatype in the getter and the java.util.Locale parameter datatype in the setter. 因此,我自己创建了一个XmlAdapter,希望生成的类(DelimitedDataSchema)在getter中包含java.util.Locale返回数据类型,在setter中包含java.util.Locale参数数据类型。 Which I mistakenly assumed. 我错误地假设了。

LocaleXmlAdapter.java: LocaleXmlAdapter.java:

public class LocaleXmlAdapter extends XmlAdapter<org.mylib.schema.LocaleType, java.util.Locale> {
    @Override
    public java.util.Locale unmarshal(org.mylib.schema.LocaleType pSchemaLocale) throws Exception {
        if (pSchemaLocale == null) {
            throw new NullPointerException("LocaleXmlAdapter.unmarshal(...) received a NULL literal.");
        }

        java.util.Locale mLocale = null;
        String mLanguage = pSchemaLocale.getLanguage().toLowerCase();
        String mCountry = pSchemaLocale.getCountry().toUpperCase();
        String mVariant = pSchemaLocale.getVariant();

        if (mVariant == null) {
            mLocale = new java.util.Locale(mLanguage, mCountry);
        } else {
            mLocale = new java.util.Locale(mLanguage, mCountry, mVariant);
        }
        return mLocale;
    }

    @Override
    public org.mylib.schema.LocaleType marshal(java.util.Locale pJavaLocale) throws Exception {
        if (pJavaLocale == null) {
            throw new NullPointerException("LocaleXmlAdapter.marshal(...) received a NULL literal.");
        }

        org.mylib.schema.LocaleType mLocale = new org.mylib.schema.LocaleType();
        mLocale.setLanguage(pJavaLocale.getLanguage().toLowerCase());
        mLocale.setCountry(pJavaLocale.getCountry().toUpperCase());
        String mVariant = pJavaLocale.getVariant();
        if (mVariant != null) {
            mLocale.setVariant(mVariant);
        }

        return mLocale;
    }
}

To let the JAXB library know that it must use the LocaleXmlAdapter, I provided the library with an external binding file, in which the LocaleXmlAdapter is defined for the Locale class. 为了让JAXB库知道它必须使用LocaleXmlAdapter,我为该库提供了一个外部绑定文件,其中为Locale类定义了LocaleXmlAdapter。

External JAXB binding file: 外部JAXB绑定文件:

<?xml version="1.0" encoding="utf-8"?>
<jaxb:bindings jaxb:version="2.1" xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               schemaLocation="dataschema.xsd" node="/xs:schema">

    <jaxb:schemaBindings>
        <jaxb:package name="org.mylib.schema">
            <jaxb:javadoc>
                Package level documentation for generated package org.mylib.schema.
            </jaxb:javadoc>
        </jaxb:package>
    </jaxb:schemaBindings>

    <jaxb:bindings node="//xs:complexType[@name='LocaleType']">
        <jaxb:class name="LocaleType"/>
        <jaxb:property>
            <jaxb:baseType name="org.mylib.schema.LocaleXmlAdapter"/>
        </jaxb:property>
    </jaxb:bindings>

    <jaxb:bindings node="//xs:complexType[@name='DelimitedSchemaType']">
        <jaxb:class name="DelimitedDataSchema"/>
    </jaxb:bindings>

    <jaxb:bindings node="//xs:complexType[@name='FixedWidthSchemaType']">
        <jaxb:class name="FixedWidthDataSchema"/>
    </jaxb:bindings>
</jaxb:bindings>

Now the weird part, which I obviously don't get, is that I would have expected that the JAXB library would translate the org.mylib.schema.LocaleType type into the java.util.Locale type for the DelimitedDataSchema class, so you would see the following method signatures in the DelimitedDataSchema class: 现在,我显然不会得到的怪异部分是,我希望JAXB库将org.mylib.schema.LocaleType类型转换为DelimitedDataSchema类的java.util.Locale类型,因此您将请参见DelimitedDataSchema类中的以下方法签名:

  • public java.util.Locale getLocale() {} 公共java.util.Locale getLocale(){}

  • public void setLocale(java.util.Locale value) {} public void setLocale(java.util.Locale value){}

What I want to accomplish is that the java.util.Locale datatype is used instead of the org.mylib.schema.LocaleType datatype. 我要完成的工作是使用java.util.Locale数据类型而不是org.mylib.schema.LocaleType数据类型。 How else do I get the translation done between the user code and the JAXB generated code? 如何在用户代码和JAXB生成的代码之间进行转换? I can't call the LocaleXmlAdapter class myself to translate the locale type for me, that must be done by the JAXB library, but I do want to call: getLocale() and in return get a java.util.Locale datatype. 我不能自己调用​​LocaleXmlAdapter类来为我转换语言环境类型,这必须由JAXB库完成,但是我确实要调用:getLocale()并返回一个java.util.Locale数据类型。

What am I doing 'wrong'? 我究竟做错了什么'?

Update: 更新:

So far I figured out that the < jaxb :baseType /> should NOT be used. 到目前为止,我发现不应使用< jaxb :baseType />。 Instead the < xjc :javaType > should be used within the binding file as a child element of < jaxb :baseType>. 相反,< xjc :javaType>应该在绑定文件中用作< jaxb :baseType>的子元素。 I also falsely assumed that the < jaxb :baseType> had to be defined under the LocaleType node, which is NOT true. 我还错误地假定< jaxb :baseType>必须在LocaleType节点下定义,这是不正确的。 It must be defined under the element node of the DelimitedSchemaType node and FixedWidthSchemaType node. 它必须在DelimitedSchemaType节点和FixedWidthSchemaType节点的元素节点下定义。 Like this: 像这样:

...
<jaxb:bindings node="//xs:complexType[@name='DelimitedSchemaType']">
    <jaxb:property>
        <jaxb:baseType>
            <xjc:javaType name="org.mylib.schema.LocaleType" adapter="org.mylib.schema.LocaleXmlAdapter"/>
        </jaxb:baseType>
    </jaxb:property>
</jaxb:bindings>
...

This should be correct, but somehow the XJC compiler produced compile errors. 这应该是正确的,但是XJC编译器某种程度上会产生编译错误。 The following error occurs: 发生以下错误:

[ERROR] Error while parsing schema(s).Location [ file:/C:/IdeaProjects/JaxbMarshalling/src/main/resources/dataschema.xjb{25,113}].
com.sun.istack.SAXParseException2; systemId: file:/C:/IdeaProjects/JaxbMarshalling/src/main/resources/dataschema.xjb; lineNumber: 25; columnNumber: 113; compiler was unable to honor this conversion customization. It is attached to a wrong place, or its inconsistent with other bindings.
    at com.sun.tools.xjc.ErrorReceiver.error(ErrorReceiver.java:86)
    etc.

It keeps nagging about "compiler was unable to honor this conversion customization. It is attached to a wrong place, or its inconsistent with other bindings." 它一直在抱怨“编译器无法执行此转换自定义。它被附加到错误的位置,或者与其他绑定不一致。” , while there is no mistake within the bindings file to be found. ,而在绑定文件中没有发现错误。

I have improved my bindings file, but still something isn't 'right'. 我已经改进了绑定文件,但是仍然有些不正确。 I can't pinpoint the exact location where it goes 'wrong'. 我无法查明“错误”的确切位置。

BTW: I am using the following tools: 顺便说一句:我正在使用以下工具:

  • Oracle Java JDK 64-bit, version 1.8.0_112-b15 Oracle Java JDK 64位版本1.8.0_112-b15
  • xjc, version 2.2.8-b130911.1802 (shipped with above mentioned JDK) xjc,版本2.2.8-b130911.1802(随附上述JDK)
  • maven3, version 3.3.9 maven3版本3.3.9
  • IntelliJ IDEA 2016.3, version 163.7743.44 IntelliJ IDEA 2016.3版本163.7743.44
  • maven-jaxb2-plugin, version 0.13.1 maven-jaxb2-plugin,版本0.13.1

Because I am struggling with this for a few days now, I have started a bounty. 由于我为此苦苦挣扎了几天,所以我开始了赏金计划。 The person that really solves the problem using the external bindings file and correct annotations gets my bounty points. 使用外部绑定文件和正确的注释真正解决问题的人会得到我的赏识。

This question/problem has several flaws in it that first must be addressed in order to successfully answer the question/problem. 这个问题/问题有几个缺陷,必须首先解决该问题才能成功回答该问题/问题。 Because you requested a detailed canonical answer, which addresses ALL concerns, here it is: 由于您要求提供详细的规范答案,以解决所有问题,因此这里是:

First, some observations: 首先,一些观察:

  1. The DelimitedDataSchema.java and FixedWidthDataSchema.java both contain an object field 'locale' of datatype org.mylib.schema.LocaleType which must be corrected to the java.util.Locale datatype. DelimitedDataSchema.java和FixedWidthDataSchema.java都包含数据类型为org.mylib.schema.LocaleType的对象字段“ locale”,必须将其更正为java.util.Locale数据类型。
  2. The object field 'locale' within DelimitedDataSchema.java and FixedWidthDataSchema.java must contain the @XmlJavaTypeAdapter annotation, which is now absent. DelimitedDataSchema.java和FixedWidthDataSchema.java中的对象字段“语言环境”必须包含@XmlJavaTypeAdapter批注,现在该批注不存在。
  3. The object field 'locale' within DelimitedDataSchema.java and FixedWidthDataSchema.java already contain an @XmlElement annotation, which now only specifies the 'required' element. DelimitedDataSchema.java和FixedWidthDataSchema.java中的对象字段“ locale”已经包含一个@XmlElement批注,该批注现在仅指定“ required”元素。 It must also include the 'type' element. 它还必须包含“ type”元素。
  4. The to be (un)marshalled 'locale' xml data is a complexType datastructure, namely: LocaleType, having only three attributes of type xs:string. 待编组的“区域设置” xml数据是complexType数据结构,即:LocaleType,仅具有xs:string类型的三个属性。 The current Xml Java Compiler (= XJC) does not (yet) support complexTypes to be processed into the correct annotations. 当前的Xml Java编译器(= XJC)(尚未)不支持将complexTypes处理为正确的注释。
  5. The node LocaleType in the external binding file contains a wrongly defined property. 外部绑定文件中的节点LocaleType包含错误定义的属性。 The 'localeType' only has three attributes, which are not defined. “ localeType”仅具有三个未定义的属性。
  6. The defined LocaleXmlAdapter is wrongly defined, and should be moved to the property (xml element) of the DelimitedDataSchema and FixedWidthDataSchema class. 定义的LocaleXmlAdapter错误定义,应将其移至DelimitedDataSchema和FixedWidthDataSchema类的属性(xml元素)。
  7. The <jaxb:baseType> declarations of the field/element 'locale' in the DelimitedDataSchema and FixedWidthDataSchema class is missing. DelimitedDataSchema和FixedWidthDataSchema类中的字段/元素“ locale”的<jaxb:baseType>声明丢失。
  8. You have not specified your maven pom.xml file in your question. 您尚未在问题中指定maven pom.xml文件。 Please include this too when asking your question next time. 下次问您的问题时,也请包括此内容。 It provides valuable information to the users who want to answer your question as precise as possible. 它为想要尽可能精确地回答您的问题的用户提供了有价值的信息。 And even for new seekers coping with the same questions you had, will find the answers they need in your post. 即使是对于新求职者,如果他们也遇到了同样的问题,他们也会在您的帖子中找到所需的答案。 Be as complete as possible. 尽可能完整。

Second, some modifications/addition: 第二,一些修改/添加:

  1. Your XML Schema file is well-defined. 您的XML Schema文件定义明确。 So, no modifications are needed in this file. 因此,此文件中不需要修改。
  2. Your bindings file (datasource.jxb) does contain flaws. 您的绑定文件(datasource.jxb)确实包含缺陷。

In order to correct these flaws, do the following: 为了纠正这些缺陷,请执行以下操作:

  1. recommendation: rename the class name of the LocaleType to XmlLocale, so you can easily match the XmlLocale.class and LocaleXmlAdapter.class together, without getting confused between java.util.Locale and org.mylib.schema.Locale (which is now renamed to org.mylib.schema.XmlLocale) 建议:将LocaleType的类名称重命名为XmlLocale,这样您就可以轻松地将XmlLocale.class和LocaleXmlAdapter.class匹配在一起,而不会在java.util.Locale和org.mylib.schema.Locale之间产生混淆(现在已将其重命名为org.mylib.schema.XmlLocale)
  2. correction: remove the element and child element completely, because these MUST NOT be specified at the level of LocaleType, but at the level of the 'locale' element within the DelimitedSchemaType and FixedWidthSchemaType. 更正:完全删除元素和子元素,因为必须在LocaleType级别上指定这些元素和子元素,而不能在DelimitedSchemaType和FixedWidthSchemaType中指定“ locale”元素的级别。
  3. correction: within the complexType DelimitedSchemaType and FixedWidthSchemaType define a child XPath node that represents the 'locale' element specified in these complexTypes. 更正:在complexType DelimitedSchemaType和FixedWidthSchemaType中定义一个子XPath节点,该节点表示在这些complexTypes中指定的“ locale”元素。
  4. correction: place under the 'locale' element node defined within the complextType DelimitedSchemaType and FixedWidthSchemaType a <jaxb:property> element with a child <jaxb:baseType> element. 校正:在complextType DelimitedSchemaType和FixedWidthSchemaType内定义的'locale'元素节点下,放置<jaxb:property>元素和子<jaxb:baseType>元素。 Give the property a name. 给属性命名。 This can be the same name as specified within the XML Schema, but can also be named differently. 该名称可以与XML模式中指定的名称相同,但也可以使用不同的名称。 If you name it differently than the XML Schema, be aware that your object field is called like the <jaxb:property> name and not the XML Schema element name. 如果您使用的名称与XML Schema的名称不同,请注意,对象字段的名称类似于<jaxb:property>名称,而不是XML Schema元素名称。 This has also effect on the getter and setter method names. 这也会影响getter和setter方法的名称。
  5. correction: Define the datatype of the property using the <jaxb:baseType> name. 更正:使用<jaxb:baseType>名称定义属性的数据类型。 The wanted object field datatype is of type java.util.Locale. 所需对象字段数据类型的类型为java.util.Locale。 Thus you get: <jaxb:baseType name="java.util.Locale"/>. 这样就得到:<jaxb:baseType name =“ java.util.Locale” />。 You do NOT specify the org.mylib.schema.XmlLocale datatype as the name! 您不要将org.mylib.schema.XmlLocale数据类型指定为名称!
  6. addition: In order to add the @XmlJavaTypeAdapter to the element 'locale' in DelimitedDataSchema and FixedWidthDataSchema classes, you normally would specify this using the <xjc:baseType> element. 另外:为了将@XmlJavaTypeAdapter添加到DelimitedDataSchema和FixedWidthDataSchema类中的元素“ locale”,通常可以使用<xjc:baseType>元素进行指定。 But because the Xml Java Compiler (=XJC) does NOT (yet) handle this type of conversion between complexTypes and Java datatypes, you cannot use the default specification as described in the documentation. 但是,由于Xml Java编译器(= XJC)尚未(尚未)处理complexType和Java数据类型之间的这种类型的转换,因此您不能使用文档中所述的默认规范。 This just doesn't work (yet) with the current XJC: 这对于当前的XJC仍然不起作用:

     <jaxb:baseType> <xjc:javaType name="org.mylib.schema.XmlLocale" adapter="org.mylib.schema.LocaleXmlAdapter"/> </jaxb:baseType> 

    So you must provide the @XmlJavaTypeAdapter annotation yourself. 因此,您必须自己提供@XmlJavaTypeAdapter批注。 This is where the jaxb2-basics-annotate plugin [link] comes in handy. 这是jaxb2-basics-annotate插件[link]派上用场的地方。 With this plugin you can annotate ANY existing Java annotation at any location within a Java class: class-level, field-level, method-level, etc. In order to annotate the 'missing' annotation you have to set up a few things, which are described later on. 使用此插件,您可以在Java类中的任何位置注释任何现有的Java注释:类级别,字段级别,方法级别等。为了注释“缺失”注释,您必须进行一些设置,稍后将对此进行描述。
    In the bindings file you have to specify the following settings: 在绑定文件中,您必须指定以下设置:
    a) specify the namespace (annox) used by the plugin in the root of your bindings file; a)在绑定文件的根目录中指定插件使用的名称空间(annox);
    b) specify the extensionBindingPrefixed in the root of your bindings file; b)在绑定文件的根目录中指定extensionBindingPrefixed;

     <jaxb:bindings jaxb:version="2.1" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:annox="http://annox.dev.java.net" schemaLocation="dataschema.xsd" node="/xs:schema" jaxb:extensionBindingPrefixes="xjc annox"> 

    c) specify the preferred annotation using the element. c)使用元素指定首选注释。
    Because the adapter needs to be specified on the object field 'locale' of the DelimitedDataSchema and FixedWidthDataSchema classes, the annotate element must be specified within the <jaxb:bindings> node of the 'locale': 因为需要在DelimitedDataSchema和FixedWidthDataSchema类的对象字段“ locale”上指定适配器,所以必须在“ locale”的<jaxb:bindings>节点内指定注释元素:

     <jaxb:bindings node="//xs:complexType[@name='DelimitedSchemaType']"> <jaxb:class name="DelimitedDataSchema"/> <jaxb:bindings node=".//xs:element[@name='locale']"> <jaxb:property name="locale"> <jaxb:baseType name="java.util.Locale" /> </jaxb:property> <annox:annotate target="field"> @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value = org.mylib.schema.LocaleXmlAdapter.class) </annox:annotate> <jaxb:bindings> </jaxb:bindings> 

    Likewise for the FixedWidthSchemaType complexType. 对于FixedWidthSchemaType complexType同样。 Notice that the FULL package name and class name, including the annotation parameter(s) must be specified! 注意,必须指定FULL包名和类名,包括注释参数!

  7. recommendation: make sure that all your nodes within the bindings file are specified and mapped to the XML Schema elements and attributes. 建议:确保已指定绑定文件中的所有节点并将其映射到XML Schema元素和属性。 This is recommended because by specifying each node and giving each node a name (can be the same or different than the XML Schema name) in the bindings file you make sure that the generated classes, fields, getters/setters are named the way YOU like it. 推荐这样做是因为通过在绑定文件中指定每个节点并为每个节点指定名称(可以与XML Schema名称相同或不同),可以确保生成的类,字段,getter / setter的命名方式如您所愿它。 This prevents a lot of manual work later on, when someone decides to rename the XML Schema names, which obviously leads to recompiling of java classes and reference mismatched with other (manually written) classes. 当有人决定重命名XML Schema名称时,这将阻止以后进行大量手动工作,这显然会导致Java类的重新编译以及与其他(手动编写的)类不匹配的引用。 For example: your manually written XmlAdapter class: LocaleXmlAdapter, which refers to method names in the generated XmlLocale class. 例如:您手动编写的XmlAdapter类:LocaleXmlAdapter,它引用生成的XmlLocale类中的方法名称。 What you actually do is decouple the XML Schema names from the Java names using one single binding file. 您实际上所做的是使用一个绑定文件将XML模式名称与Java名称分离。 That saves you from a lot of trouble later on (trust me: I've seen it happen many times in different development teams)! 这样一来,您就免除了以后的麻烦(相信我:我已经在不同的开发团队中看到了很多次)! That is just wasting valuable time and money. 那只是浪费宝贵的时间和金钱。 With this binding file fully specified, you only have to adopt the name of the XML Schema node, the Java-side does not change! 完全指定此绑定文件后,您只需采用XML Schema节点的名称,Java端就不会改变!

    Your binding file now results in this complete bindings file: 现在,您的绑定文件将生成以下完整的绑定文件:

     <?xml version="1.0" encoding="utf-8"?> <jaxb:bindings jaxb:version="2.1" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:annox="http://annox.dev.java.net" schemaLocation="dataschema.xsd" node="/xs:schema" jaxb:extensionBindingPrefixes="xjc annox"> <jaxb:schemaBindings> <jaxb:package name="org.mylib.schema"/> </jaxb:schemaBindings> <jaxb:bindings node="//xs:complexType[@name='DelimitedSchemaType']"> <jaxb:class name="DelimitedDataSchema"/> <jaxb:bindings node=".//xs:element[@name='locale']"> <jaxb:property name="locale"> <jaxb:baseType name="java.util.Locale" /> </jaxb:property> <annox:annotate target="field"> @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value = org.mylib.schema.LocaleXmlAdapter.class) </annox:annotate> </jaxb:bindings> </jaxb:bindings> <jaxb:bindings node="//xs:complexType[@name='FixedWidthSchemaType']"> <jaxb:class name="FixedWidthDataSchema"/> <jaxb:bindings node=".//xs:element[@name='locale']"> <jaxb:property name="locale"> <jaxb:baseType name="java.util.Locale"/> </jaxb:property> <annox:annotate target="locale"> @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value = org.mylib.schema.LocaleXmlAdapter.class) </annox:annotate> </jaxb:bindings> </jaxb:bindings> <jaxb:bindings node="//xs:complexType[@name='LocaleType']"> <jaxb:class name="XmlLocale"/> <jaxb:bindings node=".//xs:attribute[@name='language']"> <jaxb:property name="language"/> </jaxb:bindings> <jaxb:bindings node=".//xs:attribute[@name='country']"> <jaxb:property name="country"/> </jaxb:bindings> <jaxb:bindings node=".//xs:attribute[@name='variant']"> <jaxb:property name="variant"/> </jaxb:bindings> </jaxb:bindings> </jaxb:bindings> 


3. Your maven pom.xml file is not specified, but for the complete overview of this topic, it is mentioned here as well. 3.未指定您的maven pom.xml文件,但是为了完整了解本主题,此处也将其提及。

There are a few things to take into consideration when generating JAXB classes using maven. 使用maven生成JAXB类时,需要考虑一些事项。

In order to do the 'right thing', do the following: - Since JAXB XJC is specific on which Java version you compile and run it, you should specify to which source and target Java version it must be compiled. 为了做“正确的事情”,请执行以下操作:-由于JAXB XJC专门针对您编译和运行的Java版本,因此您应指定必须将其编译到的源Java版本和目标Java版本。 This can be accomplished with the maven-compiler-plugin. 这可以通过maven-compiler-plugin完成。 Instead of letting XJC compile the Java classes with default level, version 1.5, you now specify version 1.8. 现在,您可以指定版本1.8,而不是让XJC用默认级别1.5版编译Java类。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>

        <groupId>org.mylib</groupId>
        <artifactId>mytool</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>project-name</name>

        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>

        <build>
            <resources>
                <resource>
                    <directory>${pom.basedir}/src/main/resources</directory>
                </resource>
            </resources>

            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.6.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>

                <plugin>
                    <groupId>org.jvnet.jaxb2.maven2</groupId>
                    <artifactId>maven-jaxb2-plugin</artifactId>
                    <version>0.13.1</version>
                    <executions>
                        <execution>
                            <phase>generate-sources</phase>
                            <goals>
                                <goal>generate</goal>
                            </goals>
                            <configuration>
                                <!-- allow specific vendor extension bindings (jaxb:extensionBindingPrefixes) -->
                                <extension>true</extension>
                                <!-- Generate lots of output (for debug purposes) -->
                                <verbose>true</verbose>
                                <locale>en</locale>
                                <specVersion>2.2</specVersion>
                                <schemaLanguage>XMLSCHEMA</schemaLanguage>

                                <schemaDirectory>src/main/resources</schemaDirectory>
                                <schemaIncludes>
                                    <schemaInclude>dataschema.xsd</schemaInclude>
                                </schemaIncludes>

                                <bindingDirectory>src/main/resources</bindingDirectory>
                                <bindingIncludes>
                                    <bindingInclude>dataschema.xjb</bindingInclude>
                                </bindingIncludes>

                                <generateDirectory>${project.build.directory}/generated-sources/jaxb2</generateDirectory>
                                <args>
                                    <!-- covered by the jaxb2-basics-annotate plugin (YOU ONLY NEED THIS ONE IN YOUR SITUATION!) -->
                                    <arg>-Xannotate</arg>
                                    <!-- covered by the jaxb2-basics-plugin -->
                                    <arg>-Xsimplify</arg>
                                    <arg>-XtoString</arg>
                                    <arg>-Xequals</arg>
                                    <arg>-XhashCode</arg>
                                </args>
                                <plugins>
                                    <!-- plugin for generated toString, hashCode, equals methods -->
                                    <plugin>
                                        <groupId>org.jvnet.jaxb2_commons</groupId>
                                        <artifactId>jaxb2-basics</artifactId>
                                        <version>1.11.1</version>
                                    </plugin>
                                    <!-- plugin for adding specified annotations (YOU ONLY NEED THIS ONE IN YOUR SITUATION!) -->
                                    <plugin>
                                        <groupId>org.jvnet.jaxb2_commons</groupId>
                                        <artifactId>jaxb2-basics-annotate</artifactId>
                                        <version>1.0.2</version>
                                    </plugin>
                                </plugins>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>

Third, some results (before and after): 第三,一些结果(之前和之后):

When executing the maven compile phase before the modifications in the bindings file and maven pom.xml file, you ended up with the following DelimitedDataSchema class (excerpt is shown): 在绑定文件和maven pom.xml文件中进行修改之前执行maven编译阶段时,您最终得到以下DelimitedDataSchema类(显示了摘录):

package org.mylib.schema;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "DelimitedSchemaType", propOrder = {
   "locale"
})
public class DelimitedDataSchema {

@XmlElement(required = true)
protected XmlLocale locale; // XmlLocale instead of Locale (java.util.Locale)

}

When executing the maven compile phase after the modifications in the bindings file and maven pom.xml file, you ended up with the following DelimitedDataSchema class (excerpt is shown): 在对绑定文件和maven pom.xml文件进行修改之后执行maven编译阶段时,您最终得到以下DelimitedDataSchema类(显示了摘录):

package org.mylib.schema;

import java.util.Locale;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "DelimitedSchemaType", propOrder = {
    "locale"
})
public class DelimitedDataSchema {

    @XmlElement(required = true, type = XmlLocale.class)
    @XmlJavaTypeAdapter(LocaleXmlAdapter.class)
    protected Locale locale;

}   

The result is clear: the desired solution is accomplished by using the above mentioned solution. 结果很明显:使用上述解决方案即可实现所需的解决方案。 Notice that the @XmlElement now contains an extra parameter: type = XmlLocale.class. 请注意,@ XmlElement现在包含一个额外的参数:type = XmlLocale.class。 Notice that the @XmlJavaTypeAdapter only contains the LocaleXmlAdapter.class parameter. 请注意,@ XmlJavaTypeAdapter仅包含LocaleXmlAdapter.class参数。 You can also write the same situation differently, like: 您还可以不同地编写相同的情况,例如:

@XmlElement(required = true)
@XmlJavaTypeAdapter(type = XmlLocale, value = LocaleXmlAdapter.class)

Which accomplishes exactly the same. 哪个完全一样。 But remember that you need to specify the <jaxb:baseType name="java.util.Locale"/> because the object field must be defined as java.util.Locale. 但是请记住,您必须指定<jaxb:baseType name =“ java.util.Locale” />,因为对象字段必须定义为java.util.Locale。 Currently, this is the ONLY way to accomplish this. 当前,这是完成此操作的唯一方法。 You cannot just ONLY specify the @XmlJavaTypeAdapter(type = XmlLocale, value = LocaleXmlAdapter.class) annotation using the jaxb2-basics-annotate plugin because the object field property is then NOT changed into the java.util.Locale datatype. 您不能仅使用jaxb2-basics-annotate插件指定@XmlJavaTypeAdapter(type = XmlLocale,value = LocaleXmlAdapter.class)批注,因为对象字段属性随后不会更改为java.util.Locale数据类型。

In the near future I hope the Xml Java Compiler (XJC) will support complexTypes and distinguishes the datatypes being marshalled/unmarshalled itself by inspecting the custom written XmlAdapter parameter and return types. 我希望在不久的将来,Xml Java编译器(XJC)将支持complexTypes,并通过检查自定义编写的XmlAdapter参数和返回类型来区分被编组/解组的数据类型。

Fourth, the evidence (Marshalling and Unmarshalling) 四,证据(编组和拆组)

There is a saying that says: "The proof is in the eating of the pudding", which means something like this: if you have created something and it looks good, the only way to know if it really is good (ie that it works) is by testing it. 有一句话说:“证明是在吃布丁”,意思是这样的:如果您创造了某种东西,并且看起来不错,那么唯一的办法就是知道它是否真的很好(即,它有效) )通过测试。 In this case marshalling/unmarshalling it. 在这种情况下,将其进行编组/解组。 In the case of the pudding, tasting it is the only way to be absolutely sure the recipe is great! 在布丁的情况下,品尝它是绝对可以确保食谱很棒的唯一方法!

Main.java: Main.java:

package org.mylib;

import org.mylib.schema.Dataschema;
import org.mylib.schema.DelimitedDataSchema;
import org.mylib.schema.FixedWidthDataSchema;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import java.io.Reader;
import java.io.StringReader;

public class Main {

    public static void main(String[] args) throws Exception {
        Dataschema ds = null;

        Reader xmlFileDelimited = new StringReader("<dataschema>\n" +
                "  <delimited>\n" +
                "    <locale language=\"en\" country=\"us\" />\n" +
                "  </delimited>\n" +
                "</dataschema>");

        try {
            JAXBContext jc = JAXBContext.newInstance(Dataschema.class);
            Unmarshaller um = jc.createUnmarshaller();
            ds = (Dataschema) um.unmarshal(new StreamSource(xmlFileDelimited));
        } catch (JAXBException e) {
            e.printStackTrace();
        }

        if (ds == null) {
            throw new NullPointerException("null literal as output of marshaller!");
        }

        DelimitedDataSchema delimited = ds.getDelimited();
        FixedWidthDataSchema fixedwidth = ds.getFixedwidth();

        if (((fixedwidth == null) && (delimited == null)) || ((fixedwidth != null) && (delimited != null))) {
            throw new IllegalStateException("schemas cannot be both absent or be both present at the same time!"); // (because of <choice> xml schema)!
        }

        if (delimited != null) {
            // very primitive code for proving correctness
            System.out.println(delimited.getLocale().toString());
        }

        if (fixedwidth != null) {
            // very primitive code for proving correctness
            System.out.println(fixedwidth.getLocale().toString());
        }
    }
}

Marshalling omitted, that is left to the reader to implement. 编组省略,即留给读者实施。

End of example. 示例结束。

Note that JAXB itself DOES marshal and unmarshal the org.mylib.schema.XmlLocale and java.util.Locale using the LocaleXmlAdapter pretty well. 请注意,JAXB本身确实很好地使用LocaleXmlAdapter对org.mylib.schema.XmlLocale和java.util.Locale进行了编组和解组。 So, it is not the JAXB core that is the troublemaker in this case. 因此,在这种情况下,麻烦者不是JAXB核心。 The Xml Java Compiler is the one to blame for not accepting complexTypes (yet). Xml Java编译器是尚未接受complexTypes的罪魁祸首。 These are the current limitations/shortcommings of the XJC and will hopefully be solved in the near future! 这些是XJC当前的局限性/不足之处,并有望在不久的将来得到解决!

Last words to consider: If the beaten path doesn't get you there, then take the road that does. 最后要考虑的问题:如果人迹罕至的地方无法带您到达目的地,那就走那条路。 Slow and steady wins the race! 踏实和稳重是赢得比赛的关键!

Some footnotes: Set the source and target compiler level to 1.8 instead of 1.5 (which is the default) using the maven-compiler-plugin. 一些脚注:使用maven-compiler-plugin将源和目标编译器级别设置为1.8,而不是1.5(这是默认设置)。

Important note concerning the maven-compiler-plugin: "Merely setting the target option does not guarantee that your code actually runs on a JRE with the specified version. The pitfall is unintended usage of APIs that only exist in later JREs which would make your code fail at runtime with a linkage error. To avoid this issue, you can either configure the compiler's boot classpath to match the target JRE or use the Animal Sniffer Maven Plugin to verify your code doesn't use unintended APIs. In the same way, setting the source option does not guarantee that your code actually compiles on a JDK with the specified version. To compile your code with a specific JDK version, different than the one used to launch Maven, refer to the Compile Using A Different JDK example." 关于maven-compiler-plugin的重要说明:“仅设置目标选项并不能保证您的代码实际在具有指定版本的JRE上运行。陷阱在于,只有在以后的JRE中才存在的API意外使用,它们会使您的代码成为现实。为了避免此问题,您可以在运行时失败,为避免此问题,您可以配置编译器的引导类路径以匹配目标JRE,也可以使用Animal Sniffer Maven插件来验证您的代码没有使用意外的API。 source选项不能保证您的代码实际上可以在具有指定版本的JDK上进行编译。要使用特定的JDK版本(与用于启动Maven的版本不同)来编译代码,请参阅使用其他JDK进行编译的示例。”

See: https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html 参见: https : //maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html

In other words: if you want to make sure your project is using the correct Java version and thus correct JAXB version, use the Animal Sniffer Maven Plugin. 换句话说:如果要确保项目使用的是正确的Java版本,从而是正确的JAXB版本,请使用Animal Sniffer Maven插件。 See: http://www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin/ 请参阅: http//www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin/

That's all ;) 就这样 ;)

JAXB seems only support xml simple type only in <xjc:javaType> and hence you are seeing error " ...unable to honor... ". JAXB似乎仅在<xjc:javaType>仅支持xml simple类型,因此您将看到错误“ ...unable to honor... ”。 See related question on this. 参见相关问题

How to generate @XmlJavaTypeAdapter annotation on generted class. 如何在生成的类上生成@XmlJavaTypeAdapter批注。 (based on jaxb2-annotate-plugin ) (基于jaxb2-annotate-plugin

Specify @XmlJavaTypeAdapter in binding file as shown below 在绑定文件中指定@XmlJavaTypeAdapter,如下所示

<?xml version="1.0" encoding="utf-8"?>
<jaxb:bindings jaxb:version="2.1" xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:annox="http://annox.dev.java.net"
               schemaLocation="dataschema.xsd" node="/xs:schema">

    <jaxb:schemaBindings>
        <jaxb:package name="org.mylib.schema">
            <jaxb:javadoc>
                Package level documentation for generated package org.mylib.schema.
            </jaxb:javadoc>
        </jaxb:package>
    </jaxb:schemaBindings>

    <jaxb:bindings node="//xs:complexType[@name='LocaleType']">
        <jaxb:class name="LocaleType"/>
        <jaxb:property>
            <jaxb:baseType name="java.util.Locale"/>
        </jaxb:property>
        <annox:annotate target="field">@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(org.mylib.schema.adaptors.LocaleXmlAdapter.class)</annox:annotate>
    </jaxb:bindings>

    <jaxb:bindings node="//xs:complexType[@name='DelimitedSchemaType']">
        <jaxb:class name="DelimitedDataSchema"/>
    </jaxb:bindings>

    <jaxb:bindings node="//xs:complexType[@name='FixedWidthSchemaType']">
        <jaxb:class name="FixedWidthDataSchema"/>
    </jaxb:bindings>
</jaxb:bindings>

Configure the JAXB plugin in your pom file as: 将pom文件中的JAXB插件配置为:

<build>
        <plugins>
            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <configuration>
                    <extension>true</extension>
                    <args>
                        <arg>-Xannotate</arg>
                    </args>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.jvnet.jaxb2_commons</groupId>
                        <artifactId>jaxb2-basics-annotate</artifactId>
                        <version>1.0.2</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

You have a class name conflict between your two Locale classes. 您的两个Locale类之间的类名冲突。

I got your code working by making the following changes. 通过进行以下更改,我使您的代码正常工作。 I didn't generate the JAXB classes from the XML schema, but simply updated them directly, so you'll have to figure out how to update the bindings file to get this result. 我不是从XML模式生成JAXB类,而是直接直接对其进行更新,因此您必须弄清楚如何更新绑定文件才能获得此结果。

  • Rename your Locale class, so it doesn't conflict with java.util.Locale , eg rename to LocaleType . 重命名您的Locale类,因此它与java.util.Locale不冲突,例如,重命名为LocaleType

  • Change org.mylib.schema.Locale to org.mylib.schema.LocaleType in LocaleXmlAdapter . 更改org.mylib.schema.Localeorg.mylib.schema.LocaleTypeLocaleXmlAdapter
    Note: You can now use import statements and not have to fully qualify the classes in the code. 注意:现在,您可以使用import语句,而不必完全限定代码中的类。

  • Make sure DelimitedDataSchema uses java.util.Locale , eg it still says Locale in the field and method declarations, and it imports java.util.Locale . 确保DelimitedDataSchema使用java.util.Locale ,例如,它在字段和方法声明中仍显示Locale ,并导入java.util.Locale

  • Add @XmlJavaTypeAdapter(LocaleXmlAdapter.class) to the locale field of DelimitedDataSchema . @XmlJavaTypeAdapter(LocaleXmlAdapter.class)添加到DelimitedDataSchemalocale字段中。

Now it works. 现在可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM