2012-04-14 4 views
5

Update - siehe Bearbeiten am unteren RandJAXB-Annotationen - Wie mache ich eine Liste von XmlIDRef-Elementen mit dem ID-Wert als Attribut anstelle von Elementtext?

IDREFS/keyrefs scheint in JAXB Annotationen möglich zu sein, aber der Schiedsrichter endet als Element Text oben.

Ich möchte, dass der Verweis ein Attribut eines Elements ist.

Zum Beispiel gegeben dieses Objektmodell:

@XmlType 
public class Employee { 
    @XmlID 
    @XmlAttribute 
    String name; 
    @XmlAttribute 
    int years; 
    @XmlAttribute 
    String foo; 
} 

@XmlType 
public class Office { 
    @XmlAttribute 
    String name; 
    @XmlElementWrapper 
    @XmlElement(name = "employee") 
    List<Employee> employees; 
} 

@XmlRootElement 
public class Company { 
    @XmlElementWrapper 
    @XmlElement(name = "office") 
    List<Office> offices; 
    @XmlElementWrapper 
    @XmlElement(name = "employee") 
    List<Employee> employees; 
} 

Ich würde das externalisierte XML-Format wie wie diese am Ende aussehen:

<company> 
    <offices> 
     <office name="nyc"> 
      <employees> 
       <!--*** id ref to employee name ***--> 
       <employee ref="alice"/> 
       <employee ref="bob"/> 
      </employees> 
     </office> 
     <office name="sf"> 
      <employees> 
       <employee ref="connie"/> 
       <employee ref="daphne"/> 
      </employees> 
     </office> 
    </offices> 
    <employees> 
     <!-- *** name is the id *** --> 
     <employee name="alice" years="3" foo="bar"/> 
     <employee name="bob" years="3" foo="bar"/> 
     <employee name="connie" years="3" foo="bar"/> 
     <employee name="daphne" years="3" foo="bar"/> 
    </employees> 
</company> 

Stattdessen das Beste, was ich tun kann, ist dies (mit den Anmerkungen oben in dem Java-Code aufgeführt):

<company> 
    <offices> 
     <office name="nyc"> 
      <employees> 
       <employee>alice</employee> 
       <employee>bob</employee> 
      </employees> 
     </office> 
     <office name="sf"> 
      <employees> 
       <employee>connie</employee> 
       <employee>daphne</employee> 
      </employees> 
     </office> 
    </offices> 
    <employees> 
     <employee name="alice" years="3" foo="bar"/> 
     <employee name="bob" years="3" foo="bar"/> 
     <employee name="connie" years="3" foo="bar"/> 
     <employee name="daphne" years="3" foo="bar"/> 
    </employees> 
</company> 

gibt es eine Möglichkeit ich die idref zwingen kann, Wert als Attribut des Mitarbeiters und nicht als Elementtext? Ich weiß, dass ich dies mit einem XML-Schema tun kann, aber ich möchte gerne Anmerkungen machen, wenn es überhaupt möglich ist.

Vielen Dank.

Bearbeiten Die Lösung von Torious unten funktioniert fast, aber es funktioniert nicht ganz unter bestimmten Umständen.

unmarshalling schlägt fehl, wenn die "offices" -Elemente (in der XML-Datei) die "employee" -Elemente enthalten, auf die das Office verweist. Die Mitarbeiterreferenzen werden nicht gefunden, und der EmployeeRef-Wrapper hat ein Null-Mitarbeiterobjekt. Wenn der "Angestellte" zuerst ist, funktioniert es.

Dies wäre nicht so ein Problem, aber die Marshal-Methode wird "Büros" zuerst, so dass der Versuch, zu entpacken, was gerade gerüstet wurde fehlschlägt.

Bearbeiten 2 Kommentar in Torious 'Antwort löst das Bestellproblem.

Antwort

4

Die Lösung ist eine XmlAdapter zu verwenden, die eine Employee in einer Instanz eines neuen Typs Wraps, EmployeeRef, die angibt, wie die XML-ID ref zur Karte:

@XmlType 
public class Office { 

    @XmlAttribute 
    String name; 

    @XmlElementWrapper 
    @XmlElement(name="employee") 
    @XmlJavaTypeAdapter(EmployeeAdapter.class) // (un)wraps Employee 
    List<Employee> employees; 
} 

@XmlType 
public class EmployeeRef { 

    @XmlIDREF 
    @XmlAttribute(name="ref") 
    Employee employee; 

    public EmployeeRef() { 
    } 

