2012-01-20 12 views
5

पर आधारित विरासत और विशेषता नाम oveloading मुझे MOXy के JAXB कार्यान्वयन और बाहरी मेटाडेटा बाइंडिंग फ़ाइल का उपयोग करके विरासत और बहुरूपता शामिल एक मार्शलिंग/अनमर्शलिंग समस्या का सामना करना पड़ रहा है।eclipselink/moxy:

मेरे पास XML फ़ाइलों या मॉडल कक्षाओं पर कोई नियंत्रण नहीं है।

मॉडल के अंदर कई कक्षाएं हैं जो अन्य डीटीओ कक्षाओं का उत्तराधिकारी हैं। । यहाँ पर्यावरण मैं में काम कर रहा हूँ का एक उदाहरण है यह उदाहरण केवल कुछ वाक्य रचना उद्देश्य के लिए यहाँ है, असली पर्यावरण नेस्टेड विरासत शामिल है, संग्रह आदि:

यहाँ वर्ग कि विरासत में मिला हो जाएगा

class A { 

     private String name; 

     public String getName(){ 
       return name; 
     } 

     public void setName(String value){ 
       name = value; 
     } 

    } 

यहाँ विरासत में मिला वर्ग से एक है

class B extends A { 

     private String attrFromB; 

     public String getAttrFromB(){ 
       return attrFromB; 
     } 

     public void setAttrFromB(String value){ 
       attrFromB = value; 
     } 
    } 

और एक और

class C extends A { 

     private String attrFromC; 

     public String getAttrFromC(){ 
       return attrFromC; 
     } 

     public void setAttrFromC(String value){ 
       attrFromC= value; 
     } 
    } 

यहाँ एक कंटेनर वर्ग

class MyContainerClass{ 

     private A myObject; 

     public A getMyObject(){ 
      return myObject; 
     } 

     public void setMyObject(A value){ 
      myObject = value; 
     } 
    } 

यहाँ है एक्सएमएल कि यह MyContainer के मामले में प्रस्तुत करना चाहिए युक्त बी

<MyContainer> 
     <MyObject nameB="foo" attrFromB="bar" /> 
    </MyContainer> 

युक्त

<MyContainer> 
     <MyObject nameA="foo" /> 
    </MyContainer> 

MyContainer और MyContainer सी युक्त है

<MyContainer> 
     <MyObject nameC="foo" attrFromC="bar" /> 
    </MyContainer> 

तो आप पहले से ही क्षितिज में समस्याओं को देख सकते हैं ...

यहाँ मैपिंग फ़ाइल है कि मैं लिखना होता है:

<?xml version="1.0"?> 
    <xml-bindings 
     xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" 
     package-name="com.test.example" 
     version="2.1"> 

     <java-type name="A" xml-accessor-type="NONE"> 
      <xml-root-element name="MyObject" /> 
      <java-attributes> 
       <xml-element java-attribute="name" xml-path="@nameA" /> 
      </java-attributes> 
     </java-type> 

     <java-type name="B" xml-accessor-type="NONE"> 
      <xml-root-element name="MyObject" /> 
      <xml-see-also> 
       com.test.example.A 
      </xml.see.also> 
      <java-attributes> 
       <xml-element java-attribute="name" xml-path="@nameB" /> 
       <xml-element java-attribute="attrFromB" xml-path="@attrFromB" /> 
      </java-attributes> 
     </java-type> 

     <java-type name="C" xml-accessor-type="NONE"> 
      <xml-root-element name="MyObject" /> 
      <xml-see-also> 
       com.test.example.A 
      </xml.see.also> 
      <java-attributes> 
       <xml-element java-attribute="name" xml-path="@nameC" /> 
       <xml-element java-attribute="attrFromC" xml-path="@attrFromC" /> 
      </java-attributes> 
     </java-type> 

     <java-type name="MyContainer" xml-accessor-type="NONE"> 
      <xml-root-element name="MyContainer" /> 
      <java-attributes> 
       <xml-element java-attribute="myObject" type="com.test.example.A" xml-path="MyObject" /> 
      </java-attributes> 
     </java-type> 

    </xml-bindings> 

पहली समस्या है कि अगर मैं जैसे वर्गों के लिए बाध्य

