2009-06-23 19 views
14

nehme an, ich habe diese Klasse:Unmarshalling Sammlungen in jaxb

public class A { 

    private HashMap<String, B> map; 

    @XmlElement 
    private void setB(ArrayList<B> col) { 
     ... 
    } 

    private ArrayList<B> getB() { 
     ... 
    } 

} 

Beim Versuch, ein XML-Dokument zu dieser Klasse entordnen jaxb mit merke ich, dass statt der setb() -Methode aufrufen und mir die Liste der B Senden Instanzen JaxB ruft tatsächlich getB() auf und fügt der zurückgegebenen Liste die B-Instanzen hinzu. Warum?

Der Grund, dass der Setter aufgerufen werden soll, ist, dass die Liste eigentlich nur ein temporärer Speicher ist, aus dem ich das Kartenfeld erstellen möchte, also dachte ich, es im Setter zu tun.

Danke.

Antwort

8

das ist die Art und Weise, wie jaxb Sammlungen bearbeitet. Sie müssen sicher sein, dass Sie eine Nicht-Null-Sammlung haben, wenn Jaxb versucht, zu entpacken.

es ein Plugin ist (es nie selbst), aber hilfreich sein kann: https://jaxb2-commons.dev.java.net/collection-setter-injector/

+0

+1 für den Hinweis, wie Jaxb Collections macht, aber dieses Plugin wird nicht helfen. Das ist ein Plugin für den XJC Code Generator, m und ändert nicht das Laufzeitverhalten von JAXB. – skaffman

+0

Ich habe einen alten E-Mail-Thread gelesen, der behauptet, dass dieses Verhalten in JaxB 2.1 behoben wurde. Ich würde erwarten, dass es eine Eigenschaft auf JaxbContext.newInstance (Klassen, * Eigenschaften *) gibt, die das Marshalling-Verhalten steuert, aber ich kann es nicht finden. – Justin

3

JAXB Probleme unterstützen Schnittstellen und abstrakte Klassen hat; Es weiß normalerweise nicht, welche Unterklasse zu instanziieren ist. Das Problem ist, es ist ein gemeinsames Muster eine Klasse entlang der Linien zu haben:

ArrayList list; 

@XMLElement 
public List getList() {return this.list;} 

Um dies zu umgehen, ist JAXB nicht einmal versuchen, die Festigkeitsklasse (zB List) aus dem Getter/Setter abgeleitet zu instanziiert Paar, wenn es eine Sammlung ist. Es nimmt nur an, dass es nicht null und änderbar ist.

Wahrscheinlich die einfachste Arbeit ist, Ihre Geschäftsschnittstelle mit @XMLTransient zu markieren und ein anderes Getter/Setter-Paar mit @XMLElement für die Ansicht für die Daten hinzuzufügen, die Sie JAXB verfügbar machen möchten. Normalerweise mache ich diese eher geschützt als öffentlich, weil es mir egal ist, ob ich das etwas alberne JAXB-Verhalten als Teil des öffentlichen Auftrags meiner Klassen habe.

6

Hy,

Sie es mit jaxb verwenden können, es ist Arbeit !!! (Mit Maven ....)

<plugin> 
      <groupId>org.jvnet.jaxb2.maven2</groupId> 
      <artifactId>maven-jaxb2-plugin</artifactId> 
      <executions> 
       <execution> 
        <goals> 
         <goal>generate</goal> 
        </goals> 
       </execution> 
      </executions> 
      <configuration> 
       <args> 
        <arg>-Xcollection-setter-injector</arg> 
       </args> 
       <plugins> 
        <plugin> 
         <groupId>net.java.dev.vcc.thirdparty</groupId> 
         <artifactId>collection-setter-injector</artifactId> 
         <version>0.5.0-1</version> 
        </plugin> 
       </plugins> 
       <schemaDirectory>src/schemas</schemaDirectory> 
       <generateDirectory>src/main/java</generateDirectory> 
       <extension>true</extension> 
      </configuration> 
     </plugin> 

