Apa gunanya kelas ObjectFactory JAXB 2?

98

Saya baru menggunakan JAXB, dan saya menggunakan xjc JAXB 2.1.3 untuk menghasilkan satu set kelas dari Skema XML saya. Selain menghasilkan kelas untuk setiap elemen dalam skema saya, itu membuat kelas ObjectFactory.

Sepertinya tidak ada yang menghentikan saya untuk membuat instance elemen secara langsung, mis

MyElement element = new MyElement();

sedangkan tutorial tampaknya lebih memilih

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

Jika saya melihat ke ObjectFactory.java, saya melihat:

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

jadi apa masalahnya? Mengapa saya harus repot-repot menjaga kelas ObjectFactory? Saya berasumsi ini juga akan ditimpa jika saya melakukan kompilasi ulang dari skema yang diubah.

Andrew Coleson
sumber
Saya tidak yakin apakah itu desain yang dimaksudkan, tetapi saya telah menemukan ObjectFactory sebagai kelas yang ideal untuk digunakan untuk pembuatan JAXBContext. Anda perlu menghitung beberapa kelas di sana dan JAXB akan mengikuti metode mereka, dll, jadi mereka seperti root. Dan ObjectFactory memiliki referensi ke semua elemen, jadi cukup menggunakan ObjectFactory.class untuk membuat JAXBContext dengan semua kelas yang relevan.
vbezhenar

Jawaban:

68

Kompatibilitas ke belakang bukan satu-satunya alasan. :-P

Dengan skema yang lebih rumit, seperti skema yang memiliki batasan rumit pada nilai yang dapat diambil oleh konten elemen, terkadang Anda perlu membuat JAXBElementobjek sebenarnya . Mereka biasanya tidak mudah dibuat dengan tangan, jadi create*metode ini bekerja keras untuk Anda. Contoh (dari skema 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);
}

Beginilah cara Anda memasukkan <style>tag ke dalam <head>tag:

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));

Tiga penggunaan pertama dari ObjectFactorydapat dianggap berlebihan (meskipun berguna untuk konsistensi), tetapi yang keempat membuat JAXB jauh lebih mudah digunakan. Pencitraan harus menulis new JAXBElementdengan tangan setiap kali!

Chris Jester-Young
sumber
Dapatkah Anda memberikan contoh / referensi tentang apa (atau seberapa rumit) elemen Skema yang dibutuhkan agar create * () dapat melakukan sesuatu yang berguna? Saya kesulitan menemukan bagian dari Skema yang Anda referensikan dengan contoh JAXB Anda. Jika Skema saya menjadi lebih rumit di kemudian hari, tentu akan menyenangkan untuk membuat * menangani sebagian darinya untuk saya, tetapi karena itu dibuat * bahkan tidak repot-repot membuat sub-elemen sendiri ..
Andrew Coleson
Jika Anda mendownload tarball XHTML 1.1 dan XHTML Modularization 1.1, Anda akan menemukan direktori di dalamnya yang disebut "SCHEMA". Letakkan semua file .xsd di direktori yang sama. Beberapa file .xsd juga akan mengimpor w3.org/2001/xml.xsd ; Anda akan ingin menyesuaikan lokasi dengan tepat jika Anda tidak ingin file diunduh setiap kali Anda menjalankan xjc. [lanjutan]
Chris Jester-Young
[lanjutan] Bagian spesifik dari .xsd yang menentukan konten <head>, dalam hal ini, dalam xhtml11-model-1.xsd, di bawah grup xhtml.head.content.
Chris Jester-Young
2
Bagaimanapun, tidak ada yang mengarahkan pistol ke kepala Anda mengatakan Anda harus menggunakan ObjectFactory (meskipun saya merasa berguna untuk digunakan), tetapi ketika Anda menemukan kasus di mana itu benar-benar berguna, Anda akan mengetahuinya. :-)
Chris Jester-Young
Terima kasih! Saya kira skema saya tidak cukup rumit, tapi saya akan mengingatnya untuk masa depan. :) Saya tahu saya harus melewatkan sesuatu.
Andrew Coleson
39

Seperti yang ditunjukkan @Chris, terkadang JAXB tidak dapat bekerja dengan POJO, karena skema tidak dapat dipetakan secara tepat ke Java. Dalam kasus ini, JAXBElementobjek pembungkus diperlukan untuk memberikan informasi tipe tambahan.

Ada dua contoh konkret yang saya temukan di mana ini umum.

  • Jika Anda ingin mengatur objek dari kelas yang tidak memiliki @XmlRootElementanotasi. Secara default XJC hanya menghasilkan @XmlRootElementuntuk beberapa elemen, dan tidak untuk yang lain. Logika yang tepat untuk ini agak rumit, tetapi Anda dapat memaksa XJC untuk menghasilkan lebih banyak @XmlRootElementkelas menggunakan "mode pengikatan sederhana"

  • Saat skema Anda menggunakan grup substituion. Ini adalah penggunaan skema yang cukup canggih, tetapi XJC menerjemahkan grup substitusi ke dalam Java dengan menggunakan banyak JAXBElementpembungkus.

Jadi dalam model objek yang dihasilkan XJC yang banyak menggunakan JAXBElement(untuk alasan apa pun), Anda memerlukan cara untuk membuat JAXBElementinstance tersebut. Yang dihasilkan ObjectFactorysejauh ini adalah cara termudah untuk melakukannya. Anda dapat membuatnya sendiri, tetapi itu kikuk dan rawan kesalahan untuk melakukannya.

skaffman
sumber
Terima kasih atas contoh tambahannya!
Andrew Coleson
2
Wow, itu jawaban yang bagus. +1
Chris Jester-Young
Saya suka menggunakan annox untuk menghasilkan XmlRootElement sebanyak 95% dari waktu jika saya memiliki elememtn yang mengacu pada complexType, saya ingin XmlRootElement (yah, lebih seperti 100% karena saya belum mencapai kasus penggunaan di mana saya tidak menginginkannya belum)
Dean Hiller
9

Kompatibilitas ke belakang, saya kira ...

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

... Tidak ada lagi ObjectFactory.createXYZ. Masalah dengan metode pabrik tersebut adalah bahwa mereka melempar JAXBException yang dicentang. Sekarang Anda cukup melakukan XYZ baru (), tidak ada lagi blok coba / tangkap. (Aku tahu, aku tahu, ... ini adalah salah satu hal "apa yang kita pikirkan !?") ...

Bert F
sumber