XmlSerializer で CDATA セクションを扱うのが面倒だったので、自分なりのまとめ。(ぐぐれば引っかかる内容。)
System.Xml.Serialization 名前空間には、なぜか CDATA セクションのためのカスタム属性が用意されてない・・・
XmlCDataSection のプロパティを用意するだけ。
XmlCDataSection は new XmlDocument().CreateCDataSection("CDATA セクションの中身") で作成できる。
面倒な場合はプロパティで工夫。
※シリアライズ時、CreateCDataSection の引数が null でも <![CDATA[]]> は出力される。
XmlCDataSection の代わりに、IXmlSerializable を継承した CDATA セクション用クラスを自作するのもあり。
[CDATA セクション用クラス]
CDATA セクションにあたるプロパティを、XmlCDataSection を 1 つだけ含む XmlNode[] 型にして、XmlTextAttribute を付加。
そうすると、直接 CDATA セクションを出力できる。
XmlNode[] 型だとデータの設定・取得が面倒なので、別のプロパティ(上記だと Content)を用意する。
ただし、この方法はデシリアライズ時にちょっとした問題があり、CDATA セクションの代わりに普通のテキストノードでもデシリアライズできてしまう。
デシリアライズされたインスタンス(上記だと value[0])は、元が CDATA セクションかテキストノードかに関わらず XmlText になるため、型チェックではじくことができない。
OuterXml や InnerText などのプロパティにも <![CDATA[]]> の文字列が含まれないため、デシリアライズインスタンスからはおそらく判断不可能。
CDATA セクションかテキストノードかどちらでもいい場合は問題ないが、厳密にチェックしたい場合、この方法では難しいかもしれない。
System.Xml.Serialization 名前空間には、なぜか CDATA セクションのためのカスタム属性が用意されてない・・・
CDATA セクションのみを含む要素
[XML]
<root>
<text><![CDATA[<b>cdata</b>]]></text>
</root>
[対応クラス]
[XmlRoot("root")]
public class CDataXml1
{
[XmlElement("text")]
public XmlCDataSection Text { get; set; }
}
XmlCDataSection のプロパティを用意するだけ。
XmlCDataSection は new XmlDocument().CreateCDataSection("CDATA セクションの中身") で作成できる。
面倒な場合はプロパティで工夫。
※シリアライズ時、CreateCDataSection の引数が null でも <![CDATA[]]> は出力される。
XmlCDataSection の代わりに、IXmlSerializable を継承した CDATA セクション用クラスを自作するのもあり。
[CDATA セクション用クラス]
public class CDataSection : IXmlSerializable
{
public CDataSection() { }
public CDataSection(string text) { Text = text; }
public string Text { get; set; }
public virtual XmlSchema GetSchema()
{
return null;
}
public virtual void ReadXml(XmlReader reader)
{
Text = reader.ReadElementString();
}
public virtual void WriteXml(XmlWriter writer)
{
writer.WriteCData(Text);
}
}
CDATA セクションと属性がある要素
[XML]
<root>
<node attr="value"><![CDATA[<b>cdata</b>]]></node>
</root>
[対応クラス]
[XmlRoot("root")]
public class CDataXml3
{
[XmlElement("node")]
public CDataNode Node { get; set; }
}
public class CDataNode
{
[XmlAttribute("attr")]
public string Attr { get; set; }
// データの設定・取得用
[XmlIgnore]
public string Content { get; set; }
// シリアライズ・デシリアライズ用
[XmlText]
public XmlNode[] ContentNode
{
get { return new[] { new XmlDocument().CreateCDataSection(Content) }; }
set
{
if( value == null ){
Content = null;
return;
}
if( value.Length != 1 ) throw new InvalidOperationException();
Content = value[0].Value;
}
}
}
CDATA セクションにあたるプロパティを、XmlCDataSection を 1 つだけ含む XmlNode[] 型にして、XmlTextAttribute を付加。
そうすると、直接 CDATA セクションを出力できる。
XmlNode[] 型だとデータの設定・取得が面倒なので、別のプロパティ(上記だと Content)を用意する。
ただし、この方法はデシリアライズ時にちょっとした問題があり、CDATA セクションの代わりに普通のテキストノードでもデシリアライズできてしまう。
デシリアライズされたインスタンス(上記だと value[0])は、元が CDATA セクションかテキストノードかに関わらず XmlText になるため、型チェックではじくことができない。
OuterXml や InnerText などのプロパティにも <![CDATA[]]> の文字列が含まれないため、デシリアライズインスタンスからはおそらく判断不可能。
CDATA セクションかテキストノードかどちらでもいい場合は問題ないが、厳密にチェックしたい場合、この方法では難しいかもしれない。
0 件のコメント:
コメントを投稿