und Sie erhalten Ihre Setter für Ihre Sammlung

es Hoffnung würden die Leute

bye helfen

+0

Um das Paket auszuwählen, können Sie auch den Parameter com.your.package hinzufügen – George

1

Jaxb2 Unmarshaller definiert eine Listener-Schnittstelle, die aufgerufen wird jedes Mal, wenn ein Objekt nicht gemarshallt wurde. Sie können einen benutzerdefinierten Listener definieren, um Setter-Methoden für alle Sammlungen (oder für Unterobjekte) aufzurufen. Dies sollte mit einer der bean utils-Klassen ziemlich einfach zu tun sein. Ich suche nach einer bestehenden Implementierung, obwohl ich keine sehe.

JAXBContext context = JAXBContext.newInstance(classesToBeBound); 
m_unmarshaller = context.createUnmarshaller(); 
m_unmarshaller.setListener(
    new Unmarshaller.Listener() { 
    public void afterUnmarshal(Object target, Object parent) { 
    for (Property p : getBeanProperties(target.getClass())) 
     if (p.isCollectionType() || p.isCompositeType()) 
     p.invokeSetter(p.invokeGetter()); 
    } 
    }); 

Wenn Sie den Federrahmen verwenden, es ist ziemlich einfach:

new Unmarshaller.Listener() { 
     public void afterUnmarshal(Object target, Object parent) { 
      BeanWrapper wrapper = new BeanWrapperImpl(target); 
      for (PropertyDescriptor pd : wrapper.getPropertyDescriptors()) { 
       if (pd.getPropertyType() != null) { 
         if (!BeanUtils.isSimpleProperty(pd.getPropertyType())) { 
          try { 
           Method setter = pd.getWriteMethod(); 
           if (setter != null) { 
            Method getter = pd.getReadMethod(); 
            if (getter != null) 
             setter.invoke(target, getter.invoke(target)); 
           } 
          } 
          catch (Exception ex) { 
           s_logger.error("can't invoke setter", ex); 
          } 
         } 
       } 
      } 
     } 
    } 
1

Sie können nur ein Array statt Liste verwenden)

5

Hinweis: Ich bin der EclipseLink JAXB (MOXy) Blei und ein Mitglied der Expertengruppe JAXB 2 (JSR-222).

Das Verhalten, das Sie sehen, wird unter JAXB-Implementierungen variieren. Wenn Sie einen Wert für die Eigenschaft List nicht initialisieren, wird EclipseLink JAXB (MOXy) die set-Methode wie erwartet aufrufen.

Weitere Informationen


Beispiel

A

package forum1032152; 

import java.util.ArrayList; 

import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
public class A { 

    private ArrayList<B> b; 

    @XmlElement 
    public void setB(ArrayList<B> col) { 
     System.out.println("Called setB"); 
     for(B b : col) { 
      System.out.println(b); 
     } 
     this.b = col; 
    } 

    public ArrayList<B> getB() { 
     return b; 
    } 

} 

B

package forum1032152; 

public class B { 

} 

Demo

package forum1032152; 

import java.io.File; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 

public class Demo { 

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

     File xml = new File("src/forum1032152/input.xml"); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     unmarshaller.unmarshal(xml); 
    } 

} 

input.xml

<?xml version="1.0" encoding="UTF-8"?> 
<a> 
    <b></b> 
    <b></b> 
</a> 

Ausgabe

Called setB 
[email protected] 
[email protected] 
0
The reason I want the setter to be called is that the list is actually 
just a temporary storage from which I want to build the map field, 
so I thought to do it in the setter. 

JAXB direkt Karten verarbeiten kann, daher könnte dies den Anruf zu setb() ein strittiger Punkt. Wenn das eine akzeptable Lösung für Sie ist, finden Sie unter example ich auf meinem Blog, um einen Adapter für Karten in JAXB zu erstellen.

Verwandte Themen