2012-06-18 13 views
7

Znalazłem kilka przykładów JAXB2 @XmlRegistry przez Internet, ale brak dobrych dogłębnych samouczków, które mówią o koncepcji używania @XmlRegistry z @XmlElementDecl, zastanawiam się, czy jest to pojęcie mało zbadane w ogóle.@XmlRegistry - jak to działa?

Anyways tutaj jest moje pytanie, pierwsze kilka klas przykładowe że używam aby wycofać xml przy użyciu JAXB:

Główna klasa Próbuję unmarshal użyciu JAXB - Employee.java

package com.test.jaxb; 

import java.util.List; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.XmlElementDecl; 
import javax.xml.bind.annotation.XmlRegistry; 
import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.namespace.QName; 

import com.test.jaxb.dto.Address; 

@XmlRootElement 
public class Employee { 
    private int id; 
    private String name; 
    private String email; 

    private List<Address> addresses; 

    public int getId() { 
     return id; 
    } 
    public void setId(int id) { 
     this.id = id; 
    } 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
    public String getEmail() { 
     return email; 
    } 
    public void setEmail(String email) { 
     this.email = email; 
    } 

    public List<Address> getAddresses() { 
     return addresses; 
    } 
    public void setAddresses(List<Address> addresses) { 
     this.addresses = addresses; 
    } 

    @SuppressWarnings("unused") 
    @XmlRegistry 
    public static class XMLObjectFactory { 
     @XmlElementDecl(scope = Employee.class, name= "id") 
     JAXBElement<String> createEmployeeId(String value) { 
      return new JAXBElement<String>(new QName("id"), String.class, "100"); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "name") 
     JAXBElement<String> createName(String value) { 
      return new JAXBElement<String>(new QName("name"), String.class, "Fake Name"); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "email") 
     JAXBElement<String> createEmail(String value) { 
      return new JAXBElement<String>(new QName("email"), String.class, value); 
     } 

     @XmlElementDecl(scope = Employee.class, name= "addresses") 
     JAXBElement<List> createAddresses(List value) { 
      return new JAXBElement<List>(new QName("addresses"), List.class, value); 
     } 
    } 
} 

Klasa dziecko - Address.java

package com.test.jaxb.dto; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.XmlElementDecl; 
import javax.xml.bind.annotation.XmlRegistry; 
import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.namespace.QName; 

import com.test.jaxb.Employee; 

@XmlRootElement 
public class Address { 
    private String addressLine1; 
    private String addressLine2; 
    private String addressLine3; 
    public String getAddressLine1() { 
     return addressLine1; 
    } 
    public void setAddressLine1(String addressLine1) { 
     this.addressLine1 = addressLine1; 
    } 
    public String getAddressLine2() { 
     return addressLine2; 
    } 
    public void setAddressLine2(String addressLine2) { 
     this.addressLine2 = addressLine2; 
    } 
    public String getAddressLine3() { 
     return addressLine3; 
    } 
    public void setAddressLine3(String addressLine3) { 
     this.addressLine3 = addressLine3; 
    } 

    @SuppressWarnings("unused") 
    @XmlRegistry 
    private static class XMLObjectFactory { 
     @XmlElementDecl(scope = Employee.class, name= "addressLine1") 
     JAXBElement<String> createAddressLine1(String value) { 
      return new JAXBElement<String>(new QName("addressLine1"), String.class, value); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "addressLine2") 
     JAXBElement<String> createAddressLine2(String value) { 
      return new JAXBElement<String>(new QName("addressLine2"), String.class, value); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "addressLine3") 
     JAXBElement<String> createAddressLine3(String value) { 
      return new JAXBElement<String>(new QName("addressLine3"), String.class, value); 
     } 
    } 
} 

xml być unmarshalled - employee.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<employee> 
    <id>1</id> 
    <name>Vaishali</name> 
    <email>[email protected]</email> 
    <addresses> 
     <address> 
      <addressLine1>300</addressLine1> 
      <addressLine2>Mumbai</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
     <address> 
      <addressLine1>301</addressLine1> 
      <addressLine2>Pune</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
    </addresses> 
</employee> 

Kod Unmarshalling:

package com.test.jaxb; 

import java.io.FileReader; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 


public class ObjectFactoryTest { 
    public static void main(String[] args) throws Exception { 
     FileReader reader = new FileReader("resources/employee.xml"); 
     JAXBContext context = JAXBContext.newInstance(Employee.class); 
     Unmarshaller unmarshaller = context.createUnmarshaller(); 
     Object obj = unmarshaller.unmarshal(reader); 
     System.out.println(obj); 
    } 
} 

