3

シリアル化/逆シリアル化のアプリケーションでprotobuf-netを使用しています。私は問題に直面しています。

[ProtoContract()]
ClsTest
{
    private bool _isPeriodic

    [ProtoMember(1)]
    public bool IsPeriodic
    {
        get
        {
             return _isPeriodic;
        }

        set
        {
            isPeriodic = value;
        }
   }

}

このクラスをコレクションオブジェクトで使用しています。

シリアル化プロセスは正常に機能しますが、逆シリアル化後、プロパティIsPeriodicの値はデフォルトでtrueになりますが、場合によってはfalseになります。誰か助けてもらえますか?

4

4 に答える 4

7

私の推測では、コードはデフォルトのインスタンス用に設定IsPeriodicされていると思います。おそらく次のようになります。truenew

private bool _isPeriodic = true;

または、コンストラクターには次のものがあります。

_isPeriodic = true; // or IsPeriodic = true

基本的に、(protobuf言語ガイドに従って)暗黙のデフォルトがあります。これにより、boolのデフォルトは。と見なされますfalse。デフォルトと思われるデータは送信されません。このデフォルトが正しくない場合は、次のように伝えてください。

[ProtoMember(1), DefaultValue(true)]

またはIIRCの代わりに、次のように設定IsRequiredしてみてください。true

[ProtoMember(1, IsRequired = true)]

常に値を送信するように指示する方法は他にもいくつかあります。

private bool ShouldSerializeIsPeriodic() { return true;}

( 、、、などのコア.NETPropertyGridでサポートされているのと同じパターンを使用します-ランダムなパターンを発明したのは私ではありません)XmlSerializerPropertyDescriptor

「v2」では、この奇妙な点を取り除くために、さらに2つの変更を加えたことに注意してください。

  • オプションでコンストラクターをバイパスできます(WCFスタイル)
  • 新しいメタモデルは、デフォルトを想定するかどうかを決定する別の方法を提供します
于 2010-07-01T22:52:09.677 に答える
1

protobuf-net で bool 型と enum 型を操作すると、いくつかのトリックがあることがわかりました。bool 型と enum 型の両方のデフォルト値に関する最初の問題: これが私の Linq スニペット コードです。

[ProtoContract]
public class MyOption
{
    [ProtoMember(2)]
    public View m_printListView = View.Details;   (A)
    [ProtoMember(5) ]
    public bool m_bool = true ;                   (B)
}

void Main()
{
    string fname = @"D:/test.dat";
    if (File.Exists(fname) )
    {
        File.Delete(fname);
    }
    using(FileStream fs=  new FileStream(fname, FileMode.OpenOrCreate, FileAccess.Write) )
    {
        MyOption opt = new MyOption();
        opt.m_printListView = View.LargeIcon; // (1)
        opt.m_bool = false;                   // (2)

        Serializer.SerializeWithLengthPrefix(fs, opt, PrefixStyle.Fixed32);
    }
    using(FileStream fs=  new FileStream(@"D:/test.dat", FileMode.Open, FileAccess.Read) )
    {
        MyOption opt;
        opt = Serializer.DeserializeWithLengthPrefix<MyOption>(fs, PrefixStyle.Fixed32);
        Console.WriteLine(opt.m_printListView);
        Console.WriteLine(opt.m_bool);
    }
}

さて、出力を推測してください。これは:

Details
True

(A) と (B) では、デフォルト値を View.Details と true に設定していることに注意してください。(1) と (2) では、値を View.LargeIcon と false に明示的に設定しました。proto-buf のシリアル化と逆シリアル化の後、間違った値を取得しました。

その理由は、bool 値の場合、デフォルト値は false であり、proto-buf の設計原則に従って、可能な場合はスペースを節約するため、デフォルト値はファイルに保存されず、デフォルトを使用する必要があることを示すフラグのみが保存されます。値(私は推測しますが、検証されていません)。

逆シリアル化では、最初に既定のコンストラクターが呼び出され、行 (B) が実際には CLR 実行時の既定のコンストラクターの一部である場合、proto-buf の逆シリアル化プロセスが開始され、メンバー m_bool に既定値フラグがあることがわかります。 、次にデフォルト値 false を使用して m_bool を設定します。これにより、(B) のデフォルト値が上書きされます。

列挙型の場合、理由は同様です。上記の例では、View.LargeIcon がデフォルト値であり、その数値は 0 です (Reflected によって検証されています)。

bool および enum メンバーの DefaultValueAttribute を使用して修正するには、次のようにします。

[ProtoContract]
public class MyOption
{
    [ProtoMember(2), DefaultValue(View.Details)]
    public View m_printListView = View.Details;   (A)
    [ProtoMember(5), DefaultValue(true) ]
    public bool m_bool = true ;                   (B)
}

