2016-11-21 17 views
2

Das Problem:java.util.Locale in JAXB Verwendung generierten Klassen mit XMLAdapter

Basierend auf der folgende Dokumentation von Oracle über die Verwendung des java.util.Locale vorgesehen: [Internationalization: Understanding Locale in the Java Platform] habe ich die folgende Frage im Zusammenhang mit JAXB und das Gebietsschema.

Ich habe eine XML-Datei, die wie folgt aussieht:

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

die auf den folgenden XML-Schema basiert:

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

Das Problem ist jetzt, dass ich die folgenden generierten Klassen für das bekommen LocaleType xml complexType, der den tatsächlichen java.util.Locale-Datentyp in der generierten DelimitedDataSchema-Klasse nicht zu reflektieren scheint. Ich hätte erwartet, dass dies vom Typ java.util.Locale und nicht vom Typ org.mylib.schema.LocaleType wäre.

Die generierten Klassen von JAXB 2.x sind:

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:

@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:

Ich folgte tapfer die Anweisungen in den folgenden Blog-Beiträge von Blaise Doughan über JAXB XmlAdapters: JAXB and Package Level XmlAdapters und auch XmlAdapter - JAXB's Secret Weapon

So habe ich eine XMLAdapter mich der Hoffnung, dass die erzeugte Klasse (DelimitedDataSchema) die Java enthalten würde .util.Locale gibt den Datentyp im Getter und den java.util.Locale-Parameterdatentyp im Setter zurück. Was ich fälschlicherweise angenommen habe.

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

die JAXB Bibliothek wissen zu lassen, dass sie die LocaleXmlAdapter verwenden müssen, sofern ich die Bibliothek mit einer externen Bindungs ​​Datei, in der die LocaleXmlAdapter für die Locale-Klasse definiert ist .

Externe JAXB Einbindemappe:

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

Nun ist der seltsame Teil, die ich nicht bekommen, ist offensichtlich, dass ich erwartet hätte, dass die JAXB Bibliothek den org.mylib.schema.LocaleType Typen übersetzen würde in den java.util.Locale Typ für die DelimitedDataSchema Klasse, so würden Sie die folgende Methode Signaturen in der DelimitedDataSchema-Klasse:

  • öffentliche java.util.Locale getLocale() {}

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

Was ich will zu tun ist, dass der java.util.Locale Datentyp anstelle des org.mylib.schema.LocaleType Datentyp verwendet wird. Wie bekomme ich sonst die Übersetzung zwischen dem Benutzercode und dem JAXB generierten Code? Ich kann die LocaleXmlAdapter-Klasse nicht selbst aufrufen, um den Gebietsschematyp für mich zu übersetzen, der von der JAXB-Bibliothek ausgeführt werden muss, aber ich möchte: getLocale() aufrufen und dafür einen java.util.Locale-Datentyp abrufen.

Was mache ich "falsch"?

Update:

Bisher habe ich herausgefunden, dass die < jaxb: basetype/> sollten nicht verwendet werden. Statt der < xjc: > basetype: javatype > sollte in der Bindungsdatei als untergeordnetes Element von < jaxb verwendet werden. Ich nahm auch fälschlicherweise an, dass die < jaxb: baseType > unter dem LocaleType-Knoten definiert werden musste, was NICHT wahr ist. Sie muss unter dem Elementknoten des DelimitedSchemaType-Knotens und des FixedWidthSchemaType-Knotens definiert werden. Gefällt mir:

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

Das sollte korrekt sein, aber irgendwie hat der XJC-Compiler Kompilierfehler erzeugt. Der folgende Fehler tritt auf: „. Compiler nicht in der Lage war, diese Umwandlung Anpassung zu ehrt Es wird zu einer falschen Stelle angebracht ist, oder sein inkonsistent mit anderen Bindungen“

