XML

さて、22日の日記のCastorのメモでは実はバインディングを行わない方法での使用方法だった。
今回はバインディングを行いJavaのクラスを自分で一切作成しない方法を書いておこう。
なお、XMLの構造は22日に使用したのと同じものとする。
行うことは以下2点。



















  • Castorからソース作成を行うためのAntビルドファイルを作成



















Antを実行すると複数のJavaソースファイルが生成された。
以下がマーシャルが行われるクラス。


/*
* This class was automatically generated with
* Castor 1.1, using an XML
* Schema.
* $Id$
*/

package castor;

//---------------------------------/
//- Imported classes and packages -/
//---------------------------------/

import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;

/**
* Class BookList.
*
* @version $Revision$ $Date$
*/
public class BookList implements java.io.Serializable {


//--------------------------/
//- Class/Member Variables -/
//--------------------------/

/**
* Field _bookList.
*/
private java.util.Vector _bookList;


//----------------/
//- Constructors -/
//----------------/

public BookList() {
super();
this._bookList = new java.util.Vector();
}


//-----------/
//- Methods -/
//-----------/

/**
*
*
* @param vBook
* @throws java.lang.IndexOutOfBoundsException if the index
* given is outside the bounds of the collection
*/
public void addBook(
final castor.Book vBook)
throws java.lang.IndexOutOfBoundsException {
this._bookList.addElement(vBook);
}

/**
*
*
* @param index
* @param vBook
* @throws java.lang.IndexOutOfBoundsException if the index
* given is outside the bounds of the collection
*/
public void addBook(
final int index,
final castor.Book vBook)
throws java.lang.IndexOutOfBoundsException {
this._bookList.add(index, vBook);
}

/**
* Method enumerateBook.
*
* @return an Enumeration over all castor.Book elements
*/
public java.util.Enumeration enumerateBook(
) {
return this._bookList.elements();
}

/**
* Method getBook.
*
* @param index
* @throws java.lang.IndexOutOfBoundsException if the index
* given is outside the bounds of the collection
* @return the value of the castor.Book at the given index
*/
public castor.Book getBook(
final int index)
throws java.lang.IndexOutOfBoundsException {
// check bounds for index
if (index < 0 || index >= this._bookList.size()) {
throw new IndexOutOfBoundsException("getBook: Index value '" + index + "' not in range [0.." + (this._bookList.size() - 1) + "]");
}

return (castor.Book) _bookList.get(index);
}

/**
* Method getBook.Returns the contents of the collection in an
* Array.

Note: Just in case the collection contents are
* changing in another thread, we pass a 0-length Array of the
* correct type into the API call. This way we know
* that the Array returned is of exactly the correct length.
*
* @return this collection as an Array
*/
public castor.Book[] getBook(
) {
castor.Book[] array = new castor.Book[0];
return (castor.Book[]) this._bookList.toArray(array);
}

/**
* Method getBookCount.
*
* @return the size of this collection
*/
public int getBookCount(
) {
return this._bookList.size();
}

/**
* Method isValid.
*
* @return true if this object is valid according to the schema
*/
public boolean isValid(
) {
try {
validate();
} catch (org.exolab.castor.xml.ValidationException vex) {
return false;
}
return true;
}

/**
*
*
* @param out
* @throws org.exolab.castor.xml.MarshalException if object is
* null or if any SAXException is thrown during marshaling
* @throws org.exolab.castor.xml.ValidationException if this
* object is an invalid instance according to the schema
*/
public void marshal(
final java.io.Writer out)
throws org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
Marshaller.marshal(this, out);
}

/**
*
*
* @param handler
* @throws java.io.IOException if an IOException occurs during
* marshaling
* @throws org.exolab.castor.xml.ValidationException if this
* object is an invalid instance according to the schema
* @throws org.exolab.castor.xml.MarshalException if object is
* null or if any SAXException is thrown during marshaling
*/
public void marshal(
final org.xml.sax.ContentHandler handler)
throws java.io.IOException, org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
Marshaller.marshal(this, handler);
}

/**
*/
public void removeAllBook(
) {
this._bookList.clear();
}

/**
* Method removeBook.
*
* @param vBook
* @return true if the object was removed from the collection.
*/
public boolean removeBook(
final castor.Book vBook) {
boolean removed = _bookList.remove(vBook);
return removed;
}

