先に「
ASP.NET Web API - シリアライズ結果が必ず JSON になってしまう」という題で投稿したが、それと関連した話題。
単一のサーバー側実装で、JSON 形式と XML 形式のいずれかの応答形式を、クライアント側でお好みで指定できて便利な ASP.NET Web API。
Web API についての説明は
以前の記事に譲るとして、いくつか ASP.NET Web API 実装を書いていて気づいたことがもうひとつ。
JSON 形式での応答を要求(Accept 要求ヘッダに "application/json" を指定)すると、期待どおりの結果が返るのだが、XML 形式での応答を要求 (Accept 要求ヘッダに "application/xml" を指定)すると、プロパティがシリアル化されない場合があったのだ。
setter が非 public だとNG!
いくつか試行錯誤を重ねた結果、setter が public でないプロパティがある場合にこの現象が発生するらしいことを突き止めた。
たとえば、下記のような POCO を返す Web API メソッドの場合である。
public class MyPOCO {
public string Foo { get; protected set; }
}public プロパティ "Foo" の setter は protected なので、クラス外からこのプロパティに値を代入することができないようになっている。
さて実はこのままでは、JSON 形式での応答を要求しても、プロパティ "Foo" はシリアル化されない。

System.Runtime.Serialization アセンブリに収録されている、DataContract 属性、DataMember 属性を付けてやることで、プロパティ "Foo" もシリアル化されるようになる。
[DataContract]
public class MyPOCO {
[DataMember]
public string Foo { get; protected set; }
}

これで一安心...
と思いきや、Accept 要求ヘッダに "application/xml" を指定して、XML 形式での応答を求めると、たしかに HTTP 200 で XML な書式の応答が返ってくるのだが...

なんと、プロパティ "Foo" が含まれていない。
まとめ
これもやはり、.NET Framework 標準の XML シリアル化の仕組みを使うことに起因する制約なのだろう。
最近はなるべく不変値な型を書こうというクセがあるため、非public なsetterを持つ public プロパティを実装することが少なからずあり、はまってしまった。
もちろん、データ転送オブジェクトとしての用途を考えると、XML にシリアル化して通信経路を転送後、逆シリアル化することになる。
そこへもって、public な setter がないプロパティには普通には代入はできないことから、そういったプロパティは XML シリアル化の対象外となるのも頷けるところではある。
とはいえ、JSON 形式と XML 形式とで、こんな形でひっそりと応答の内容に差異が発生しうるのは、危険な気もする。
ということで、JSON形式の応答にも XML 形式の応答にも正しく返信できるようにするには、この点、用心しておいたほうがいいかもしれない。
おまけ
ちなみに、XML シリアル化の手続きは、実は IXmlSerializable インタフェースを実装することで、存分に制御可能である。
実際、この投稿で取り上げたような非public な setter を持つプロパティを、XML にシリアル化することが可能であることも実験済みである。
しかし今のところ、そのようなカスタム実装した POCO では、今度は JSON 形式での応答を返せないといった状況を確認している。
まだ現時点では、自分はこの状況を打破できていない。
以上、念のためご参考まで。