[Exception [EclipseLink-44] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException 
    Exception Description: Missing class indicator field from database row [UnmarshalRecord()]. 

1 प्रश्न: कि, मैं निम्न अपवाद प्राप्त मैं समझता हूँ कि यह सामान्य है , जैक्सब को MyContaioner.myObject विशेषता के प्रकार को निर्धारित करने के लिए कुछ तरीके की आवश्यकता है। समस्या यह है कि मेरे पास आने वाली एक्सएमएल फाइलों तक कोई पहुंच नहीं है, इसलिए मैं xsi नहीं जोड़ सकता: फ़ील्ड टाइप करें। क्या इसमें एक विशिष्ट विशेषता की उपस्थिति के आधार पर कक्षा निर्धारित करने का कोई तरीका है? इसके मूल्य के बावजूद। स्रोत एक्सएमएल एक @attrFromC विशेषता शामिल है, तो मैं जानता हूँ कि ऑब्जेक्ट प्रकार सी की होनी चाहिए यह attrFromB हैं, तो वह बी


है दूसरी समस्या यह है कि "नाम" विशेषता के अंदर मौजूद नहीं है बी और सी, तो जाक्सब एम को अनदेखा करता है।

--Ignoring attribute [name] on class [com.test.example.B] as no Property was generated for it. 
    --Ignoring attribute [name] on class [com.test.example.C] as no Property was generated for it. 

2 प्रश्न: अन्य समस्या यह है कि मैं जानता हूँ कि अगर JAXB एक्सएमएल अधिभावी में सक्षम विशेषता नाम है जैसे कि यह एक्सएमएल फ़ाइल के अंदर की उम्मीद है है न है (@nameA, @nameB और nameC सभी ए की चर्चा करते हुए नाम), क्या ऐसा करने का कोई तरीका है?

आपके समय के लिए अग्रिम धन्यवाद।

उत्तर

4

नीचे आपके प्रश्नों के उत्तर हैं। JAXB किसी तरह MyContaioner.myObject विशेषता के प्रकार का निर्धारण करने की जरूरत है मैं समझता हूँ कि यह सामान्य है,: इस सवाल का जवाब 2 पर सवाल उठाने, यह भी 1.


1 प्रश्न प्रश्न का उत्तर है। समस्या यह है कि मेरे पास आने वाली XML फ़ाइलों तक कोई पहुंच नहीं है, इसलिए मैं xsi नहीं जोड़ सकता: फ़ील्ड टाइप करें। क्या पर आधारित एक विशिष्ट विशेषता की उपस्थिति के आधार पर कक्षा निर्धारित करने का कोई तरीका है? इसके मूल्य के बावजूद। स्रोत एक्सएमएल एक @attrFromC विशेषता शामिल है, तो मैं जानता हूँ कि वस्तु प्रकार सी की होनी चाहिए यदि यह attrFromB सम्मिलित हैं, तो बी

है आप उपयोग के इस मामले EclipseLink JAXB (MOXy) में ClassExtractor विस्तार का लाभ उठाने कर सकते हैं:

MyClassExtractor

एक ClassExtractor कुछ कोड है कि आप मदद करने के लिए MOXY जो निर्धारित वर्ग यह instanitate चाहिए लागू कर सकते हैं। आपको Record पारित किया गया है और आप XPath द्वारा वर्तमान तत्व पर विशेषताओं की उपस्थिति के लिए पूछ सकते हैं कि यह निर्धारित करने के लिए कि कौन सी कक्षा को तत्काल किया जाना चाहिए।

package com.test.example; 

import org.eclipse.persistence.descriptors.ClassExtractor; 
import org.eclipse.persistence.sessions.*; 

public class MyClassExtractor extends ClassExtractor{ 

    @Override 
    public Class<?> extractClassFromRow(Record record, Session session) { 
     if(null != record.get("@attrFromB")) { 
      return B.class; 
     } else if(null != record.get("@attrFromC")) { 
      return C.class; 
     } else { 
      return A.class; 
     } 
    } 

} 

मेटाडाटा (oxm.xml)

आप ClassExtractor कॉन्फ़िगर @XmlClassExtractor एनोटेशन का उपयोग कर सकते हैं। आप बाहरी मेटाडेटा फ़ाइल के माध्यम से भी ऐसा कर सकते हैं।

<?xml version="1.0"?> 
<xml-bindings 
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" 
    package-name="com.test.example" 
    version="2.3"> 
    <java-types> 
     <java-type name="A" xml-accessor-type="NONE"> 
      <xml-class-extractor class="com.test.example.MyClassExtractor"/> 
      <xml-root-element name="MyObject" /> 
      <java-attributes> 
       <xml-attribute java-attribute="name" name="nameA" /> 
      </java-attributes> 
     </java-type> 
     <java-type name="B" xml-accessor-type="NONE"> 
      <xml-root-element name="MyObject" /> 
      <java-attributes> 
       <xml-attribute java-attribute="name" name="nameB" /> 
       <xml-attribute java-attribute="attrFromB"/> 
      </java-attributes> 
     </java-type> 
     <java-type name="C" xml-accessor-type="NONE"> 
      <xml-root-element name="MyObject" /> 
      <java-attributes> 
       <xml-attribute java-attribute="name" name="nameC" /> 
       <xml-attribute java-attribute="attrFromC"/> 
      </java-attributes> 
     </java-type> 
     <java-type name="MyContainerClass" xml-accessor-type="NONE"> 
      <xml-root-element name="MyContainer" /> 
      <java-attributes> 
       <xml-element java-attribute="myObject" name="MyObject" /> 
      </java-attributes> 
     </java-type> 
    </java-types> 
</xml-bindings> 

डेमो

निम्नलिखित डेमो कोड आपके प्रश्न से XML दस्तावेज़ों में से प्रत्येक unmarshals, और प्रकार myObject द्वारा आयोजित किया जा रहा आउटपुट: मैं अपने प्रश्न में शामिल इस शामिल करने के लिए एक ढाल लिया है संपत्ति:

package com.test.example; 

import java.io.StringReader; 
import java.util.*; 
import javax.xml.bind.*; 
import org.eclipse.persistence.jaxb.JAXBContextFactory; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     Map<String, Object> properties = new HashMap<String, Object>(); 
     properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "com/test/example/oxm.xml"); 
     JAXBContext jc = JAXBContext.newInstance(new Class[] {MyContainerClass.class}, properties); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 

     StringReader aXml = new StringReader("<MyContainer><MyObject nameA='foo'/></MyContainer>"); 
     MyContainerClass myContainerA = (MyContainerClass) unmarshaller.unmarshal(aXml); 
     System.out.println(myContainerA.getMyObject().getClass()); 

     StringReader bXml = new StringReader("<MyContainer><MyObject nameB='foo' attrFromB='bar'/></MyContainer>"); 
     MyContainerClass myContainerB = (MyContainerClass) unmarshaller.unmarshal(bXml); 
     System.out.println(myContainerB.getMyObject().getClass()); 

     StringReader cXml = new StringReader("<MyContainer><MyObject nameC='foo' attrFromC='bar'/></MyContainer>"); 
     MyContainerClass myContainerC = (MyContainerClass) unmarshaller.unmarshal(cXml); 
     System.out.println(myContainerC.getMyObject().getClass()); 
    } 

} 

