2009-03-06 23 views
39

私はXMLを読み書きするためにJAXBを使用しています。私が望むのは、マーシャリングのために基底のJAXBクラスを使用し、アンマーシャリングのために継承されたJAXBクラスを使用することです。これは、送信側JavaアプリケーションがXMLを別の受信側Javaアプリケーションに送信できるようにするためです。送信者と受信者は共通のJAXBライブラリを共有します。受信者に、XMLをアンマーシャリングして、汎用のJAXBクラスを拡張する受信者固有のJAXBクラスにしたいと思います。JAXB継承、マーシャリングされたクラスのサブクラスへの非整列

例:

これは、送信者によって使用される一般的なJAXBクラスです。

@XmlRootElement(name="person") 
public class Person { 
    public String name; 
    public int age; 
} 

これはXMLをアンマーシャリングするときに使用される受信者固有のJAXBクラスです。レシーバ・クラスは、レシーバ・アプリケーション固有のロジックを持っています。

@XmlRootElement(name="person") 
public class ReceiverPerson extends Person { 
    public doReceiverSpecificStuff() ... 
} 

マーシャリングは期待通りに機能します。問題は非整列化であり、サブクラス化されたパッケージ名ReceiverPersonを使用するJAXBContextにもかかわらず、まだPersonに整列されていません。

JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson); 

私が望むのは、ReceiverPersonにアンマーシャリングすることです。これを行うには、@XmlRootElementPersonから削除するしかありません。残念ながらこれにより、Personがマーシャリングされることがなくなります。 JAXBが基本クラスから始まり、適切な名前の最初の@XmlRootElementが見つかるまで下がります。私はReceiverPersonObjectFactoryに返すcreatePerson()メソッドを追加しようとしましたが、それは役に立ちません。

答えて

19

あなたはJAXB 2.0を正しく使用していますか?クラスがあり

(JDK6ので):

一つはサブクラス、および以下のメソッドをオーバーライドすることができ
javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType> 

public abstract BoundType unmarshal(ValueType v) throws Exception; 
public abstract ValueType marshal(BoundType v) throws Exception; 

例:

public class YourNiceAdapter 
     extends XmlAdapter<ReceiverPerson,Person>{ 

    @Override public Person unmarshal(ReceiverPerson v){ 
     return v; 
    } 
    @Override public ReceiverPerson marshal(Person v){ 
     return new ReceiverPerson(v); // you must provide such c-tor 
    } 
} 

使用法によって行われます次のようになります。

@Your_favorite_JAXB_Annotations_Go_Here 
class SomeClass{ 
    @XmlJavaTypeAdapter(YourNiceAdapter.class) 
    Person hello; // field to unmarshal 
} 

私は、このコンセプトを使用することで、自分でマーシャリング/アンマーシャリングプロセスを制御できることを確信しています(適切な[サブ|スーパー]タイプの選択を含む)。

+0

私はこれを試しても動作しません。私が試しても、ObjectFactoryまたはXmlAdapterは決して呼び出されません。私が読んだことから、SunのJAXBは静的なクラス参照によって解決します。 Glassfishの実装にはより有望なhttps://jaxb.dev.java.net/guide/Adding_behaviors.html –

+0

があります.XmlJavaAdaptersは、JAXB以外のクラスをJAXBで使用できるようにするのに適しています。 PersonとReceiverPerson(および/またはSuperlassがある場合)はJAXBアノテーションを持っているため、動作しません。 JAXBを使用しないPersonとReceiverPerson、およびその両方のためのアダプタが必要です。 –

+0

マーシャル|アンマーシャルのスワップタイプを試しましたか? [...]はXmlAdapter [...]を継承しています] @Override public ReceiverPerson unmarshal(Person v){ 新しいReceiverPerson(v)を返します。 } @Override public Person marshal(ReceiverPerson v){ return v; } –

3

私はあなたがこれをしたいと思う理由を確信していません...それは私にすべて安全だとは思われません。

ReceiverPersonに何が起こるかを考慮すると、インスタンス変数が追加されています...これらの変数がnull、0、またはfalseであると思います。 0より大きい?

おそらくあなたがしたいことはPersonで読み込まれ、それから新しいReceiverPersonを構築することです(おそらく、Personをとるコンストラクタを提供します)。

+0

ReceiverPersonには追加の変数がありません。その目的は、受信機固有のものを行うことです。 –

+0

あなたはまだあるレベルで別のクラスとしてそれを作成しなければなりません...そして、それを行う通常の方法は、変換したいタイプを取るコンストラクタ経由です。また、それが今の場合であるからといって、将来あなたがヴァルスを追加しないことを意味するわけではありません。 – TofuBeer

+2

Yikes、それは、クラスが将来何をするかについての多くの推測への道です。今日のために十分であれば十分ですが、後でいつでもリファクタリングすることができます... –

-1