[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. 

Es hält Gezeter über , während es keinen Fehler gibt, innerhalb der Bindungsdatei zu finden.

Ich habe meine Bindungsdatei verbessert, aber immer noch ist etwas nicht "richtig". Ich kann den genauen Ort, an dem es falsch läuft, nicht genau bestimmen.

BTW: Ich nehme die folgenden Tools verwenden:

  • Oracle Java JDK 64-Bit-Version 1.8.0_112-b15
  • xjc, Version 2.2.8-b130911.1802 (mit oben genannten ausgeliefert JDK)
  • maven3, Version 3.3.9
  • IntelliJ IDEA 2016,3, Version 163.7743.44
  • maven-jaxb2-Plugin, Version 0.13.1

Weil ich jetzt seit ein paar Tagen damit zu kämpfen habe, habe ich ein Kopfgeld begonnen. Die Person, die wirklich löst das Problem mit der externen Bindings-Datei und korrekte Anmerkungen erhalten meine Bounty-Punkte.

Antwort

3

Diese Frage/Problem hat mehrere Mängel darin muss das zuerst adressiert werden, um die Frage/das Problem erfolgreich zu beantworten. Weil Sie eine detaillierte kanonische Antwort angefordert, die alle Bedenken Adressen, hier ist es:

Zunächst einige Beobachtungen:

  1. Die DelimitedDataSchema.java und FixedWidthDataSchema.java sowohl ein Objektfeld ‚locale‘ enthalten von Datentyp org.mylib.schema.LocaleType, der zum Datentyp java.util.Locale korrigiert werden muss.
  2. Das Objektfeld 'locale' in DelimitedDataSchema.java und FixedWidthDataSchema.java muss die Annotation @XmlJavaTypeAdapter enthalten, die jetzt nicht mehr vorhanden ist.
  3. Das Objektfeld 'locale' in DelimitedDataSchema.java und FixedWidthDataSchema.java enthält bereits eine @ XmlElement-Annotation, die jetzt nur das Element 'required' angibt. Es muss auch das Element "type" enthalten.
  4. Die zu verortenden XML-Daten sind eine ComplexType-Datenstruktur, nämlich: LocaleType, die nur drei Attribute vom Typ xs: string hat. Der aktuelle Xml-Java-Compiler (= XJC) unterstützt (noch) keine ComplexTypes, die zu den richtigen Annotationen verarbeitet werden sollen.
  5. Der Knoten LocaleType in der externen Bindungsdatei enthält eine falsch definierte Eigenschaft. Der 'localeType' hat nur drei Attribute, die nicht definiert sind.
  6. Der definierte LocaleXmlAdapter ist falsch definiert und sollte in die Eigenschaft (xml-Element) der DelimitedDataSchema- und FixedWidthDataSchema-Klasse verschoben werden.
  7. Die < jaxb: baseType > Deklarationen des Feldes/Element 'Locale' in der DelimitedDataSchema und FixedWidthDataSchema-Klasse fehlt.
  8. Sie haben Ihre maven pom.xml-Datei nicht in Ihrer Frage angegeben. Bitte geben Sie dies auch an, wenn Sie Ihre Frage das nächste Mal stellen. Es liefert wertvolle Informationen für die Benutzer, die Ihre Frage so präzise wie möglich beantworten möchten. Und auch für neue Suchende, die mit den gleichen Fragen fertig werden, die Sie hatten, finden Sie die Antworten, die sie in Ihrem Beitrag benötigen. Sei so vollständig wie möglich.

Zweitens einige Modifikationen/Zusatz:

  1. Ihre XML-Schemadatei ist gut definiert. Daher sind in dieser Datei keine Änderungen erforderlich.
  2. Ihre Bindungsdatei (datasource.jxb) enthält Fehler.

Um diese Mängel zu beheben, gehen Sie wie folgt vor:

  1. Empfehlung: Benennen Sie den Klassennamen des LocaleType zu XmlLocale, so dass Sie leicht die XmlLocale.class und LocaleXmlAdapter.class zusammen passen können, ohne zu verwechseln zwischen java.util.Locale und org.mylib.schema.Locale (das jetzt umbenannt wird in org.mylib.schema.XmlLocale)
  2. korrektur: entferne das element und das untergeordnete element komplett, da diese NICHT angegeben werden dürfen auf der Ebene von LocaleType, aber auf der Ebene des Elements 'locale' in DelimitedSchemaType und FixedWidthSchemaType.
  3. Korrektur: Innerhalb des complexType definieren DelimitedSchemaType und FixedWidthSchemaType einen untergeordneten XPath-Knoten, der das in diesen complexTypes angegebene Element 'locale' darstellt.
  4. Korrektur: unter den 'locale' Elementknoten innerhalb des complextType DelimitedSchemaType und FixedWidthSchemaType ein < jaxb definiert: mit einem Kind < jaxb Eigenschaft > Element: basetype > Element. Geben Sie der Unterkunft einen Namen. Dies kann derselbe Name sein wie im XML-Schema angegeben, kann aber auch anders benannt werden. Wenn Sie es anders als das XML-Schema benennen, beachten Sie, dass Ihr Objektfeld wie der Name < jaxb: property > und nicht der Name des XML-Schemaelements aufgerufen wird. Dies wirkt sich auch auf die Namen der Getter- und Setter-Methode aus.
  5. Korrektur: Definieren Sie den Datentyp der Eigenschaft mit dem < jaxb: baseType > Namen. Der gewünschte Objektfelddatentyp ist vom Typ java.util.Locale. So erhalten Sie: < jaxb: baseType name = "java.util.Locale"/>. Sie geben den org.mylib.schema.XmlLocale-Datentyp als den Namen nicht an!
  6. Zusatz: Um die @XmlJavaTypeAdapter auf das Element 'locale' in DelimitedDataSchema und FixedWidthDataSchema Klassen hinzuzufügen, würden Sie normalerweise angeben, dies mit dem < xjc: basetype > Elemente. Da der Xml-Java-Compiler (= XJC) diese Art der Konvertierung zwischen complexTypes und Java-Datentypen jedoch (noch) NICHT verarbeitet, können Sie die Standardspezifikation nicht wie in der Dokumentation beschrieben verwenden.Dies funktioniert nur (noch) nicht mit dem aktuellen XJC:

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

    So müssen Sie liefern die @XmlJavaTypeAdapter sich Annotation. Hier kommt das jaxb2-basics-annotate Plugin [link] zum Einsatz. Mit diesem Plugin können Sie JEDEN vorhandenen Java-Kommentar an beliebiger Stelle innerhalb einer Java-Klasse kommentieren: Klassen-, Feldebene, Methodenlevel usw. Um die 'fehlende' Annotation zu kommentieren, müssen Sie einige Dinge einrichten , die später beschrieben werden.
    In der Bindungsdatei müssen Sie die folgenden Einstellungen vornehmen:
    a) Geben Sie den Namespace (annox) an, der vom Plugin im Stammverzeichnis Ihrer Bindungsdatei verwendet wird;
    b) Spezifizieren Sie die ErweiterungBindingPrefixed im Stamm Ihrer Bindings-Datei;

    <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) die bevorzugte Annotation unter Verwendung des Elements.
    Da der Adapter angegeben werden muss, auf dem ‚locale‘ Objektfeldes der DelimitedDataSchema und FixedWidthDataSchema Klassen muss das Beschriften Element innerhalb des < jaxb angegeben werden: Bindings > Knoten des ‚locale‘:

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

    Ebenso für den ComplexType FixedWidthSchemaType. Beachten Sie, dass der vollständige Paketname und Klassenname einschließlich der Annotationsparameter angegeben werden müssen!

  7. Empfehlung: Stellen Sie sicher, dass alle Knoten in der Bindungsdatei angegeben und den XML-Schemaelementen und -Attributen zugeordnet sind. Dies wird empfohlen, da Sie durch Angabe jedes Knotens und Angabe eines Namens in der Bindungsdatei jedem Namen einen Namen geben (der gleich oder verschieden vom Namen des XML-Schemas sein kann), dass die generierten Klassen, Felder, Getter/Setter den Namen YOU erhalten mag ich. Dies verhindert später eine Menge manueller Arbeit, wenn jemand beschließt, die Namen des XML-Schemas umzubenennen, was offensichtlich dazu führt, dass Java-Klassen neu kompiliert werden und die Referenz nicht mit anderen (manuell geschriebenen) Klassen übereinstimmt. Beispiel: Ihre manuell geschriebene XmlAdapter-Klasse: LocaleXmlAdapter, die auf Methodennamen in der generierten XmlLocale-Klasse verweist. Sie entkoppeln die XML-Schema-Namen von den Java-Namen mithilfe einer einzelnen Bindungsdatei. Das erspart Ihnen später viele Probleme ( ) (Vertrauen Sie mir: Ich habe es schon oft in verschiedenen Entwicklungsteams erlebt)! Das verschwendet nur wertvolle Zeit und Geld. Wenn diese Bindungsdatei vollständig angegeben ist, müssen Sie nur den Namen des XML-Schema-Knotens übernehmen, die Java-Seite ändert sich nicht!

    Ihre Einbindemappe ergibt sich nun in dieser vollständigen Bindungsdatei:

    <?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. Ihre Maven pom.xml Datei nicht angegeben ist, sondern für die gesamte Überblick über dieses Thema, es wird auch hier erwähnt.