आउटपुट

[EL Warning]: 2012-01-20 10:36:41.828--Ignoring attribute [name] on class [com.test.example.B] as no Property was generated for it. 
[EL Warning]: 2012-01-20 10:36:41.828--Ignoring attribute [name] on class [com.test.example.C] as no Property was generated for it. 
class com.test.example.A 
class com.test.example.B 
class com.test.example.C 

2 प्रश्न: अन्य समस्या यह है कि मैं नहीं जानता अगर JAXB एक्सएमएल अधिभावी में सक्षम विशेषता नाम है जैसे कि यह एक्सएमएल फ़ाइल के अंदर की उम्मीद है है (@nameA, @nameB और nameC सब चर्चा करते हुए एनाम में), ऐसा करने का कोई तरीका है?

आप इस प्रश्न के लिए XmlAdapter का लाभ उठा सकते हैं।यह दृष्टिकोण भी अपने पहले सवाल का जवाब देने के लिए इस्तेमाल किया जा सकता है:

AAdapter

package com.test.example; 

import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.adapters.XmlAdapter; 

public class AAdapter extends XmlAdapter<AAdapter.AdaptedA, A> { 

    @Override 
    public AdaptedA marshal(A a) throws Exception { 
     if(null == a) { 
      return null; 
     } 
     AdaptedA adaptedA = new AdaptedA(); 
     if(a instanceof C) { 
      C c = (C) a; 
      adaptedA.nameC = c.getName(); 
      adaptedA.attrFromC = c.getAttrFromC(); 
     } else if(a instanceof B) { 
      B b = (B) a; 
      adaptedA.nameB = b.getName(); 
      adaptedA.attrFromB = b.getAttrFromB(); 
     } else if(a instanceof A) { 
      adaptedA.nameA = a.getName(); 
     } 
     return adaptedA; 
    } 

