2009-07-12 10 views
4

Problem:Set Nur auf Deserialisierung

Ich habe eine Klasse, sagen Foo, die eine Id-Eigenschaft implementiert. Foo muss serialisierbar sein. Foo.Id sollte bei der Initialisierung von Foo zu einer neuen GUID initialisiert werden. Foo.Id sollte nach dem Einstellen nicht mehr änderbar sein. Deserialisierung wird versuchen, die Foo.Id festzulegen, also muss sie öffentlich gemacht werden.

Private _Id As String=system.Guid.NewGuid.tostring 
Public Property Id As String 
    Get 
     return _Id 
    End Get 
    Set(ByVal value As String) 
     _Id = value 
    End Set 
End Property 

oder C# ers

private string _Id = system.Guid.NewGuid().ToString(); 
public string Id { 
    get { return _Id; } 
    set { _Id = value; } 
} 

Lösung Gedanken:

Die einzige Lösung scheint eine Laufzeitausnahme zu werfen, wenn Foo.Id Einstellung, aber wird dies dazu führen, ein Problem bei der Deserialisierung. Also, irgendwie müssen wir sicherstellen, dass die Ausnahme nur ausgelöst wird, wenn ein Versuch von Set Foo.Id außerhalb des Serialisierers vorgenommen wird. Irgendeine Art von Flagge oder etwas im Konstruktor?

bearbeiten, Deserialisierung Methode ...

public static Foo DeserializeFromFile(string sFilespec) 
{ 
    Xml.Serialization.XmlSerializer oSerializer = new Xml.Serialization.XmlSerializer(typeof(Foo)); 
    System.IO.FileStream oStream = new System.IO.FileStream(sFilespec, IO.FileMode.Open); 
    Foo oObject = oSerializer.Deserialize(oStream); 
    oStream.Close(); 
    return oObject; 
} 
+0

Welche Art von Serialisierung verwenden Sie und welche Version von .net? –

Antwort

9

Ich bin nicht sicher, ob ich das Problem verstehen, aber Sie können versuchen, die ISerializable Schnittstelle in Ihrer Klasse Implementierung manuell Feinabstimmung der Serialisierung/Deserialisierung Prozesse.

[Serializable] 
public class YourClass : ISerializable 
{  
    private Guid _Id = Guid.NewGuid(); 

    public string Id 
    { 
      get { return _Id; } 
      private set { _Id = value; } 
    } 

    public YourClass() // Normal constructor 
    { 
     // ... 
    } 

    // This constructor is for deserialization only 
    private YourClass(SerializationInfo information, StreamingContext context) 
    { 
     Id = (Guid)information.GetValue("Id", typeof(Guid)); // etc 
    } 

    void ISerializable.GetObjectData(SerializationInfo information, 
     StreamingContext context) 
    { 
     // You serialize stuff like this 
     information.AddValue("Id", Id, typeof(Guid)); 
    } 
} 

Lesen Sie auch für weitere Informationen über die SerializationInfo Klasse bis auf Serialisieren und Deserialisieren die häufigsten Arten.

+0

Aus der Tatsache, dass das OP über Eigenschaften (keine Felder) spricht, nehme ich an, dass sie über XmlSerializer sprechen. Ich denke nicht, dass diese Antwort hilfreich ist. –

+0

@Marc Gravell: Ja, es scheint als hättest du recht. Zu meiner Verteidigung war die Tatsache, dass er über XmlSerializer sprach, nicht offensichtlich, als ich die Antwort postete, er machte diese Bearbeitung erst später. –

0

Zunächst einmal glaube ich nicht, dass Ihr Code kompiliert. Sie müssen nach (Foo) umwandeln. Sie sind auch ein „mit“ Block fehlt:

public static Foo DeserializeFromFile(string sFilespec) 
{ 
     Xml.Serialization.XmlSerializer oSerializer = new Xml.Serialization.XmlSerializer(typeof(Foo)); 
     using (System.IO.FileStream oStream = new System.IO.FileStream(sFilespec, IO.FileMode.Open)) { 
      return (Foo) oSerializer.Deserialize(oStream); 
     } 
} 

Noch wichtiger ist, Sie Pech sind hier, wenn Sie die IXmlSerializable-Schnittstelle implementieren und Ihre eigene Deserialisierung tun. Wenn Sie die Id-Eigenschaft deserialisieren, können Sie das _Id-Feld direkt festlegen, ohne die Eigenschaft zu verwenden.

3

Aus der Beschreibung gehe ich davon aus, dass Sie XmlSerializer verwenden. Tatsache ist, dass XmlSerializer die Granularität dafür fehlt. Es unterstützt keine Rückrufe (die Sie Serialisierung zu erfassen erlauben würde) usw.

