Jaki jest sens klas ObjectFactory JAXB 2?

98

Jestem nowy w używaniu JAXB i użyłem xjc JAXB 2.1.3 do wygenerowania zestawu klas z mojego schematu XML. Oprócz wygenerowania klasy dla każdego elementu w moim schemacie, utworzono klasę ObjectFactory.

Wydaje się, że nic nie powstrzymuje mnie przed bezpośrednim tworzeniem instancji elementów, np

MyElement element = new MyElement();

podczas gdy samouczki wydają się preferować

MyElement element = new ObjectFactory().createMyElement();

Jeśli zajrzę do ObjectFactory.java, widzę:

public MyElement createMyElement() {
    return new MyElement();
}

więc o co chodzi? Dlaczego miałbym w ogóle zawracać sobie głowę utrzymywaniem klasy ObjectFactory? Zakładam, że zostanie on również nadpisany, gdybym miał ponownie skompilować ze zmienionego schematu.

Andrew Coleson
źródło
Nie jestem pewien, czy to zamierzony projekt, ale znalazłem ObjectFactory jako idealną klasę do użycia do tworzenia JAXBContext. Musisz tam wyliczyć kilka klas, a JAXB podąży za ich metodami itp., Więc są one czymś w rodzaju korzeni. A ObjectFactory ma odwołania do wszystkich elementów, więc wystarczy użyć ObjectFactory.class do stworzenia JAXBContext ze wszystkimi odpowiednimi klasami.
vbezhenar

Odpowiedzi:

68

Kompatybilność wsteczna nie jest jedynym powodem. :-P

W przypadku bardziej skomplikowanych schematów, takich jak te, które mają skomplikowane ograniczenia dotyczące wartości, jakie może przyjąć zawartość elementu, czasami trzeba utworzyć rzeczywiste JAXBElementobiekty. Zwykle nie są one łatwe do wykonania ręcznie, więc create*metody wykonują ciężką pracę za Ciebie. Przykład (ze schematu XHTML 1.1):

@XmlElementDecl(namespace = "http://www.w3.org/1999/xhtml", name = "style", scope = XhtmlHeadType.class)
public JAXBElement<XhtmlStyleType> createXhtmlHeadTypeStyle(XhtmlStyleType value) {
    return new JAXBElement<XhtmlStyleType>(_XhtmlHeadTypeStyle_QNAME, XhtmlStyleType.class, XhtmlHeadType.class, value);
}

Oto jak <style>umieścić tag w <head>tagu:

ObjectFactory factory = new ObjectFactory();
XhtmlHtmlType html = factory.createXhtmlHtmlType();
XhtmlHeadType head = factory.createXhtmlHeadType();
html.setHead(head);
XhtmlStyleType style = factory.createXhtmlStyleType();
head.getContent().add(factory.createXhtmlHeadTypeStyle(style));

Pierwsze trzy zastosowania ObjectFactorymożna by uznać za zbędne (choć przydatne dla spójności), ale czwarte z nich sprawia, że ​​JAXB jest dużo, dużo łatwiejszy w użyciu. Wyobrazić sobie konieczność new JAXBElementręcznego wypisywania za każdym razem

Chris Jester-Young
źródło
Czy możesz podać przykład / odniesienie do tego, co (lub jak skomplikowany) musi być element schematu, aby create * () wykonało coś pożytecznego? Mam problem ze znalezieniem części schematu, do której odwołujesz się w przykładzie JAXB. Jeśli mój schemat stanie się później bardziej skomplikowany, z pewnością byłoby miło,
gdyby twórca
Jeśli pobierzesz paczki z XHTML 1.1 i XHTML Modularization 1.1, w środku znajdziesz katalogi o nazwie "SCHEMA". Umieść wszystkie pliki .xsd w tych samych katalogach. Niektóre pliki .xsd będą również importowane w3.org/2001/xml.xsd ; będziesz chciał odpowiednio dostosować lokalizacje, jeśli nie chcesz, aby plik był pobierany za każdym razem, gdy uruchamiasz xjc. [cd]
Chris Jester-Young
[cd] Konkretna część .xsd, która określa zawartość <head>, w tym przypadku znajduje się w xhtml11-model-1.xsd w grupie xhtml.head.content.
Chris Jester-Young
2
W każdym razie nikt nie celuje w twoją głowę, mówiąc, że musisz użyć ObjectFactory (chociaż uważam to za przydatne), ale kiedy natkniesz się na przypadek, w którym jest naprawdę przydatny, będziesz o tym wiedział. :-)
Chris Jester-Young
Dzięki! Myślę, że mój schemat nie jest wystarczająco skomplikowany, ale będę o tym pamiętać na przyszłość. :) Wiedziałem, że czegoś mi brakuje.
Andrew Coleson
39

Jak zauważył @Chris, czasami JAXB nie może współpracować z POJO, ponieważ schemat nie może być dokładnie odwzorowany na Javę. W takich przypadkach JAXBElementobiekty opakowujące są niezbędne, aby zapewnić dodatkowe informacje o typie.

Są dwa konkretne przykłady, z którymi się spotkałem, gdzie jest to powszechne.

  • Jeśli chcesz zorganizować obiekt klasy, która nie ma @XmlRootElementadnotacji. Domyślnie XJC generuje tylko @XmlRootElementdla niektórych elementów, a nie dla innych. Dokładna logika tego jest nieco skomplikowana, ale możesz zmusić XJC do wygenerowania większej liczby @XmlRootElementklas przy użyciu „trybu prostego wiązania”

  • Kiedy Twój schemat używa grup podstawników. Jest to dość zaawansowane użycie schematu, ale XJC tłumaczy grupy podstawień na Javę, intensywnie wykorzystując JAXBElementopakowania.

Tak więc w modelu obiektowym wygenerowanym przez XJC, który intensywnie wykorzystuje JAXBElement(z jakiegokolwiek powodu), potrzebujesz sposobu konstruowania tych JAXBElementinstancji. Wygenerowany ObjectFactoryjest zdecydowanie najłatwiejszym sposobem na zrobienie tego. Możesz je zbudować samodzielnie, ale jest to niezgrabne i podatne na błędy.

skaffman
źródło
Dzięki za dodatkowe przykłady!
Andrew Coleson
2
Wow, to zwycięska odpowiedź. +1
Chris Jester-Young
Lubię używać annox do generowania XmlRootElement jako 95% czasu, jeśli mam element, który odnosi się do complexType, chcę XmlRootElement (cóż, bardziej jak 100%, ponieważ nie trafiłem w przypadek użycia, w którym nie chcę tego jeszcze)
Dean Hiller
9

Wydaje mi się, że kompatybilność wsteczna ...

http://weblogs.java.net/blog/kohsuke/archive/2005/08/a_story_of_migr.html :

... Nigdy więcej ObjectFactory.createXYZ. Problem z tymi metodami fabrycznymi polegał na tym, że rzucają sprawdzony wyjątek JAXBException. Teraz możesz po prostu wykonać nowe XYZ (), bez bloków try / catch. (Wiem, wiem, ... to jedna z tych rzeczy „o czym myśleliśmy !?”) ...

Bert F.
źródło