Kiedy unmarshal xml pracowników przy użyciu powyżej kodu, lista adresów nie uzyskać zaludnionych. Wynikowy obiekt pracownika ma tylko pustą listę adresów. Czy coś jest nie tak z moimi mapowaniami?

Aby dowiedzieć się, co się dzieje i zobaczyć, czy obiekty pracowników są rzeczywiście tworzone przy użyciu Fabryki obiektów (z adnotacją @XMLRegistry), zmieniłem wartość identyfikatora i nazwy w metodach fabrycznych, jednak nie miało to żadnego skutku na wyjściu, które mówi mi, że JAXB nie używa w rzeczywistości ObjectFactory, dlaczego?

Czy wszystko się zgadza? Każda pomoc będzie doceniona.

Odpowiedz

15

@XmlRegistry - jak to działa?

@XmlRegistry służy do oznaczania klasy z adnotacjami @XmlElementDecl. Aby mieć proces implementacji JAXB tej klasy, musisz upewnić się, że jest ona zawarta na liście klas używanych do ładowania systemu JAXBContext. Nie wystarczy na to, aby być statyczna wewnętrzna klasa jednej z klas modelu domeny:

JAXBContext context = JAXBContext.newInstance(Employee.class, Employee.XMLObjectFactory.class); 

@XmlElementDecl - jak to działa?

Jeśli wartość pola/nieruchomość ma być JAXBElement to trzeba wykorzystać @XmlElementDecl. JAXBElement przechwytujące informacje mogą być przydatne:

  • nazwa elementu, to jest konieczne, jeśli mapowanie do struktury wyboru, gdzie wiele elementów są tego samego typu. Jeśli nazwa elementu nie odpowiada typowi unikatowemu, wówczas nie będzie można w obie strony wyzwolić dokumentu.
  • JAXBElement może być używany do reprezentowania elementu z xsi:nil="true".

XmlObjectFactory

@XmlElementDecl pozwala również określić zakres. Zmodyfikowałem model z twojego posta. Wprowadziłem klasę XmlObjectFactory, która ma dwie @XmlElementDecl. Oba określają nazwę address. Zastosowałem właściwość scope, aby dla właściwości w klasie Employee ustawić @XmlElementDecl odpowiadającą klasie Address z użyciem.

package forum11078850; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.XmlElementDecl; 
import javax.xml.bind.annotation.XmlRegistry; 
import javax.xml.namespace.QName; 

@XmlRegistry 
public class XmlObjectFactory { 

    @XmlElementDecl(scope = Employee.class, name = "address") 
    JAXBElement<Address> createAddress(Address value) { 
     return new JAXBElement<Address>(new QName("address"), Address.class, value); 
    } 

    @XmlElementDecl(name = "address") 
    JAXBElement<String> createStringAddress(String value) { 
     return new JAXBElement<String>(new QName("address"), String.class, value); 
    } 

} 

Pracownik

@XmlElementRef adnotacji spowoduje wartość nieruchomości powinna być dopasowana na jego nazwę elementu głównego. Możliwe dopasowania będą zawierać klasy zmapowane z @XmlRootElement lub @XmlElementDecl.

package forum11078850; 

import java.util.List; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.*; 

@XmlRootElement 
@XmlType(propOrder = { "id", "name", "email", "addresses" }) 
public class Employee { 
    private int id; 
    private String name; 
    private String email; 

    private List<JAXBElement<Address>> addresses; 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getEmail() { 
     return email; 
    } 

    public void setEmail(String email) { 
     this.email = email; 
    } 

    @XmlElementWrapper 
    @XmlElementRef(name="address") 
    public List<JAXBElement<Address>> getAddresses() { 
     return addresses; 
    } 

    public void setAddresses(List<JAXBElement<Address>> addresses) { 
     this.addresses = addresses; 
    } 

} 

ObjectFactoryTest

package forum11078850; 

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