/**
* Method removeBookAt.
*
* @param index
* @return the element removed from the collection
*/
public castor.Book removeBookAt(
final int index) {
java.lang.Object obj = this._bookList.remove(index);
return (castor.Book) obj;
}

/**
*
*
* @param index
* @param vBook
* @throws java.lang.IndexOutOfBoundsException if the index
* given is outside the bounds of the collection
*/
public void setBook(
final int index,
final castor.Book vBook)
throws java.lang.IndexOutOfBoundsException {
// check bounds for index
if (index < 0 || index >= this._bookList.size()) {
throw new IndexOutOfBoundsException("setBook: Index value '" + index + "' not in range [0.." + (this._bookList.size() - 1) + "]");
}

this._bookList.set(index, vBook);
}

/**
*
*
* @param vBookArray
*/
public void setBook(
final castor.Book[] vBookArray) {
//-- copy array
_bookList.clear();

for (int i = 0; i < vBookArray.length; i++) {
this._bookList.add(vBookArray[i]);
}
}

/**
* Method unmarshal.
*
* @param reader
* @throws org.exolab.castor.xml.MarshalException if object is
* null or if any SAXException is thrown during marshaling
* @throws org.exolab.castor.xml.ValidationException if this
* object is an invalid instance according to the schema
* @return the unmarshaled castor.BookList
*/
public static castor.BookList unmarshal(
final java.io.Reader reader)
throws org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
return (castor.BookList) Unmarshaller.unmarshal(castor.BookList.class, reader);
}

/**
*
*
* @throws org.exolab.castor.xml.ValidationException if this
* object is an invalid instance according to the schema
*/
public void validate(
)
throws org.exolab.castor.xml.ValidationException {
org.exolab.castor.xml.Validator validator = new org.exolab.castor.xml.Validator();
validator.validate(this);
}

}

こんな感じで自分でソースファイルを作成しなくてもいいためより楽になった。
使用した上での知った点

1.スキーマの定義で要素の中に子要素を使用する場合は


を使用しなければならないこと。
sequenceは順番を定義するだけなので不必要かと思っていたが、Castorでエラーとなった。
sequenceを定義したら問題なし。

2.Castorから自動生成されたクラスで


public void setBook(
final castor.Book[] vBookArray) {
//-- copy array
_bookList.clear();

for (int i = 0; i < vBookArray.length; i++) {
this._bookList.add(vBookArray[i]);
}
}

という部分がある、
みてのとおり既存のデータを一度クリアしてから新しいデータを設定し直している。
よって、同じXMLにAとBの箇所から同時アクセスがあった場合に
Aで更新しようとする時にこの部分で一度データが消えてしまう。
内部データだけの処理かと思っていたが、どうやらマーシャルが行われるクラスの内部データ(この場合bookList)に変更が行われると
XMLの方も変更されてしまうようだ。

つまり
Bで読み込みしようとしたときに、ちょうど別な処理で書き込みが行われようとしてこのClear処理が行われた直後の場合、
Bで読み込みしたときにデータが消えてしまう場合がある
よって、


public void setBook(
final castor.Book[] vBookArray) {
//-- copy array
//_bookList.clear();
Vector tmpArray = new Vector();
for (int i = 0; i < vBookArray.length; i++) {
tmpArray.add(vBookArray[i]);
}
_bookList = tmpArray;
}

というように一時データに変更を加えて実データには参照を変えるようにした方が良い。
あとはアンマーシャルのメソッドを synchronized にして置かないといけない。

以上の点を注意しよう。
自動生成されたものだからといって問題がないと思っていると痛い目にあう。
(それで痛い目にあった人orz)
自動生成されたクラスも一通り目を通しましょう
すみません。確認ミスです。問題なかった・・・ぽい?
ちょっと再度確認してみないと・・・。

3/04 追記
うーん。データの一部が消えるときがたまにある。って自動生成されたクラスをそのまま使っている人に仕事でいわれたが、
てっきり上の原因かと思ったがそうでもないみたいだなぁ。
そもそもClearしているのは書込み時に使用する内部データなのでそれを変えてもアンマーシャル(読込み)には、
関係ないはずだし。実際にXMLが変更されていなければアンマーシャルには影響はないよなぁ。

データが消えたっていうのはマルチスレッドなどでパラでXMLを操作するクラスが起動された時に
まさにマーシャルが実行されようとする直前に別のスレッドでデータ変更処理(上だとsetBook)を呼ばれたって感じなのかなぁ。
うーん。ちょっと今度会社でどういったふうに使用されているか確認しないと。