    @Override 
    public A unmarshal(AdaptedA adaptedA) throws Exception { 
     if(null == adaptedA) { 
      return null; 
     } 
     if(null != adaptedA.attrFromC) { 
      C c = new C(); 
      c.setName(adaptedA.nameC); 
      c.setAttrFromC(adaptedA.attrFromC); 
      return c; 
     } else if(null != adaptedA.attrFromB) { 
      B b = new B(); 
      b.setName(adaptedA.nameB); 
      b.setAttrFromB(adaptedA.attrFromB); 
      return b; 
     } 
     A a = new A(); 
     a.setName(adaptedA.nameA); 
     return a; 
    } 

    public static class AdaptedA { 
     @XmlAttribute public String nameA; 
     @XmlAttribute public String nameB; 
     @XmlAttribute public String nameC; 
     @XmlAttribute public String attrFromB; 
     @XmlAttribute public String attrFromC; 
    } 

} 

मेटाडाटा (OXM-2.xml)

<?xml version="1.0"?> 
<xml-bindings 
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" 
    package-name="com.test.example" 
    version="2.3"> 
    <java-types> 
     <java-type name="MyContainerClass" xml-accessor-type="NONE"> 
      <xml-root-element name="MyContainer" /> 
      <java-attributes> 
       <xml-element java-attribute="myObject" name="MyObject"> 
       <xml-java-type-adapter value="com.test.example.AAdapter"/> 
       </xml-element> 
      </java-attributes> 
     </java-type> 
    </java-types> 
</xml-bindings> 

demo2

package com.test.example; 

import java.io.StringReader; 
import java.util.*; 
import javax.xml.bind.*; 
import org.eclipse.persistence.jaxb.JAXBContextFactory; 

public class Demo2 { 

    public static void main(String[] args) throws Exception { 
     Map<String, Object> properties = new HashMap<String, Object>(); 
     properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "com/test/example/oxm-2.xml"); 
     JAXBContext jc = JAXBContext.newInstance(new Class[] {MyContainerClass.class}, properties); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

     StringReader aXml = new StringReader("<MyContainer><MyObject nameA='foo'/></MyContainer>"); 
     MyContainerClass myContainerA = (MyContainerClass) unmarshaller.unmarshal(aXml); 
     System.out.println(myContainerA.getMyObject().getClass()); 
     marshaller.marshal(myContainerA, System.out); 

     StringReader bXml = new StringReader("<MyContainer><MyObject nameB='foo' attrFromB='bar'/></MyContainer>"); 
     MyContainerClass myContainerB = (MyContainerClass) unmarshaller.unmarshal(bXml); 
     System.out.println(myContainerB.getMyObject().getClass()); 
     marshaller.marshal(myContainerB, System.out); 

     StringReader cXml = new StringReader("<MyContainer><MyObject nameC='foo' attrFromC='bar'/></MyContainer>"); 
     MyContainerClass myContainerC = (MyContainerClass) unmarshaller.unmarshal(cXml); 
     System.out.println(myContainerC.getMyObject().getClass()); 
     marshaller.marshal(myContainerC, System.out); 
    } 

} 

आउटपुट

class com.test.example.A 
<?xml version="1.0" encoding="UTF-8"?> 
<MyContainer> 
    <MyObject nameA="foo"/> 
</MyContainer> 
class com.test.example.B 
<?xml version="1.0" encoding="UTF-8"?> 
<MyContainer> 
    <MyObject nameB="foo" attrFromB="bar"/> 
</MyContainer> 
class com.test.example.C 
<?xml version="1.0" encoding="UTF-8"?> 
<MyContainer> 
    <MyObject nameC="foo" attrFromC="bar"/> 
</MyContainer> 
+1

अपने जवाब के पहले भाग पढ़ना, यह पहले से ही वास्तव में उपयोगी किया जा रहा है। धन्यवाद। दूसरे भाग के लिए इंतजार नहीं कर सकता। – Drewman

+0

बस यह सुनिश्चित करने के लिए, यदि क्लास एक्स्ट्रेक्टर के भीतर, मैं नोड की उपस्थिति का परीक्षण करना चाहता हूं, न कि विशेषता। क्या मैं record.get ("SomeNodeName") या record.get ("SomeNodeName/text()") का उपयोग कर सकता हूं? – Drewman

+0

@ user1121108 - मैंने दूसरे भाग के लिए एक जवाब जोड़ा है। कक्षा निकालने में आप केवल गुणों की उपस्थिति के लिए परीक्षण करने में सक्षम होंगे। निष्पादन में उस बिंदु पर उप तत्वों को अभी तक संसाधित नहीं किया गया है। –