    public EmployeeRef(Employee employee) { 
     this.employee = employee; 
    } 
} 

public class EmployeeAdapter extends XmlAdapter<EmployeeRef, Employee> { 

    @Override 
    public EmployeeRef marshal(Employee employee) throws Exception { 
     return new EmployeeRef(employee); 
    } 

    @Override 
    public Employee unmarshal(EmployeeRef ref) throws Exception { 
     return ref.employee; 
    } 
} 

Viel Glück.

+0

yup, das hat den Trick gemacht. Vielen Dank. – marathon

+0

ok, das funktioniert unter einigen (meisten) Umständen nicht ganz. Siehe Bearbeiten über – marathon

+1

. Kommentieren Sie die 'Company' Klasse mit' @XmlType (propOrder = {"employees", "offices"}) ', um die gewünschte Reihenfolge beim Marshalling zu erzwingen. – Torious

2

Hinweis: Ich bin die EclipseLink JAXB (MOXy) Leitung und Mitglied der JAXB (JSR-222) Expertengruppe.

Unten finden Sie ein Beispiel, wie dies mit MOXy unter Verwendung der @XmlPath Annotation gemacht werden kann. Aufgrund eines bug müssen Sie ab dem 17. Mai 2012 ein EclipseLink 2.4.0-Label verwenden. Dieses Update wird auch dem Stream EclipseLink 2.3.3 (ab dem 18. Mai 2012) hinzugefügt.Sie können eine nächtliche Label unter der folgenden Adresse herunterladen:

Büro

Auf der employees Eigenschaft Sie die @XmlIDREF Anmerkung in Kombination mit der @XmlPath Anmerkung können das bekommen gewünschte Zuordnung. @XmlIDREF teilt der JAXB-Implementierung mit, einen Fremdschlüssel anstelle des Objekts zu schreiben.

package forum10150263; 

import java.util.List; 
import javax.xml.bind.annotation.*; 
import org.eclipse.persistence.oxm.annotations.XmlPath; 

@XmlType 
public class Office { 
    @XmlAttribute 
    String name; 

    @XmlPath("employees/employee/@ref") 
    @XmlIDREF 
    List<Employee> employees; 
} 

Mitarbeiter

Das Gegenstück zum @XmlIDREF ist @XmlID. @XmlID wird verwendet, um den Primärschlüssel für ein Objekt anzugeben.

package forum10150263; 

import javax.xml.bind.annotation.*; 

@XmlType 
public class Employee { 
    @XmlID 
    @XmlAttribute 
    String name; 

    @XmlAttribute 
    int years; 

    @XmlAttribute 
    String foo; 
} 

Firma

Jedes Objekt über den @XmlIDREF Mechanismus verwiesen muss auch durch eine Einschließungsbeziehung referenziert werden. In diesem Beispiel wird dies durch die Eigenschaft employees erreicht.

package forum10150263; 

import java.util.List; 
import javax.xml.bind.annotation.*; 

@XmlRootElement 
public class Company { 
    @XmlElementWrapper 
    @XmlElement(name = "office") 
    List<Office> offices; 

    @XmlElementWrapper 
    @XmlElement(name = "employee") 
    List<Employee> employees; 
} 

jaxb.properties

Um moxy als JAXB-Provider geben Sie benötigen eine Datei jaxb.properties im selben Paket wie Ihr Domain-Modell mit dem folgenden Eintrag (siehe Specifying EclipseLink MOXy as Your JAXB Provider)

Namen hinzufügen
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

Demo

Die JAXB-Standard-APIs werden zum Zurückmarshalieren/Marshalling von XML verwendet.

package forum10150263; 

import java.io.File; 
import javax.xml.bind.*; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(Company.class); 

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     File xml = new File("src/forum10150263/input.xml"); 
     Company company = (Company) unmarshaller.unmarshal(xml); 

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(company, System.out); 
    } 

} 

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?> 
<company> 
    <offices> 
     <office name="nyc"> 
     <employees> 
      <employee ref="alice"/> 
      <employee ref="bob"/> 
     </employees> 
     </office> 
     <office name="sf"> 
     <employees> 
      <employee ref="connie"/> 
      <employee ref="daphne"/> 
     </employees> 
     </office> 
    </offices> 
    <employees> 
     <employee name="alice" years="3" foo="bar"/> 
     <employee name="bob" years="3" foo="bar"/> 
     <employee name="connie" years="3" foo="bar"/> 
     <employee name="daphne" years="3" foo="bar"/> 
    </employees> 
</company> 

Weitere Informationen