JAXB ( javax.xml.bind
) は、必要なすべてのことを実行できますが、一部のビットは他のビットよりも簡単です。簡単にするために、すべての XML ファイルに名前空間があると仮定します。名前空間がない場合はややこしくなりますが、StAX API を使用して回避できます。
<list xmlns="http://example.com/cars">
<brand id="1">
Volvo
</brand>
<car>
<brand>BMW</brand>
<v12engine horsePowers="300" />
</car>
<car>
<brand refId="1" />
<v6engine fuel="unleaded" />
</car>
</list>
対応package-info.java
する
@XmlSchema(namespace = "http://example.com/cars",
elementFormDefault = XmlNsForm.QUALIFIED)
package cars;
import javax.xml.bind.annotation.*;
要素名によるエンジンの種類
これは、次を使用して簡単です@XmlElementRef
。
package cars;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Car {
String brand;
@XmlElementRef
Engine engine;
}
@XmlRootElement
abstract class Engine {
}
@XmlRootElement(name = "v12engine")
@XmlAccessorType(XmlAccessType.FIELD)
class V12Engine extends Engine {
@XmlAttribute
int horsePowers;
}
@XmlRootElement(name = "v6engine")
@XmlAccessorType(XmlAccessType.FIELD)
class V6Engine extends Engine {
// override the default attribute name, which would be fuelType
@XmlAttribute(name = "fuel")
String fuelType;
}
さまざまなタイプのEngine
すべてに注釈が付け@XmlRootElement
られ、適切な要素名が付けられています。アンマーシャリング時に、XML で見つかった要素名を使用して、使用するEngine
サブクラスを決定します。したがって、与えられた XML
<car xmlns="http://example.com/cars">
<brand>BMW</brand>
<v12engine horsePowers="300" />
</car>
およびアンマーシャリングコード
JAXBContext ctx = JAXBContext.newInstance(Car.class, V6Engine.class, V12Engine.class);
Unmarshaller um = ctx.createUnmarshaller();
Car c = (Car)um.unmarshal(new File("file.xml"));
assert "BMW".equals(c.brand);
assert c.engine instanceof V12Engine;
assert ((V12Engine)c.engine).horsePowers == 300;
新しいタイプの を追加するには、Engine
単に新しいサブクラスを作成し、@XmlRootElement
必要に応じて注釈を付けて、この新しいクラスを に渡されるリストに追加しJAXBContext.newInstance()
ます。
ブランドの相互参照
@XmlID
JAXB にはandに基づく相互参照メカニズム@XmlIDREF
がありますが、ID 属性が有効な XML ID、つまり XML 名である必要があり、特に完全に数字で構成されていない必要があります。しかし、「前方」参照 (つまり、まだ「宣言」されていない<car>
a を参照する a) を必要としない限り、自分で相互参照を追跡することはそれほど難しくありません。<brand>
最初のステップは、JAXB クラスを定義して、<brand>
package cars;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Brand {
@XmlValue // i.e. the simple content of the <brand> element
String name;
// optional id and refId attributes (optional because they're
// Integer rather than int)
@XmlAttribute
Integer id;
@XmlAttribute
Integer refId;
}
Brand
ここで、オブジェクトとString
必要なとの間で変換Car
し、id/ref マッピングを維持するための「型アダプター」が必要です。
package cars;
import javax.xml.bind.annotation.adapters.*;
import java.util.*;
public class BrandAdapter extends XmlAdapter<Brand, String> {
private Map<Integer, Brand> brandCache = new HashMap<Integer, Brand>();
public Brand marshal(String s) {
return null;
}
public String unmarshal(Brand b) {
if(b.id != null) {
// this is a <brand id="..."> - cache it
brandCache.put(b.id, b);
}
if(b.refId != null) {
// this is a <brand refId="..."> - pull it from the cache
b = brandCache.get(b.refId);
}
// and extract the name
return (b.name == null) ? null : b.name.trim();
}
}
別の注釈を使用して、アダプターをbrand
フィールドにリンクします。Car
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Car {
@XmlJavaTypeAdapter(BrandAdapter.class)
String brand;
@XmlElementRef
Engine engine;
}
パズルの最後の部分は<brand>
、最上位で見つかった要素がキャッシュに保存されるようにすることです。ここに完全な例があります
package cars;
import javax.xml.bind.*;
import java.io.File;
import java.util.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Main {
public static void main(String[] argv) throws Exception {
List<Car> cars = new ArayList<Car>();
JAXBContext ctx = JAXBContext.newInstance(Car.class, V12Engine.class, V6Engine.class, Brand.class);
Unmarshaller um = ctx.createUnmarshaller();
// create an adapter, and register it with the unmarshaller
BrandAdapter ba = new BrandAdapter();
um.setAdapter(BrandAdapter.class, ba);
// create a StAX XMLStreamReader to read the XML file
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(new File("file.xml")));
xsr.nextTag(); // root <list> element
xsr.nextTag(); // first <brand> or <car> child
// read each <brand>/<car> in turn
while(xsr.getEventType() == XMLStreamConstants.START_ELEMENT) {
Object obj = um.unmarshal(xsr);
// unmarshal from an XMLStreamReader leaves the reader pointing at
// the event *after* the closing tag of the element we read. If there
// was a text node between the closing tag of this element and the opening
// tag of the next then we will need to skip it.
if(xsr.getEventType() != XMLStreamConstants.START_ELEMENT && xsr.getEventType() != XMLStreamConstants.END_ELEMENT) xsr.nextTag();
if(obj instanceof Brand) {
// top-level <brand> - hand it to the BrandAdapter so it can be
// cached if necessary
ba.unmarshal((Brand)obj);
}
if(obj instanceof Car) {
cars.add((Car)obj);
}
}
xsr.close();
// at this point, cars contains all the Car objects we found, with
// any <brand> refIds resolved.
}
}