45

XMLの読み取りと書き込みにJAXBを使用しています。私が欲しいのは、マーシャリングには基本JAXBクラスを使用し、アンマーシャリングには継承されたJAXBクラスを使用することです。これは、送信側のJavaアプリケーションがXMLを別の受信側のJavaアプリケーションに送信できるようにするためです。送信者と受信者は、共通のJAXBライブラリを共有します。レシーバーがXMLをアンマーシャリングして、汎用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() ...
}

マーシャリングは期待どおりに機能します。問題はアンマーシャリングにありPerson、JAXBContextがサブクラス化されたのパッケージ名を使用しているにもかかわらず、アンマーシャリングを行いますReceiverPerson

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

私が欲しいのは、にマーシャルを解除することReceiverPersonです。私がこれを行うことができた唯一の方法は、から削除すること@XmlRootElementですPerson。残念ながら、これを行うPersonとマーシャリングされなくなります。@XmlRootElementこれは、JAXBが基本クラスから始まり、適切な名前の最初のクラスが見つかるまで下に向かって進むかのようです。createPerson()に戻るメソッドを追加しようとしましReceiverPersonObjectFactoryが、それは役に立ちません。

4

6 に答える 6

21

次のスニペットは、緑色のライトを使用したJunit4テストのメソッドです。

@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(したがって、パラメーターはこの順序で渡される必要があります)。

于 2009-03-18T03:23:48.220 に答える
20

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
}

この概念を使用することで、マーシャリング/アンマーシャリング プロセスを自分で制御できると確信しています (構築する正しい [sub|super]type の選択を含む)。

于 2009-03-11T22:14:23.467 に答える
13

Person のサブクラスを 2 回 (1 回は受信者用、もう 1 回は送信者用)、これらのサブクラスには XmlRootElement のみを配置します ( PersonXmlRootElement なしでスーパークラス を残します)。送信側と受信側の両方が同じ 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 注釈がある場合、注意した問題を回避します。

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

于 2009-03-18T09:15:48.980 に答える
7

カスタム 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();
    }
}
于 2010-12-01T05:24:09.487 に答える
3

なぜあなたがこれをやりたいのかわかりません...それは私にはそれほど安全ではないようです。

ReceiverPersonに追加のインスタンス変数があるとどうなるかを考えてみてください...そうすると、それらの変数はnull、0、またはfalseになります...そしてnullが許可されていないか、数値が0より大きくなければならない場合はどうなりますか?

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

于 2009-03-06T18:42:00.727 に答える
-1

実際には 2 つの別個のアプリがあるため、"Person" クラスの異なるバージョンを使用してそれらをコンパイルします。レシーバー アプリには@XmlRootElement(name="person")on がありませんPerson。これは見苦しいだけでなく、送信者と受信者の両方に同じ Person 定義を使用することで必要な保守性が損なわれます。その1つの償還機能は、それが機能することです.

于 2009-03-17T09:26:51.497 に答える