开发者

How to serialize multiple members of "this"

开发者 https://www.devze.com 2023-03-14 19:48 出处:网络
I have a class with a couple of members I want to serialise to store state. However, I want to serialise from WITHIN the class itself, not via some external class feeding it to a formatter.

I have a class with a couple of members I want to serialise to store state.

However, I want to serialise from WITHIN the class itself, not via some external class feeding it to a formatter.

So in theory I want to do something like:

[DataContract]
class MyClass
{
    [DataMember]
    private MyCompoundClass _someCompoundField;

    [DataMember]
    private int _someOtherField;

    private void SaveState()
    { 
         using (Stream stream = GetStream())
         {
             DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
             serialiser.WriteObject(stream, this);
         }
    }

    private void LoadState()
    { 
         using (Stream stream = GetStream())
         {
             DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
             this = (MyClass)serialiser.ReadObject(stream);
         }
    }
}

Now obviously the line

 this = (MyClass)serialiser.ReadObject(stream);

is nonsense, but you can see what I'm trying to do. I want to serialise the two fields of my class from within the class. (I am using the WCF ser开发者_运维问答ializer, but I assume this will be the same if I use XmlSerializer).

I tried to implement this properly by serialising each field myself like so:

private void SaveState()
{ 
    using (Stream stream = GetStream())
    {
        //serialise field 1
        DataContractSerializer serialiser = new DataContractSerializer(typeof(MyCompoundClass));
        serialiser.WriteObject(stream, _someCompoundField);

        //serialise field 2
        serialiser = new DataContractSerializer(typeof(int));
        serialiser.WriteObject(stream, _someOtherField);
    }
}

Now this works as a save, but when I come to read the document back in it throws an exception since there are two root nodes in the XML file.

How do I create my "wrapper" node to wrap my fields. Or is there some other way I should be doing this?

Many thanks,


I haven't seen any built-in deserialisation methods which modify an existing object, rather than returning a new one. Your options as I see them are:

  1. Deserialise a new MyClass and copy the members over
  2. Make LoadState static and have it return the deserialised MyClass
  3. Use a different serialisation mechanism which can do what you want


you could at your LoadState do:

    private void LoadState()
    { 
         using (Stream stream = GetStream())
         {
             DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
             MyClass deserialized = (MyClass)serialiser.ReadObject(stream);

             this._someCompoundField = deserialized._someCompoundField;
             this._someOtherField = deserialized._someOtherField;
         }
    }


Are you tied to a specific serializer? protobuf-net supports that use-case, for example:

[DataContract]
class MyClass
{
    [DataMember(Order = 1)]
    private MyCompoundClass _someCompoundField;

    [DataMember(Order = 2)]
    private int _someOtherField;

    private void SaveState()
    {
        using (Stream stream = GetStream())
        {
            ProtoBuf.Serializer.Serialize(stream, this);
        }
    }

    private void LoadState()
    {
        using (Stream stream = GetStream())
        {
            ProtoBuf.Serializer.Merge(stream, this);
        }
    }
}

Note the addition of Order = n on the member-attributes; that is because protobuf uses numeric identifiers on fields/properties, and needs a way to choose them. You can also use the project-specific [ProtoContract]/[ProtoMember(n)] attributes, but it works with the WCF ones too (as shown).

(Merge is also available on the non-generic 2.0 API - but you pass this in as an argument to Deseroalize instead)


Put your deserialization method in a static method:

class MyClass
{
    public static MyClass LoadState()
    { 
         // Deserialize, and return the new MyClass instance.
    }
}

To serialize just some objects, annotate some fields to avoid serialization, or create a superclass or interface with just the fields in it you want to serialize.

0

精彩评论

暂无评论...
验证码 换一张
取 消