10 Stimmen

JAXB Marshalling und Generika

Ich versuche, die Introspektion von JAXB zu verwenden, um einige vorhandene Domänenobjekte, die mit JAXB-Annotationen markiert sind, zu marshallieren und unmashallieren. Die meisten Dinge funktionieren wie erwartet, aber ich habe ziemliche Schwierigkeiten, eine ziemlich einfache Klasse zu serialisieren. Diese Klasse wird als @XmlElement in einer Reihe von Beans verwendet und sieht in etwa so aus:

public class Range<E extends Comparable<E>> implements Serializable {
    protected boolean startInclusive, endInclusive;
    protected E       start, end;

    public Range(){
            startInclusive = endInclusive = true;
    }

    public boolean contains(E value){...}

    public E getEnd() {
            return end;
    }

    public void setEnd(E end) {
            this.end = end;
    }

    public boolean isEndInclusive() {
            return endInclusive;
    }

    public void setEndInclusive(boolean endInclusive) {
            this.endInclusive = endInclusive;
    }

    public E getStart() {
            return start;
    }

    public void setStart(E start) {
            this.start = start;
    }

    public boolean isStartInclusive() {
            return startInclusive;
    }

    public void setStartInclusive(boolean startInclusive) {
            this.startInclusive = startInclusive;
    }
}

Ich habe versucht, das Folgende zu tun, ohne Erfolg, JAXB ist immer noch böse mit der Schnittstelle Comparable.

public class DoubleRange extends Range<Double> {}

Die Verwendung von Range und DoubleRange als Rückgabetypen für die Bean Getter führt zu einer Ausnahme wie:

java.lang.Comparable is an interface, and JAXB can't handle interfaces.
    this problem is related to the following location:
        at java.lang.Comparable
        at protected java.lang.Comparable com.controlpath.util.Range.start
        at example.util.Range
        at example.util.DoubleRange
        at public example.util.DoubleRange example.domain.SomeBean.getRange()
        at example.domain.SomeBean

Mir ist klar, dass in den meisten Fällen List<T> und Map<T, U> nur funktionieren, weil die JAXB-Spezifikation spezielle Bestimmungen für diese Typen hat, wenn sie auf Beans angetroffen werden, aber gibt es eine Möglichkeit, zu übermitteln, was ich will, um die JAXB-Introspektions-Engine, ohne Bereich mit nicht-generischen Feldern neu zu implementieren?

5voto

ivan_ivanovich_ivanoff Punkte 18553

Sie können einen benutzerdefinierten Adapter schreiben (der nicht den XmlAdapter von JAXB verwendet), indem Sie Folgendes tun:

1) Deklarieren Sie eine Klasse, die alle Arten von Elementen akzeptiert und JAXB-Annotationen hat und sie nach Belieben behandelt (in meinem Beispiel konvertiere ich alles in String)

@YourJAXBAnnotationsGoHere
public class MyAdapter{

  @XmlElement // or @XmlAttribute if you wish
  private String content;

  public MyAdapter(Object input){
    if(input instanceof String){
      content = (String)input;
    }else if(input instanceof YourFavoriteClass){
      content = ((YourFavoriteClass)input).convertSomehowToString();
    }else if(input instanceof .....){
      content = ((.....)input).convertSomehowToString();
    // and so on
    }else{
      content = input.toString();
    }
  }
}

// I would suggest to use a Map<Class<?>,IMyObjToStringConverter> ...
// to avoid nasty if-else-instanceof things

2) Verwenden Sie diese Klasse anstelle von E in Ihrer zu bezeichnenden Klasse

ANMERKUNGEN

  • Natürlich würde dies no Arbeit für komplexe (verschachtelte) Datenstrukturen.
  • Man muss sich überlegen, wie man das wieder rückgängig machen kann, das könnte etwas kniffliger sein. Wenn es zu knifflig ist, warte auf einen besseren Vorschlag als meinen ;)

1voto

どうですか?

public class Range<**E extends Number**> implements Serializable { ...
  • Nummer ist eine Klasse

  • Ich wette, JAXB weiß es Standard Regeln für das Einkuppeln und Auskuppeln für die Nummer

Für das Unmarshalling auf einen bestimmten Typ benötigen Sie einen XmlAdapter wie I hier beschrieben: JAXB Vererbung, unmarshal zu Unterklasse der marshaled Klasse

0voto

ng. Punkte 6919

Versuchen Sie etwas wie Einfache XML-Serialisierung Es bietet Unterstützung für generische Typen in XML-Elementen mit einer Reihe von Annotationen wie @Element und @ElementList. Das Programmiermodell ist sehr ähnlich, aber einfacher als JAXB.

0voto

StaxMan Punkte 107669

Eigentlich ist mir nicht ganz klar, warum dies nicht funktionieren sollte. Es scheint, dass JAXB in der Lage sein sollte, bestimmte Untertypen korrekt aufzulösen: wenn (und nur wenn!) dieser Typ NICHT der Root-Typ ist (was er laut Ihrer Beschreibung nicht ist). Ich meine, es ist nur eine Bohne; wenn also eine Bohne mit T durch einen direkten Typ ersetzt wird, sollte auch die generische Version funktionieren, wenn die Unterklassenbildung verwendet wird, um Typen zu binden (wie es im Beispiel gemacht wird).

Könnte es sich also um einen Fehler in der Implementierung handeln?

0voto

Daniel Szabo Punkte 11

Ich habe Folgendes getan:

  1. Erstellen Sie eine leere Klasse und markieren Sie sie für JAXB

    @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public abstract class Marshallable {}

  2. Jede Klasse, die als Typ in der generischen Klasse landen soll, muss diesen Marshallable-Typ erweitern

    (...) public class A extends Marshallable { (...)

  3. Beschränken Sie Ihre generische Klasse darauf, nur Unterklassen von Marshallable als generischen Typ zuzulassen

    public class GenericType<T extends Marshallable> {

  4. Verwenden Sie die Klasse so, wie Sie es ursprünglich vorhatten

    A a = new A();

    jaxbMarshaller.marshal(new GenericType(a), outputStream);

Und jetzt wird Jaxb mit jedem Typ arbeiten, der Marshallable erweitert, sogar als Teil eines generischen Typs.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X