Bei der Generierung von JAXB-Klassen mithilfe von Maven müssen einige Punkte berücksichtigt werden. Gehen Sie folgendermaßen vor, um das Richtige zu tun: - Da JAXB XJC spezifisch ist, welche Java-Version Sie kompilieren und ausführen, sollten Sie angeben, zu welcher Quell- und Ziel-Java-Version es kompiliert werden muss. Dies kann mit dem Maven-Compiler-Plugin erreicht werden. Anstatt XJC die Java-Klassen mit der Standardstufe Version 1.5 kompilieren zu lassen, geben Sie jetzt Version 1.8 an.

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

Drittens einige Ergebnisse (vorher und nachher):

Wenn die Maven Ausführung kompiliert Phase, bevor die Änderungen in den Bindungen und Maven pom.xml Datei Datei, beendete man mit der folgenden DelimitedDataSchema Klasse bis (Ausschnitt dargestellt):

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) 

} 

bei der Ausführung des maven-Phase nach den Änderungen in den Bindungen kompilieren Datei und maven pom.xml Datei, beendete man mit der folgenden DelimitedDataSchema Klasse bis (Auszug) angezeigt:

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; 

} 

Das Ergebnis ist klar: Die gewünschte Lösung wird unter Verwendung der oben genannten Lösung erreicht. Beachten Sie, dass das @ XmlElement jetzt einen zusätzlichen Parameter enthält: type = XmlLocale.class. Beachten Sie, dass der @ XmlJavaTypeAdapter nur den Parameter LocaleXmlAdapter.class enthält. Sie können auch die gleiche Situation anders schreiben, wie:

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

