質問
JAXBのイントロスペクションを使用して、JAXBアノテーションでマークアップされた既存のドメインオブジェクトをマーシャリングおよびアンマッシュアしようとしています。ほとんどのことは期待どおりに機能しますが、かなり単純なクラスをシリアル化するのにかなり苦労しています。このクラスは、いくつかのBeanで@XmlElementとして使用され、次のようになります。
public class Range<E extends Comparable<E>> implements Serializable {
protected boolean startInclusive, endInclusive;
protected E start, end;
public Range(){
startInclusive = endInclusive = true;
}
public boolean contains(E value){...}
public E getEnd() {
return end;
}
public void setEnd(E end) {
this.end = end;
}
public boolean isEndInclusive() {
return endInclusive;
}
public void setEndInclusive(boolean endInclusive) {
this.endInclusive = endInclusive;
}
public E getStart() {
return start;
}
public void setStart(E start) {
this.start = start;
}
public boolean isStartInclusive() {
return startInclusive;
}
public void setStartInclusive(boolean startInclusive) {
this.startInclusive = startInclusive;
}
}
次のことを試みましたが、成功しませんでしたが、JAXBはまだインターフェイスComparableに対して怒っています。
public class DoubleRange extends Range<Double> {}
Beanゲッターの戻り値の型としてRangeとDoubleRangeの両方を使用すると、次のような例外が発生します。
java.lang.Comparable is an interface, and JAXB can't handle interfaces. this problem is related to the following location: at java.lang.Comparable at protected java.lang.Comparable com.controlpath.util.Range.start at example.util.Range at example.util.DoubleRange at public example.util.DoubleRange example.domain.SomeBean.getRange() at example.domain.SomeBean
ほとんどの場合、List <!> lt; T <!> gt;およびMap <!> lt; T、U <!> gt; JAXB仕様では、Beanでこれらのタイプに遭遇したときに特別な規定があるためにのみ機能しますが、非ジェネリックフィールドで範囲を再実装することなく、JAXBイントロスペクションエンジンに必要なものを伝える方法はありますか?
解決
次の手順を実行することにより、カスタムアダプター(JAXBのXmlAdapterを使用しない)を作成できます。
1)すべての種類の要素を受け入れ、JAXBアノテーションを持つクラスを宣言します そして、あなたが望むようにそれらを処理します(私の例では、すべてを文字列に変換します)
@YourJAXBAnnotationsGoHere
public class MyAdapter{
@XmlElement // or @XmlAttribute if you wish
private String content;
public MyAdapter(Object input){
if(input instanceof String){
content = (String)input;
}else if(input instanceof YourFavoriteClass){
content = ((YourFavoriteClass)input).convertSomehowToString();
}else if(input instanceof .....){
content = ((.....)input).convertSomehowToString();
// and so on
}else{
content = input.toString();
}
}
}
// I would suggest to use a Map<Class<?>,IMyObjToStringConverter> ...
// to avoid nasty if-else-instanceof things
2)マーシャリングされるクラスでEの代わりにこのクラスを使用します
注
- もちろん、これは複雑な(ネストされた)データ構造では動作しません。
- これを再びアンマーシャリングする方法を考える必要があります。もっと難しいかもしれません。もし トリッキーすぎるので、私の提案よりも良い提案を待ってください;)
他のヒント
方法について
public class Range<**E extends Number**> implements Serializable { ...
-
数字はクラス
-
JAXBは、デフォルト Numberのマーシャリング/アンマーシャリングルールを知っているに違いありません
特定の型にアンマーシャリングするには、XmlAdapterが必要です ここで説明されています: JAXB継承、非整列化されたクラスのサブクラスへ
シンプルなXMLシリアル化のようなものを試してください@Elementや@ElementListなど。プログラミングモデルは非常に似ていますが、JAXBより単純です。
実際には、なぜこれが機能しないのかは明確ではありません。 JAXBは特定のサブタイプを正しく解決できるはずです。このタイプがルートタイプではない場合(およびその場合のみ)(説明どおりではありません)。つまり、それはただのBeanです。したがって、Tが直接型に置き換えられたBeanが機能する場合は、サブクラス化を使用して型をバインドする汎用バージョン(例で行われているように)が必要です。
では、実装のバグかもしれませんか?
したがって、問題はE
のstart
の消去であり、end
はComparable
のようです。インターフェイスを処理できない場合は、Object
を試すことができますが、それでも不満を言うことを望みます(現在または今後)。おそらくRange
を抽象化し、特定の<=>ごとに特殊化することができます。 JAXBについて詳しく知る必要があります。