public class ObjectFactoryTest { 
    public static void main(String[] args) throws Exception { 
     FileReader reader = new FileReader("src/forum11078850/input.xml"); 
     JAXBContext context = JAXBContext.newInstance(Employee.class, XmlObjectFactory.class); 
     Unmarshaller unmarshaller = context.createUnmarshaller(); 
     Object obj = unmarshaller.unmarshal(reader); 

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

Klasa Address i input.xml z mojego pierwotnego odpowiedź może być użyty do uruchomienia tego przykładu.


ORIGINAL ODPOWIEDŹ

Nie jestem pewien, jak próbujesz użyć @XmlRegistry, więc skupię się na dalszej części posta:

Kiedy unmarshal xml pracownika przy użyciu powyższego kodu, lista adresów nie zostanie wypełniona. Wynikowy obiekt pracownika ma tylko pustą listę adresów. Czy coś jest nie tak z moimi mapowaniami?

Twoja lista Address obiektów jest owinięty w elemencie grupie (addresses), więc trzeba użyć @XmlElementWrapper adnotacji mapowania tego przypadku użycia. Poniżej znajduje się pełna przykład:

Pracownik

package forum11078850; 

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

@XmlRootElement 
@XmlType(propOrder = { "id", "name", "email", "addresses" }) 
public class Employee { 
    private int id; 
    private String name; 
    private String email; 

    private List<Address> addresses; 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getEmail() { 
     return email; 
    } 

    public void setEmail(String email) { 
     this.email = email; 
    } 

    @XmlElementWrapper 
    @XmlElement(name = "address") 
    public List<Address> getAddresses() { 
     return addresses; 
    } 

    public void setAddresses(List<Address> addresses) { 
     this.addresses = addresses; 
    } 

} 

Adres

package forum11078850; 

public class Address { 
    private String addressLine1; 
    private String addressLine2; 
    private String addressLine3; 

    public String getAddressLine1() { 
     return addressLine1; 
    } 

    public void setAddressLine1(String addressLine1) { 
     this.addressLine1 = addressLine1; 
    } 

    public String getAddressLine2() { 
     return addressLine2; 
    } 

    public void setAddressLine2(String addressLine2) { 
     this.addressLine2 = addressLine2; 
    } 

    public String getAddressLine3() { 
     return addressLine3; 
    } 

    public void setAddressLine3(String addressLine3) { 
     this.addressLine3 = addressLine3; 
    } 

} 

ObjectFactoryTest

package forum11078850; 

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

public class ObjectFactoryTest { 
    public static void main(String[] args) throws Exception { 
     FileReader reader = new FileReader("src/forum11078850/input.xml"); 
     JAXBContext context = JAXBContext.newInstance(Employee.class); 
     Unmarshaller unmarshaller = context.createUnmarshaller(); 
     Object obj = unmarshaller.unmarshal(reader); 

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

input.xml/Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<employee> 
    <id>1</id> 
    <name>Vaishali</name> 
    <email>[email protected]</email> 
    <addresses> 
     <address> 
      <addressLine1>300</addressLine1> 
      <addressLine2>Mumbai</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
     <address> 
      <addressLine1>301</addressLine1> 
      <addressLine2>Pune</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
    </addresses> 
</employee> 
+0

Znalazłem kilka przykładów "@XmlRegistry" użytych w taki sam sposób, w jaki ja go używam, patrz [tutaj] (http://www.codezealot.org/archives/5). Rozumiem, że mogę użyć "@XmlElementWrapper", aby to zrobić. Moje pytanie dotyczyło raczej zrozumienia, czym jest "@XmlRegistry" i co mogę z tym zrobić. AFAIK Powinienem móc użyć obiektu objectfactory (opatrzonego adnotacją "@XmlRegistry") do niepoprawnego zapisu xml i przekształcenia go w obiekt. Jeśli nie, to do czego służy? Czy muszę go gdzieś zarejestrować, aby został użyty (jak pokazałem na moim przykładzie, moja fabryka obiektów nigdy nie jest tak naprawdę używana przez JAXB)? – gresdiplitude

+0

Moje rozumienie może być tutaj całkowicie błędne, ale pomyślałem, że musi istnieć sposób zastosowania czegoś podobnego do "@XmlElementWrapper" do "@XmlRegistry", ponieważ jest to alternatywny sposób eliminowania XML. Brak punktów za zgadywanie Jestem całkowicie zdezorientowany, jak działa @XmlRegistry !!! – gresdiplitude

+0

@Vaishali - zaktualizowałem swoją odpowiedź, dodając przykład wykorzystujący "@ XmlRegistry' i' @ XmlElementDecl' –

-1

Musisz wziąć obiekt listy adresu. W tym obiekcie będziesz musiał dodać obiekt, który zawiera dane takie jak addressline1. addressline2 i tak dalej.

i.e. 
List addrObjList = new List(); 
addrObjList.add(object); // Bind an object containing data and add one by one 
+0

Dwie rzeczy, jedno - że nie powinno być wymagane jako adres klasy sama jest opatrzone @XmlRootElement i posiada fabryki obiektów własnych, JAXB powinny być w stanie to zrobić automatycznie ...i dwa - jak wspomniałem w moim pytaniu, JAXB nie wydaje się w ogóle używać Fabryki Obiektu, więc wszelkie zmiany wprowadzone w metodach fabrycznych nie mają żadnego skutku. – gresdiplitude