web-dev-qa-db-ja.com

Java / JAXB:属性に基づいてXmlを特定のサブクラスにアンマーシャルします

JAXBを使用して、xmlの属性に基づいてxmlを特定のJavaクラスにアンマーシャルすることは可能ですか?

<shapes>
  <shape type="square" points="4" square-specific-attribute="foo" />
  <shape type="triangle" points="3" triangle-specific-attribute="bar" />
</shapes>

三角形と正方形を含み、それぞれが独自の形状固有の属性を持つShapeオブジェクトのリストが必要です。 IE:

abstract class Shape {
    int points;
    //...etc
}

class Square extends Shape {
    String square-specific-attribute;
    //...etc
}

class Triangle extends Shape {
    String triangle-specific-attribute;
    //...etc
}

私は現在、すべての属性を1つの大きな「Shape」クラスに入れているだけで、理想的とは言えません。

シェイプに適切な名前のxml要素があれば、これを機能させることができますが、残念ながら、取得するxmlを制御できません。

ありがとう!

29
Frothy

JAXBは仕様であり、特定の実装はこのようなことを行うための拡張ポイントを提供します。 EclipseLink JAXB(MOXy) を使用している場合は、Shapeクラスを次のように変更できます。

import javax.xml.bind.annotation.XmlAttribute;
import org.Eclipse.persistence.oxm.annotations.XmlCustomizer;

@XmlCustomizer(ShapeCustomizer.class)
public abstract class Shape {

    int points;

    @XmlAttribute
    public int getPoints() {
        return points;
    }

    public void setPoints(int points) {
        this.points = points;
    }

}

次に、MOXy @XMLCustomizerを使用して、InheritancePolicyにアクセスし、クラスインジケーターフィールドを「@xsi:type」から「type」に変更できます。

import org.Eclipse.persistence.config.DescriptorCustomizer;
import org.Eclipse.persistence.descriptors.ClassDescriptor;

public class ShapeCustomizer implements DescriptorCustomizer {

    @Override
    public void customize(ClassDescriptor descriptor) throws Exception {
        descriptor.getInheritancePolicy().setClassIndicatorFieldName("@type");
    }
}

モデルクラス(Shape、Squareなど)にjaxb.propertiesファイルがあり、EclipseLink MOXyJAXB実装を指定する次のエントリがあることを確認する必要があります。

javax.xml.bind.context.factory=org.Eclipse.persistence.jaxb.JAXBContextFactory

以下は、残りのモデルクラスです。

import Java.util.ArrayList;
import Java.util.List;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Shapes {

    private List<Shape> shape = new ArrayList<Shape>();;

    public List<Shape> getShape() {
        return shape;
    }

    public void setShape(List<Shape> shape) {
        this.shape = shape;
    }

}

平方

import javax.xml.bind.annotation.XmlAttribute;

public class Square extends Shape {
    private String squareSpecificAttribute;

    @XmlAttribute(name="square-specific-attribute")
    public String getSquareSpecificAttribute() {
        return squareSpecificAttribute;
    }

    public void setSquareSpecificAttribute(String s) {
        this.squareSpecificAttribute = s;
    }

}

三角形

import javax.xml.bind.annotation.XmlAttribute;

public class Triangle extends Shape {
    private String triangleSpecificAttribute;

    @XmlAttribute(name="triangle-specific-attribute")
    public String getTriangleSpecificAttribute() {
        return triangleSpecificAttribute;
    }

    public void setTriangleSpecificAttribute(String t) {
        this.triangleSpecificAttribute = t;
    }

}

以下は、すべてが機能することを確認するためのデモプログラムです。

import Java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jaxbContext = JAXBContext.newInstance(Shapes.class, Triangle.class, Square.class);

        StringReader xml = new StringReader("<shapes><shape square-specific-attribute='square stuff' type='square'><points>4</points></shape><shape triangle-specific-attribute='triangle stuff' type='triangle'><points>3</points></shape></shapes>");
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        Shapes root = (Shapes) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }
}

これがお役に立てば幸いです。

EclipseLink MOXyの詳細については、以下を参照してください。

[〜#〜]編集[〜#〜]

EclipseLink 2.2では、これを簡単に構成できるようにしています。詳細については、次の記事を確認してください。

18
bdoughan

アノテーション@ XmlElementsを使用すると、どのタグがどのサブクラスに対応するかを指定できます。

@XmlElements({
    @XmlElement(name="square", type=Square.class),
    @XmlElement(name="triangle", type=Triangle.class)
})
public List<Shape> getShape() {
    return shape;
}

@ XmlElements のjavadocも参照してください。

7
Barmak

AFAIK、Shapeのマーシャリング/アンマーシャリングを処理する方法を知っている XmlAdapter を作成する必要があります。

3
Quotidian

いいえ、それはオプションではないのではないかと思います。JAXBはそれほど柔軟ではありません。

私が提案できる最善の方法は、属性に基づいて「正しい」タイプをインスタンス化するメソッドをShapeクラスに配置することです。クライアントコードは、そのファクトリメソッドを呼び出して取得します。

ごめんなさい.

0
skaffman