私はJAXBを使用してXMLを読み書きしています。私が欲しいのは、マーシャリングにベースJAXBクラスを使用し、アンマーシャリングに継承されたJAXBクラスを使用することです。これは、送信者がJavaアプリケーションからXMLを別の受信者に送信できるようにするためです。Javaアプリケーション。送信者と受信者は共通のJAXBライブラリを共有します。汎用JAXBクラスを拡張するレシーバー固有のJAXBクラスにXMLを非整列化します。
例:
これは、送信者が使用する一般的な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() ...
}
_
マーシャリングは期待どおりに機能します。問題は非整列化にあり、JAXBContextがサブクラス化されたPerson
のパッケージ名を使用しているにもかかわらず、依然としてReceiverPerson
に非整列化されます。
_JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson);
_
私が望むのは、ReceiverPerson
にアンマーシャルすることです。これを行うことができた唯一の方法は、Person
から_@XmlRootElement
_を削除することです。残念ながら、これを行うとPerson
がマーシャリングされなくなります。まるでJAXBが基本クラスから開始し、適切な名前を持つ最初の_@XmlRootElement
_を見つけるまで下降するかのようです。 ReceiverPerson
をObjectFactory
に返すcreatePerson()
メソッドを追加しようとしましたが、それは役に立ちません。
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
}
この概念を使用することで、自分でマーシャリング/アンマーシャリングプロセスを制御できます(構築する正しい[サブ|スーパー]タイプの選択を含む)。
次のスニペットは、緑色のライトを使用した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
_が得られます(したがって、必須はこの順序で渡されます)。
Personを2回サブクラス化し、1回は受信者用、1回は送信者用とし、XmlRootElementをこれらのサブクラスにのみ配置します(XmlRootElementなしでスーパークラスPerson
を残します)。送信者と受信者の両方が同じ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注釈がある場合、注意する問題を回避します。
これはおそらく、より適切でより多くのOOアプローチです。これは、共通のデータモデルを分離するため、「回避策」ではないためです。
カスタム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();
}
}
なぜあなたはこれをしたいのか分かりません...それは私にとってそれほど安全ではないようです。
ReceiverPersonに追加のインスタンス変数があるとどうなるか考えてみてください...その後、変数がnull、0、またはfalseであると推測します... ?
おそらくあなたがやりたいことは、Personで読み取られ、それから新しいReceiverPersonを構築することだと思います(おそらくPersonを受け取るコンストラクタを提供します)。
重要な点は、「アンマーシャリング時、JAXBが基本クラスで開始し、その方法を実行する」ことです。ただし、アンマーシャリング時はReceiverPerson
を指定しますが、@XmlRootElement
アノテーションが付いたクラスPerson
があります。 Person
に非整列化します。
そのため、クラスPerson
で@XmlRootElement
を削除する必要がありますが、これを行うとPerson
がマーシャリングされなくなります。
解決策は、Personを拡張し、@XmlRootElement
で注釈を付けるDummyPerson
を作成することです。これにより、DummyPerson
とReceiverPerson
が同じレベルになります。 、DummyPerson
の代わりにPerson
をマーシャリングし、xmlStringをReceiverPerson
にアンマーシャルできます。
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
public String name;
public int age;
}
@XmlRootElement(name = "person")
public class DummyPerson extends Person {
}
@XmlRootElement(name = "person")
public class ReceiverPerson extends Person {
public doReceiverSpecificStuff();
}
参照:
JAXBの継承サポート