実際には2つの別々のアプリがあるので、受信者アプリがPersonにある@XmlRootElement(name="person")を持たない「Person」クラスの異なるバージョンでコンパイルしてください。これは醜いだけでなく、送信者と受信者の両方でPersonの同じ定義を使用することで、保守性を損なうことになります。その1つの特典は、それが機能することです。

+0

両方のアプリが共通の基本JAXBクラスを共有したいと思っています。 –

+0

@Steve:私の第2の( "きちんとしたやり方")解決策は、共通の基本JAXBクラスを共有しています...あなたは最初の段落だけを読むように見えます。私は編集します。 – 13ren

+0

「きちんとした方法」を新しい答えに分けました。 – 13ren

17

次のスニペットは、緑色光をJUnitの4試験する方法であって、

@Test 
public void testUnmarshallFromParentToChild() throws JAXBException { 
    Person person = new Person(); 
    int age = 30; 
    String name = "Foo"; 
    person.name = name; 
    person.age= age; 

    // Marshalling 
    JAXBContext context = JAXBContext.newInstance(person.getClass()); 
    Marshaller marshaller = context.createMarshaller(); 

    StringWriter writer = new StringWriter(); 
    marshaller.marshal(person, writer); 

    String outString = writer.toString(); 

    assertTrue(outString.contains("</person")); 

    // Unmarshalling 
    context = JAXBContext.newInstance(Person.class, RecieverPerson.class); 
    Unmarshaller unmarshaller = context.createUnmarshaller(); 
    StringReader reader = new StringReader(outString); 
    RecieverPerson reciever = (RecieverPerson)unmarshaller.unmarshal(reader); 

    assertEquals(name, reciever.name); 
    assertEquals(age, reciever.age); 
} 

重要な部分は、非整列化コンテキストのJAXBContext.newInstance(Class... classesToBeBound)方法の使用である:と

context = JAXBContext.newInstance(Person.class, RecieverPerson.class); 

この呼び出しでは、JAXBは指定されたクラスの参照クロージャを計算し、RecieverPersonを認識します。テストは合格になります。パラメータの順序を変更すると、java.lang.ClassCastExceptionが返されます(したがって、をこの順序で渡す必要があります)。

12

Subclass Person受信者用に1回、送信者用に1回、これらのサブクラスにXmlRootElementを配置します(スーパークラスを残して、Person、XmlRootElementなし)。送信者と受信者の両方が同じJAXB基本クラスを共有することに注意してください。

@XmlRootElement(name="person") 
public class ReceiverPerson extends Person { 
    // receiver specific code 
} 

@XmlRootElement(name="person") 
public class SenderPerson extends Person { 
    // sender specific code (if any) 
} 

// note: no @XmlRootElement here 
public class Person { 
    // data model + jaxb annotations here 
} 

[JAXBで動作確認済み]継承階層の複数のクラスにXmlRootElementアノテーションがある場合には、この問題を回避します。

これは一般的なデータモデルを分離するので、間違いなくより多くのオブジェクト指向アプローチであるため、「回避策」ではありません。

+0

これは最善の解決策に見えますが、おそらく私の場合はやや複雑なので、私にとってはうまくいかないでしょう。 SenderPersonとReceiverPersonの両方のリストを含む 'Group'クラスのクラスインスタンスを変換する必要があります。そのようなもの @XmlRootElement パブリッククラスグループ{ public list senders; 公開リスト受信者。 } –

+0

このソリューションが私にとっても有効であることを確認できます。 – icfantv

6

アンマーシャリング時に目的のクラスをインスタンス化するカスタムObjectFactoryを作成します。例:

JAXBContext context = JAXBContext.newInstance("com.whatever.mypackage"); 
Unmarshaller unmarshaller = context.createUnmarshaller(); 
unmarshaller.setProperty("com.sun.xml.internal.bind.ObjectFactory", new ReceiverPersonObjectFactory()); 
return unmarshaller; 

public class ReceiverPersonObjectFactory extends ObjectFactory { 
    public Person createPerson() { 
     return new ReceiverPerson(); 
    } 
} 
+0

これは、最初にバインドされた型クラスインスタンスを作成し、すべての属性を値クラスインスタンスにコピーするXmlAdpaterソリューションと比べて、各オブジェクトを1回だけ作成する必要があるため、非常に巧妙な方法です。しかし、問題は:OpenJDKのようなSun/Oracleとは異なるJREでも動作しますか? – Robert

+0

+1は私にとって唯一の解決策です。私は、JAXB 2のサブクラス化(implClass)の型置換を使用しようとしています*。xsdファイルで、非マーシャリングされたクラスはimplClassで指定されたもの以外のものが生成されます。 vocaroの答えが問題を解決する! – Tatera

+0

JAXBが外部実装(SDKのものではない)の場合のマイナーアップデート:プロパティは '' com.sun.xml.bind.ObjectFactory "'と呼ばれます。 –