23

DynamicObjectでクラスをシリアライズしようとしましたBinaryFormatterが、

  • 出力ファイルが大きすぎるため、ワイヤに適していません
  • 循環参照が処理されない (シリアル化中にスタックする)

手段をシリアライズするDynamicObjectこと自体はほとんどないので、シリアライズしようとしたクラスは次のとおりです。

[Serializable()]
class Entity
    : DynamicObject, ISerializable
{

    IDictionary<string, object> values = new Dictionary<string, object>();

    public Entity()
    {

    }

    protected Entity(SerializationInfo info, StreamingContext ctx)
    {
        string fieldName = string.Empty;
        object fieldValue = null;

        foreach (var field in info)
        {
            fieldName = field.Name;
            fieldValue = field.Value;

            if (string.IsNullOrWhiteSpace(fieldName))
                continue;

            if (fieldValue == null)
                continue;

            this.values.Add(fieldName, fieldValue);
        }

    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        this.values.TryGetValue(binder.Name, out result);

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        this.values[binder.Name] = value;

        return true;
    }        

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {            
        foreach (var kvp in this.values)
        {
            info.AddValue(kvp.Key, kvp.Value);                 
        }
    }

}

(ExpandoObject を使用することもできたと思いますが、それは別の話です。)

簡単なテスト プログラムを次に示します。

    static void Main(string[] args)
    {
        BinaryFormatter binFmt = new BinaryFormatter();

        dynamic obj = new Entity();
        dynamic subObj = new Entity();
        dynamic obj2 = null;

        obj.Value = 100;
        obj.Dictionary = new Dictionary<string, int>() { { "la la la", 1000 } };

        subObj.Value = 200;
        subObj.Name = "SubObject";

        obj.Child = subObj;

        using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate))
        {
            binFmt.Serialize(stream, obj);                
        }

        using (var stream = new FileStream("test.txt", FileMode.Open))
        {
            try
            {
                obj2 = binFmt.Deserialize(stream);                    
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }                
        }

        Console.ReadLine();

    }

いくつかのブレークポイントをあちこちに配置すると、obj2 の内容を見ることができ、元のデータが正しく逆シリアル化されているように見えますが、想像力を働かせてデータを移動すると、上記の欠点があります。

私は Marc Gravell の protobuf-net を見ましたが、そのようなコンテキストでそれを使用する方法がよくわかりません (リポジトリから正しいバージョンを選択したかどうかさえわかりませんが、ねえ)。

言葉というよりはコードだとは思いますが、シナリオをこれ以上うまく説明できるとは思いません。この質問をより明確にするために追加できるものがあるかどうか教えてください。

どんな助けでも大歓迎です。

4

5 に答える 5

13

このシーケンスが動的オブジェクトで機能することは 98% 確信しています。

  1. オブジェクトを Expando オブジェクトに変換する
  2. expando オブジェクトを Dictionary 型にキャストする
  3. 通常どおり、ProtoBuf-net Serializer.Serialize / .Deserialize を使用します。
  4. 辞書を Expando オブジェクトに変換する

転送のために、オブジェクトを名前と値のペアのコレクションに変換できます。

これは dynamic でできることのほんの一部ですが、おそらくそれで十分です。

上記の変換の一部を処理するためのカスタム コードがいくつかあります。興味があればお見せします。

dynamic がクラスのプレースホルダーである場合の解決策はありません。この場合、タイプを取得し、switch ステートメントを使用して、必要に応じてシリアル化/逆シリアル化することをお勧めします。この最後のケースでは、必要な一般的なデシリアライゼーションのタイプ (文字列 / id / 完全修飾型名 / など) を示す何かを配置する必要があります。想定されるタイプのリストを扱っていると仮定します。

注: Expando は IDictionary を実装しています。Expando は、単なるキーと値のペアのリストです。すなわち。あなたが点を打ったものがキーであり、値はそれを実装する関数のチェーンからの戻り値です。シンタックス シュガー エクスペリエンスをカスタマイズするための一連の動的インターフェイスがありますが、ほとんどの場合、それらを確認することはありません。

参照:

于 2011-07-31T16:01:32.867 に答える
11

