2002年11月14日

_ [comp] Airstation AC アダプタ不良のお知らせ

メルコから突然手紙が来ました。AC アダプタに不良があるので交換するとのこと。1ヶ月〜1年経つと内部のコンデンサが劣化して正常動作しなくなるそうです。通電はしていても動かないと言うことは、どうやら規定の電圧が出せなくなるようですね。ちょっと前に修理に出した Airstation が本体に問題なし、で戻ってきてしまっていたのはこれだったのでしょう。AC アダプタはその時に交換してもらったものだったのですが、これははたしてどうなのか……。一応、無料で申し込めるようなので申し込んでおくことにします。

すらどにタレコんだら、即採用されました。前採用されたときはかなり時間が経ってからだったのに、この違いは重要性の差でしょうか……。ちなみに、この採用でカルマが一定値に行ったらしく、+1 ボーナスがつけられるようになりました。使いませんけど(笑)。

_ [C#] System.Xml.Serialization.XmlSerializer

感動しました。

XML のデータファイルから木構造を持ったオブジェクト群を生成するにはどうするのが一番綺麗なのか悩んでいて、System.SerializableAttribute から System.Runtime.Serialization.Formatters.Soap.SoapFormatter あたりを見ていたのですが、SOAP の XML はむちゃくちゃ汚いんですよね。やっぱりカスタム Formatter でも書かないといけないかなぁ、と思っていたところで XmlSerializer に出会いました。

public class MyClass1
{
  public MyClass2 someObject;
  public int size;
  public String shape;
  [XmlArrayItem(typeof(MyClass2)), XmlArrayItem(typeof(MyClass3))]
  public MyClass2[] children;
}
public class MyClass2 
{
  public double value;
}
public class MyClass3 : MyClass2
{
}

_ というクラスのとあるインスタンス myobject に対して、以下の4行を書くだけで、その下のファイルが書き出されます。

TextWriter writer = new StreamWriter("MyClass1.xml");
XmlSerializer ser = new XmlSerializer(typeof(MyClass1));
ser.Serialize(writer, myobject);
writer.Close();
<?xml version="1.0" encoding="utf-8"?>
<MyClass1 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <someObject>
    <value>3.14159265</value>
  </someObject>
  <size>5</size>
  <shape>circle</shape>
  <children>
    <MyClass2>
      <value>1</value>
    </MyClass2>
    <MyClass3>
      <value>2</value>
    </MyClass3>
  </children>
</MyClass1>

_ 当然、これは下のコードでオブジェクトを再取得できます。まさにマジカル。

TextReader reader = new StreamReader("MyClass1.xml");
XmlSerializer ser = new XmlSerializer(typeof(MyClass1));
myobject = (MyClass1)ser.Deserialize(reader);
reader.Close();

_ 便利な反面、制限も大きいのですが……。デフォルトコンストラクタが存在するクラスにしか適用できなく、public なフィールド or プロパティのみ保存・復元されます。それとは別に、IEnumerable や ICollection などのインターフェイスを持っていると配列だと見なされてその中身が保存・復元されます。

また、派生型を含む配列を保存したいときなどは、入りうる型を全て XmlArrayItemAttribute 属性で列挙しないといけないのも使いにくいですね。

動作原理としては、要するに復元するときにはリフレクションでクラスを探してきて、デフォルトコンストラクタで生成した後に、public なメンバに値を代入して復元しているだけみたいですが。

それでも、フレームワークでちゃんとサポートされていると非常に簡潔に書けて強力ですよね。

_ [C#] 続 XmlSerializer

上記のように便利な XmlSerializer なのですが、オブジェクトの配列の中に派生型を混じらせるときにはどんな型が入りうるのかをあらかじめ配列への属性として列挙しないといけないのが大問題でした。とくに、僕は動的に拡張されるモジュールによる木構造を考えているので、コンパイル時点で入れられる型が固定されているのは根本的な問題です。

いろいろ悩んだあげく、XmlAttributeOverrides を使ったバージョンの XmlSerializer のコンストラクタを使えばいいことに気づきました。

上のソースの中で、children に XmlArrayItemAttribute 属性を指定するかわりに、XmlSerializer の初期化を以下のようにします。

XmlAttributeOverrides xao = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();
foreach ( Type t in new Type[] { typeof(MyClass2), typeof(MyClass3) } ) 
{
  XmlArrayItemAttribute attr = new XmlArrayItemAttribute(t);
  attrs.XmlArrayItems.Add(attr);
}
xao.Add(typeof(MyClass1), "children", attrs);
XmlSerializer ser = new XmlSerializer(typeof(MyClass1), xao);