C#:IXmlSerializableなクラスのnamespace
XMLの仕様にネームスペースってのがあって、こいつはxmlns属性を指定するとその子要素全部の名前空間を変更する事ができる。で、変更するときに名前をつける事ができて、たとえばxmlns:html="〜"みたいにしてやれば、以降"html:"とゆうprefixでHTML要素を使えるようになる。
つまり、次のように書いてやれば、htmlはXHTMLの、annotationはオリジナルの要素であることをあらわしているワケ。
<?xml version="1.0" encoding="utf-8"?> <root xmlns="http://d.hatena.ne.jp/lord_hollow" xmlns:html="http://www.w3.org/1999/xhtml"> <html:html>...</html:html> <annotation>...</annotation> </root>
で、だ。C#でIXmlSerializableを実装したクラスを用意すると、究極に単純な例は以下のとおり。
public class Serializable<T> public T Value{get; set;} }
public class Serializable<T> : IXmlSerializable{ public T Value { get; set; } public System.Xml.Schema.XmlSchema GetSchema(){ return null; } public void ReadXml(System.Xml.XmlReader reader){ reader.ReadStartElement(); XmlSerializer ser = new XmlSerializer(typeof(T)); Value = (T)ser.Deserialize(reader); reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer){ XmlSerializer ser = new XmlSerializer(typeof(T)); ser.Serialize(writer, Value); } }
二つコードがあるけれど、上がIXmlSerializableを実装しないパターン、下がするパターン。このクラスの場合だと上で十分なんだけど、そこはほら、サンプルですから下のように実装したとしましょう。
これらのクラスをシリアライズすると、微妙な違いが出てくる。
それは、前者はルート要素(SerializableOfXXX)にxmlns:xsiとxmlns:xsdが付くけど後者には付かない、ってこと。
これの何が不都合かというと、これら二つの属性は子要素に継承されるため、先祖についてなければ必要に応じてつけなきゃならない。.NetFrameworkの場合はstringやらのプリミティブな型には付かないみたいだけどクラスなどには付いてくる。上の例だと、Tがクラス(where T:classってことです)ならば、子要素にxmlnsが付くことになる。
<?xml version="1.0" encoding="utf-8"?> <SerializableOfBaseClass> <BaseClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <BasicValue>VALUE</BasicValue> </BaseClass> </SerializableOfBaseClass>
まぁこんな感じだ。
今、Serializable
だからといって、WriteXmlのところでWriteAttributeStringなどを使ってxmlns:xsi要素を追加しても無駄だった。多分なにかフラグ的な処理が入ってるんだろう。
調べてみると、一応の解決策が。そもそもxsiネームスペースの要素なんか一個も出てこないんだから消してしまえ、というわけで
public void WriteXml(System.Xml.XmlWriter writer){ XmlSerializer ser = new XmlSerializer(typeof(T)); var ns = new XmlSerializerNamespaces(); ns.Add("", ""); ser.Serialize(writer, Value, ns); }
こういう風にしておけば、見た目上は同じになった。けどなんか気持ち悪い。ルートレベルで指定できる方法がなにかあると思うんだけどわからないなぁ・・・。