, die genau erfüllt gleich. Beachten Sie jedoch, dass Sie den Wert < jaxb: baseType name = "java.util.Locale"/> angeben müssen, da das Objektfeld als java.util.Locale definiert sein muss. Derzeit ist dies der einzige Weg, dies zu erreichen. Sie können die Annotation @XmlJavaTypeAdapter (type = XmlLocale, value = LocaleXmlAdapter.class) nicht einfach mit dem jaxb2-basics-annotate-Plug-in angeben, da die Objektfeldeigenschaft dann NICHT in den java.util.Locale-Datentyp geändert wird.

In der nahen Zukunft hoffe ich, dass der Xml Java Compiler (XJC) complexTypes unterstützt und die Datentypen, die marshalled/unmarshalled sind, durch Inspektion des benutzerdefinierten geschriebenen XmlAdapter-Parameters und der Rückgabetypen unterscheidet.

Viertens, die Beweise (Rangierung und Unmarshalling)

Es gibt ein Sprichwort, das sagt: „Der Beweis im Essen des Puddings ist“, das so etwas wie dies bedeutet: wenn Sie etwas geschaffen, und es sieht gut, der einzige Weg, um zu wissen, ob es wirklich gut ist (dh dass es funktioniert), besteht darin, es zu testen. In diesem Fall Marshalling/Unmarshalling. Im Fall der Pudding, Verkostung ist die einzige Möglichkeit, absolut sicher sein, dass das Rezept großartig ist!

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

Rangierung weggelassen wird, dass der Leser überlassen, zu implementieren.

Ende des Beispiels.

Beachten Sie, dass JAXB selbst das org.mylib.schema.XmlLocale und java.util.Locale mit dem LocaleXmlAdapter marshalieren und abmarshalieren kann. Es ist also nicht der JAXB-Kern, der in diesem Fall der Unruhestifter ist. Der Xml Java Compiler ist dafür verantwortlich, dass (noch) keine komplexenTypen akzeptiert werden. Dies sind die aktuellen Einschränkungen/Kurzschlüsse des XJC und werden hoffentlich in naher Zukunft gelöst werden!

Letzte zu erwägende Wörter: Wenn der geschlagene Weg dich nicht dorthin bringt, dann nimm die Straße, die tut. Langsam und stetig gewinnt das Rennen!

Einige Fußnoten: Setzen Sie den Quell- und Zielcompiler-Level auf 1,8 statt auf 1.5 (das ist die Standardeinstellung) mit dem Maven-Compiler-Plugin.

Wichtiger Hinweis zum maven-compiler-plugin: "Das bloße Setzen der Zieloption garantiert nicht, dass Ihr Code tatsächlich auf einer JRE mit der angegebenen Version läuft. Die Gefahr besteht in der unbeabsichtigten Verwendung von APIs, die nur in späteren JREs existieren Das kann dazu führen, dass Ihr Code zur Laufzeit mit einem Verknüpfungsfehler fehlschlägt Um dieses Problem zu vermeiden, können Sie entweder den Boot-Klassenpfad des Compilers so konfigurieren, dass er der Ziel-JRE entspricht, oder das Animal Sniffer Maven-Plugin verwenden, um sicherzustellen, dass Ihr Code keine unbeabsichtigten APIs verwendet. Auf die gleiche Weise garantiert das Festlegen der Quellenoption nicht, dass Ihr Code tatsächlich auf einem JDK mit der angegebenen Version kompiliert wird Weitere Informationen zum Kompilieren Ihres Codes mit einer bestimmten JDK-Version, die zum Starten von Maven verwendet wird, finden Sie unter Kompilieren Verwendung einer Unterschiedliches JDK-Beispiel. "

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