Optionen:

  • implementieren IXmlSerializable - viel Arbeit (mit XmlReader/XmlWriter) und leicht etwas falsch zu laufen .. tun sie nicht, es sei denn Sie
  • Verwendung eine andere Serializer haben (wie DataContractSerializer, die auch private Getter/Setter oder Felder unterstützt, unterstützt aber nicht voll xml Kontrolle.) - zum Beispiel see here
  • Verwenden Sie ein separates DTO; d.h.Verwenden Sie Ihre Klasse mit privaten Setter in der App, und eine separate (einfachere) -Klasse (öffentliche Get/Set) für die Serialisierung, vielleicht mit einem impliziten Konvertierungsoperator zwischen ihnen
  • machen das Get/Set öffentlich und keine Sorge darüber

Ich denke, ich würde für die DTO-Option gehen; Es behält die einfache, aber vollständige Formatierungskontrolle über das XML und ist nicht viel Arbeit.

using System; 
using System.Xml.Serialization; 
[XmlType("foo"), XmlRoot("foo")] 
public class FooDto { 
    [XmlAttribute("bar")] 
    public string Bar { get; set; } 

    public static implicit operator Foo(FooDto value) { 
     return value == null ? null : 
      new Foo(value.Bar); 
    } 
    public static implicit operator FooDto(Foo value) { 
     return value == null ? null : 
      new FooDto { Bar = value.Bar }; 
    } 
} 
public class Foo { 
    private readonly string bar; 
    public Foo(string bar) { this.bar = bar; } 
    public string Bar { get { return bar; } } 
} 
static class Program { 
    static void Main() { 
     Foo foo = new Foo("abcdefg"); 
     FooDto dto = foo; 
     new XmlSerializer(dto.GetType()).Serialize(
      Console.Out, dto); 
    } 
} 
+0

lol .. du postest 11 Minuten früher als ich :) Wusste nicht, dass Sie bereits geschrieben haben, während ich meine Antwort redigierte und das Sample testete. Was fuer eine Ueberraschung!! Du hast gewonnen, Marc :) –

0

Die Art wie Drjokepu beschrieben scheint der richtige Weg zu sein. Wenn Sie XmlSerialization aus irgendeinem Grund/Einschränkung/Anforderung verwenden müssen, sollten Sie die IXmlSerializable-Schnittstelle ebenfalls implementieren. Die Implementierung von IXmlSerializable ist jedoch möglicherweise nicht so intuitiv wie die Implementierung von ISerializable.

In diesem Fall können Sie den folgenden Trick als Workaround versuchen :).

  1. Make Foo.Id Eigenschaft schreibgeschützt
  2. ein Container-Objekt erstellen, Boo. Boo enthält nur denselben Typ von Membervariablen, die serialisiert werden sollen. In diesem Beispiel hat Boo eine Id-Eigenschaft mit öffentlichem Setter und Getter. Boo sollte keine Mitgliedsmethoden enthalten, die Foo hat.
  3. Verwenden Sie Boo für XML-Serialisierung und Deserialisierung, und verwenden Sie Foo für die Verarbeitung von Geschäftslogik. Logik.

Siehe folgende Beispielcode:

using System; 
using System.IO; 
using System.Xml.Serialization; 

namespace SerializationSample 
{ 
    class Program 
    { 
     private const string STR_CtempSerilizationxml = @"c:\temp\Serilization.xml"; 
     static void Main(string[] args) 
     { 
      Foo foo = new Foo(Guid.NewGuid().ToString()); 
      Console.WriteLine("Foo.Id = " + foo.Id); 

      SerializeToFile(foo, STR_CtempSerilizationxml); 

      Foo fooDeserialized = DeserializeFromFile(STR_CtempSerilizationxml); 

      Console.WriteLine("Foo.Id = " + fooDeserialized.Id); 

     } 

     private static void SerializeToFile(Foo foo, string sFilespec) 
     { 
      XmlSerializer iSerializer = new XmlSerializer(typeof(Boo)); 
      using (FileStream stream = new FileStream(sFilespec, FileMode.Create)) 
      { 
       iSerializer.Serialize(stream, new Boo(foo)); 
      } 
     } 

     public static Foo DeserializeFromFile(string sFilespec) 
     { 
      XmlSerializer oSerializer = new XmlSerializer(typeof(Boo)); 
      using (System.IO.FileStream oStream = new FileStream(sFilespec, FileMode.Open)) 
      { 
       return new Foo(((Boo)oSerializer.Deserialize(oStream)).Id); 
      } 
     } 
    } 

    public class Foo 
    { 
     private readonly string _Id; 
     public string Id 
     { 
      get { return _Id; } 
     } 

     public Foo(string id) { _Id = id; }  
    } 

    public class Boo 
    { 
     public string Id { get; set; } 

     public Boo(Foo foo) { Id = foo.Id; } 

     public Boo() { } 
    } 
}