あなたのシナリオで JSON が受け入れられるかどうかはわかりませんが、受け入れられる場合は、動的型をシリアル化するためにJson.net ( http://json.codeplex.com ) を使用しました。非常にうまく機能し、高速で、出力のサイズが小さくなります。Json.net は動的オブジェクトを直接返すわけではありませんが、Json.Net の逆シリアル化された出力を任意の動的型に変換するのは非常に簡単です。以下の例では、動的型として ExpandoObject を使用しています。以下のコードは、私が Facebook Graph Toolkit で使用したものです。元のソースについては、次のリンクを参照してください: http://facebookgraphtoolkit.codeplex.com/SourceControl/changeset/view/48442#904504

public static dynamic Convert(string s) {
            object obj = Newtonsoft.Json.JsonConvert.DeserializeObject(s);
            if (obj is string) {
                return obj as string;
            } else {
                return ConvertJson((JToken)obj);
            }
    }

    private static dynamic ConvertJson(JToken token) {
        // FROM : http://blog.petegoo.com/archive/2009/10/27/using-json.net-to-eval-json-into-a-dynamic-variable-in.aspx
        // Ideally in the future Json.Net will support dynamic and this can be eliminated.
        if (token is JValue) {
            return ((JValue)token).Value;
        } else if (token is JObject) {
            ExpandoObject expando = new ExpandoObject();
            (from childToken in ((JToken)token) where childToken is JProperty select childToken as JProperty).ToList().ForEach(property => {
                ((IDictionary<string, object>)expando).Add(property.Name, ConvertJson(property.Value));
            });
            return expando;
        } else if (token is JArray) {
            List<ExpandoObject> items = new List<ExpandoObject>();
            foreach (JToken arrayItem in ((JArray)token)) {
                items.Add(ConvertJson(arrayItem));
            }
            return items;
        }
        throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token");
    }
于 2010-06-28T13:40:43.427 に答える
1

まず、ファイルのサイズは次の 2 つの要素に依存します (BinaryFormatter の動作を理解していれば、間違っている場合は修正してください)。

  1. シリアル化される実際の値のサイズ、および
  2. メソッドでオブジェクトの値をシリアル化SerializationInfo.AddValueするために使用される名前。出力ファイルに保存されるため、逆シリアル化中に同じ名前で値を使用できます。

明らかに、#1 は最大の速度低下を引き起こします。これは、シリアライズしようとしているオブジェクトを最適化することによってのみ軽減できます。

動的オブジェクトを使用しているため、#2 によるほとんど目立たない小さなサイズの増加は避けられません。オブジェクトのメンバーの型と名前を前もって知っていれば、オブジェクトの各メンバーに非常に短く、順番に決定される名前 (「1」、「2」、「3」など) を反復処理として付けることができます。オブジェクトのメンバーを介して追加しますSerializationInfo.AddValue。次に、逆シリアル化中に、同じ順次決定された名前で使用でき、SerializationInfo.GetValue逆シリアル化される値の実際の名前に関係なく、オブジェクトのメンバーを追加されたのと同じ順序で繰り返し処理する限り、逆シリアル化は正常に機能します。確かに、これで節約できるのはメンバーあたり平均 4 ~ 5 バイトだけかもしれませんが、これらのわずかな量が大きなオブジェクトに加算される可能性があります。

@Raine:(ExpandoObjectを使用できたと思いますが、それは別の話です。)

そうではありません。クラスExpandoObjectの代わりに使用するようにコードサンプルを変更しましたが、私に投げつけられました。がマークされておらず、逆シリアル化またはシリアル化するための適切なコンストラクターがありません。ただし、これは、必要に応じて ExpandoObject を使用できないという意味ではありませ。したがって、インスタンスはインスタンスのコレクションでありシリアライズ可能とマークされています。そのため、ExpandoObject をシリアル化できますが、それを as としてキャストし、それぞれを個別にシリアル化する必要があります。ただし、元のコード サンプルを最適化するという点では、これは無意味です。同じくらい多くのファイル スペースが必要になるからです。EntitySerializationExceptionExpandoObjectSerializableAttributeIDictionary<string, object>ICollection<KeyValuePair<string, object>>ExpandoObjectKeyValuePair<string, object>ICollection<KeyValuePair<string, object>>KeyValuePair<string, object>

要約すると、動的オブジェクトのシリアル化を最適化できる方法はないと思います-オブジェクトがシリアル化されるたびにオブジェクトのメンバーをループする必要があり、オブジェクトのサイズを事前に知る方法はありません(動的の定義による) .

于 2011-04-25T00:54:59.880 に答える
0

SharpSerializer が動的オブジェクトをサポートしているかどうかはわかりませんが、試してみる価値があるかもしれません:

http://www.sharpserializer.com/en/index.html

于 2011-04-13T23:23:01.147 に答える