列挙型の場合、他にも問題があります。最初の 1 つは、すべての列挙子が個別の値を持つ必要があることです。そうしないと、シリアライズ時に proto-buf が例外をスローします。たとえば、System.Drawing.RotateFlip 列挙型には次の定義があります。

    public enum RotateFlipType
{
    Rotate180FlipNone = 2,
    Rotate180FlipX = 6,
    Rotate180FlipXY = 0,
    Rotate180FlipY = 4,
    Rotate270FlipNone = 3,
    Rotate270FlipX = 7,
    Rotate270FlipXY = 1,
    Rotate270FlipY = 5,
    Rotate90FlipNone = 1,
    Rotate90FlipX = 5,
    Rotate90FlipXY = 3,
    Rotate90FlipY = 7,
    RotateNoneFlipNone = 0,
    RotateNoneFlipX = 4,
    RotateNoneFlipXY = 2,
    RotateNoneFlipY = 6
}

画像処理の観点から、RotateNoneFlipNone と Rotate180FlipXY は同じ効果を持つため、基本的な値は同じです。これは合理的な設計ですが、そのような列挙型は proto-buf では機能しません。

    The enum System.Drawing.RotateFlipType has conflicting values RotateNoneFlipNone and RotateNoneFlipNone
Serializer.ThrowInner (Exception exception)
  at ProtoBuf.Serializer.ThrowInner(Exception exception)
  at ProtoBuf.Serializer.Serialize[T](Stream destination, T instance)
  at ProtoBuf.Serializer.SerializeWithLengthPrefix[T](Stream destination, T instance, PrefixStyle style, Int32 tag)

私の回避策は、独自の列挙型を作成し、My_RotateFlipType と System.Drawing.RotateFlipType の間で 1 対 1 のマッピングを使用することです。My_RotateFlipType のみが proto-buf によってシリアル化されます。

public enum RotateFlipType              public enum My_RotateFlipType
{                                       {
    Rotate180FlipNone = 2,                  Rotate180FlipNone,
    Rotate180FlipX = 6,                     Rotate180FlipX,
    Rotate180FlipXY = 0,                    Rotate180FlipXY,
    Rotate180FlipY = 4,                     Rotate180FlipY,
    Rotate270FlipNone = 3,                  Rotate270FlipNone,
    Rotate270FlipX = 7,                     Rotate270FlipX,
    Rotate270FlipXY = 1,                    Rotate270FlipXY,
    Rotate270FlipY = 5,                     Rotate270FlipY,
    Rotate90FlipNone = 1,                   Rotate90FlipNone,
    Rotate90FlipX = 5,                      Rotate90FlipX,
    Rotate90FlipXY = 3,                     Rotate90FlipXY,
    Rotate90FlipY = 7,                      Rotate90FlipY,
    RotateNoneFlipNone = 0,                 RotateNoneFlipNone,
    RotateNoneFlipX = 4,                    RotateNoneFlipX,
    RotateNoneFlipXY = 2,                   RotateNoneFlipXY,
    RotateNoneFlipY = 6                     RotateNoneFlipY
}                                       }

2 つのデータ メンバーと手動で同期することを避けるために、ProtoBeforeSerialization および OnProtoAfterDeserialization 機能を使用して自動化します。

[ProtoAfterDeserialization()]
public void OnProtoAfterDeserialization()
{
    Console.WriteLine("called OnProtoAfterDeserialization");
    bool ret = Enum.TryParse(m_rotate.ToString(), out m_rotate_protobuf);
}

[ProtoBeforeSerialization()]
public void OnProtoBeforeSerialization()
{
    Console.WriteLine("called OnProtoBeforeSerialization");
    bool ret = Enum.TryParse(m_rotate_protobuf.ToString(), out m_rotate);
}

列挙型に関する 2 番目の問題は、値が 0 の列挙子です。列挙型に値 0 の列挙子がない場合、実行時に protobuf が例外をスローするのは非常に簡単です。

protobuf-net を使用するときは、次のルールに従います。 1. デフォルト コンストラクターがデフォルト値以外の値を設定する場合は常に、DefaultValueAttribute を使用します。2. システムまたはサードパーティの列挙型の場合、protobuf に追加する前に、リフレクター (静的) または linq (ランタイム) で上記の問題があるかどうかを確認します。競合する場合は、上記の回避策を使用してください。

于 2014-05-23T06:13:14.680 に答える
0

以下は私にとってはうまくいきます:

[ProtoContract]
class ClsTest
{
    [ProtoMember(1)]
    public bool IsPeriodic { get; set; }
}

逆シリアル化:

   // stream is a NetworkStream object

   ClsTest clsTestObj = Serializer.DeserializeWithLengthPrefix<ClsTest>(stream, PrefixStyle.Fixed32);
   bool value = clsTestObj.IsPeriodic;
于 2010-07-01T19:07:17.473 に答える