Mit anderen Worten: Wenn Sie Ihr Projekt sicherstellen möchten, dass die richtige Java-Version verwendet und somit korrekt JAXB Version, verwenden Sie das Tier Sniffer Maven Plugin. Siehe: http://www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin/

Das ist alles;)

+0

Beeindruckende Antwort! Ich habe es jetzt funktioniert. Endlich! Danke für Ihr Bemühen. – user504342

+0

Gern geschehen! XJC kann manchmal eine Herausforderung sein. – CookingWithJava

0

Sie haben einen Klassennamenkonflikt zwischen Ihren beiden Klassen Locale.

Ich habe Ihren Code funktioniert, indem Sie die folgenden Änderungen vornehmen. Ich habe die JAXB-Klassen nicht aus dem XML-Schema generiert, sondern einfach direkt aktualisiert, sodass Sie herausfinden müssen, wie Sie die Bindungsdatei aktualisieren, um dieses Ergebnis zu erhalten.

  • Benennen Sie Ihre Locale Klasse, so dass es nicht in Konflikt mit java.util.Locale, z.B. Umbenennen zu LocaleType.

  • Ändern org.mylib.schema.Locale zu org.mylib.schema.LocaleType in LocaleXmlAdapter.
    Hinweis: Sie können jetzt Importanweisungen verwenden und müssen die Klassen im Code nicht vollständig qualifizieren.

  • Sicherstellen, dass DelimitedDataSchemajava.util.Locale verwendet, z. In den Feld- und Methodendeklarationen wird weiterhin Locale angezeigt, und es importiert java.util.Locale.

  • Fügen Sie @XmlJavaTypeAdapter(LocaleXmlAdapter.class) in das Feld locale von DelimitedDataSchema ein.

Jetzt funktioniert es.

+0

Hallo Andreas, ich habe unternommenen Schritte 1 und 2 erfolgreich, Schritt 3 und 4 sind die Probleme verursachen auf meiner Seite. Ich weiß nicht genau, wie die Annotation (in Schritt 4 erwähnt) innerhalb der DelimitedDataSchema-Klasse mithilfe der Bindungsdatei hinzugefügt wird. Ich muss die Bindungsdatei verwenden. Ich habe die Spezifikation "Die Java-Architektur für XML Binding (JAXB) 2.2 Final Release" vor mir, aber ich suche wahrscheinlich in der falschen Sektion? – user504342

+0

Ich habe Ihren Vorschlag versucht und es funktioniert nur, wenn ich nach der Generierung die generierten Java-Dateien bearbeite und den @ XmlJavaTypeAdapter (value = LocaleXmlAdapter.class) hinzufüge. Ich muss in der Lage sein, die Java-Dateien zu generieren, ohne sie später manuell zu bearbeiten. Das Problem ist, dass die verursacht einen Fehler: "Compiler konnte diese Konvertierung Anpassung nicht gerecht zu werden Es ist an einem falschen Ort angebracht, oder es ist nicht mit anderen Bindungen vereinbar. Warum funktioniert das nicht? – user504342

+0

Danke für deine Hilfe Andreas. Ich habe es funktioniert mit der Antwort von Benutzer 'CookingWithJava' gegeben. Endlich! – user504342

1

JAXB scheint nur Unterstützung xml simple Typ nur in <xjc:javaType> und daher sehen Sie Fehler "...unable to honor...". Siehe hierzu related question.

Wie generiert man @XmlJavaTypeAdapter Annotation auf generierte Klasse. (Basierend auf jaxb2-annotate-plugin)

@XmlJavaTypeAdapter Geben Sie Datei in der Bindung, wie unten

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

Konfigurieren der JAXB-Plugin in Ihrem pom-Datei wie folgt angezeigt:

<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> 
+0

Die Annotation XmlJavaTypeAdapter ist im LocaleType falsch platziert. Sie muss in den Knoten DelimitedSchemaType und FixedWidthSchemaType unter dem Gebietsschema-Knoten verschoben werden, wie vom Benutzer 'CookingWithJava' erwähnt. Danke für Ihre Hilfe. – user504342

Verwandte Themen