none
.NET CoreでFrameworkのNetDataContractSerializerの代替案 RRS feed

  • 質問

  • 現在.NET Framework4.7で匿名型(例、internal sealed class <>f__AnonymousType0<<ControlsUsedInApp>j__TPar>)、キャプチャ変数(private sealed class <>c__DisplayClass12_1)、式木(Expression)をシリアライズできるように以下のシリアライザを使っています。

    new NetDataContractSerializer(new StreamingContext(),Int32.MaxValue,false,FormatterAssemblyStyle.Full,new ExpressionSurrogateSelector());

    独自のExpressionSurrogateSelectorサロゲータを使うことで匿名型とキャプチャ変数と式木をシリアライズデシリアライズできるようになりました。

    しかし.NET CoreではNetDataContractSerializerが使えないらしく代替としてDataContractSerializerを使うそうです。サロゲーターとして

        class Program {
            class MyCustomerResolver:DataContractResolver {
                public override Boolean TryResolveType(Type dataContractType,Type declaredType,DataContractResolver knownTypeResolver,out XmlDictionaryString typeName,out XmlDictionaryString typeNamespace) {
                    typeName=null;
                    typeNamespace=null;
                    return true;
                }
                public override Type ResolveName(String typeName,String typeNamespace,Type declaredType,DataContractResolver knownTypeResolver) {
                    return null;
                }
            }
            static void Main() {
                var 匿名 = new { a=1,b="b" };
                using(var f = new FileStream("A",FileMode.Create,FileAccess.ReadWrite)) {
                    var serializer=new DataContractSerializer(匿名.GetType(),new DataContractSerializerSettings{DataContractResolver = new MyCustomerResolver() });
                    serializer.WriteObject(f,匿名);//ここでエラー
                }
            }
    

    これでシリアライズできるかなと実行してみると「//ここでエラー」の場所で

    System.Runtime.Serialization.InvalidDataContractException: 'Type '<>f__AnonymousType0`2[System.Int32,System.String]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.'

    とでます。

    MyCustomerResolver.TryResolveType,MyCustomerResolver.ResolveName内部に設定したブレークポイントで止まらないので実行していないようです。

    知りたいことは

    1. DataContractSerializerでNetDataContractSerializerの完全な代替になるか?
    2. NetDataContractSerializerは[Serializable]の型がシリアライズ対象になり[NoSerialize]フィールドはシリアライズされない、というルールを同じにできるか?出来ないなら代替方法は?
    3. NetDataContractSerializerはSurrogeteSelectorでSurrogateを指定出来て通常シリアライズできないオブジェクト(Expressionとか)をシリアライズできる。それと同じようなことがDataContractSerializerでできるか?
    4. NuGetでよりよいシリアライザがないか?求める機能は匿名型、キャプチャ型、Expressionをシリアライズできること。

    よろしくお願いします。


    2020年10月24日 16:24

回答

  • 回答ではありません。根本的にアプローチが間違っています。

    まず、匿名型ですが

    フィールド、プロパティ、イベント、またはメソッドの戻り値の型は、匿名型を持つものとして宣言できません。 同様に、メソッドの仮パラメーター、プロパティ、コンストラクター、またはインデクサーも、匿名型を持つものとして宣言できません。 メソッドの引数として匿名型または匿名型を含むコレクションを渡すために、パラメーターを型オブジェクトとして宣言できます。 ただし、これにより厳密な型指定が無効になります。 クエリ結果をメソッドの境界を越えて格納したり渡したりする必要がある場合、匿名型の代わりに、通常の名前の構造体またはクラスの使用を検討してください。

    と説明されています。シリアライズする(=メソッド境界を超える)ことは根本的に間違っており、匿名型の設計思想と相容れません。

    また、DataContractSerializerとNetDataContractSerializerの違いについては

    NetDataContractSerializer は、1 つの重要な点で DataContractSerializer とは異なります。NetDataContractSerializer はシリアル化された XML の中に CLR 型情報を含みますが、DataContractSerializer にはこの情報は含まれません。

    と説明されています。逆に言えば、型情報を持たないためDataContractSerializerはNetDataContractSerializerの代替たり得ません。
    これには歴史的経緯があります。.NET Frameworkには従来よりBinaryFormatterというシリアライザーが存在していました。しかし、BinaryFormatterはバイナリを直接再現することを目的としていたため、シリアライザーを介してプログラムが密結合になってしまいます。それを解消する目的で新たに開発されたのがDataContractSerializerとなります。
    その上で、.NET CoreでBinaryFormatterとNetDataContractSerializerが廃止されるのは、シリアライザーを介しての密結合を避けるべきというより強いメッセージと考えるべきです。

    匿名型の思想及びNetDataContractSerializerの廃止を踏まえて、より抽象度の高いデータ型を用意し、それをシリアライズすることをお勧めします。

    なお、DataContractSerializer と DataContractResolver を使用した NetDataContractSerializer 機能の提供というドキュメントを見つけました。これによりDatContractSerializerで代替できるかはちょっとわかりませんが紹介しておきます。

    • 回答としてマーク 和和和 2020年10月29日 9:26
    2020年10月24日 22:33

すべての返信

  • Core のバージョンは何でしょう? (2.x 以前と 3.x 以降ではシリアライザが違うと言うことを聞いたことがあります) 
    2020年10月24日 22:21
  • 回答ではありません。根本的にアプローチが間違っています。

    まず、匿名型ですが

    フィールド、プロパティ、イベント、またはメソッドの戻り値の型は、匿名型を持つものとして宣言できません。 同様に、メソッドの仮パラメーター、プロパティ、コンストラクター、またはインデクサーも、匿名型を持つものとして宣言できません。 メソッドの引数として匿名型または匿名型を含むコレクションを渡すために、パラメーターを型オブジェクトとして宣言できます。 ただし、これにより厳密な型指定が無効になります。 クエリ結果をメソッドの境界を越えて格納したり渡したりする必要がある場合、匿名型の代わりに、通常の名前の構造体またはクラスの使用を検討してください。

    と説明されています。シリアライズする(=メソッド境界を超える)ことは根本的に間違っており、匿名型の設計思想と相容れません。

    また、DataContractSerializerとNetDataContractSerializerの違いについては

    NetDataContractSerializer は、1 つの重要な点で DataContractSerializer とは異なります。NetDataContractSerializer はシリアル化された XML の中に CLR 型情報を含みますが、DataContractSerializer にはこの情報は含まれません。

    と説明されています。逆に言えば、型情報を持たないためDataContractSerializerはNetDataContractSerializerの代替たり得ません。
    これには歴史的経緯があります。.NET Frameworkには従来よりBinaryFormatterというシリアライザーが存在していました。しかし、BinaryFormatterはバイナリを直接再現することを目的としていたため、シリアライザーを介してプログラムが密結合になってしまいます。それを解消する目的で新たに開発されたのがDataContractSerializerとなります。
    その上で、.NET CoreでBinaryFormatterとNetDataContractSerializerが廃止されるのは、シリアライザーを介しての密結合を避けるべきというより強いメッセージと考えるべきです。

    匿名型の思想及びNetDataContractSerializerの廃止を踏まえて、より抽象度の高いデータ型を用意し、それをシリアライズすることをお勧めします。

    なお、DataContractSerializer と DataContractResolver を使用した NetDataContractSerializer 機能の提供というドキュメントを見つけました。これによりDatContractSerializerで代替できるかはちょっとわかりませんが紹介しておきます。

    • 回答としてマーク 和和和 2020年10月29日 9:26
    2020年10月24日 22:33
  • 現在の実装では同じアセンブリを共有する前提でシリアライズする密結合なので.NET Coreでは置換できないと理解しました。

    ある程度試行錯誤して今のところ置き換えは不可能と考えています。

    将来.NET Core用にシリアライザを実装するかNuGetで公開されるのを待つかします。

    教えていただいたドキュメントには

    public override void ResolveType(Type dataContractType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    というシグネチャのメソッドがありますがこれは実際には存在しないようです。

    2020年10月25日 9:22
  • 3.1を対象にしています。将来的には.NET Coreの後継である.NET5を考えると.NET Frameworkのシリアライズから脱却すべきと考えているため代替案を探しています。
    2020年10月25日 14:50
  • もう少し調査した結果.NET CoreではISerializationSurrogateProviderが代替手段になりえるようです。

    シリアライズしたい匿名型,Expressionなどを安全にシリアライズできるTypeを定義して起きそこに変換するプロキシのような仕組みのようです。

    2020年10月25日 15:07
  • 和和和さん、こんにちは。フォーラムオペレーターのKumoです。
    MSDNフォーラムにご投稿くださいましてありがとうございます。 

    ご質問いただいた件ですが、その後いかがでしょうか。
    より良いサポートのため、「Developer Community」または「Github」にご投稿いただくことをご検討ください。

    お手数ですが、ご協力の程どうかよろしくお願いいたします。 


    MSDN/ TechNet Community Support Kumo ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2020年10月28日 2:40
    モデレータ