Ich verwende eine DataContractJsonSerializer
, um ein Objektdiagramm zu serialisieren. Wenn ich die Objekte konstruiere, erhält jeder einen Verweis auf eine Instanz eines Utility-Objekts (es ist eine Factory, um Instanzen von Unterklassen einer abstrakten Vertragsklasse zu erzeugen) - das funktioniert großartig, bis der Graph serialisiert und dann wieder deserialisiert wird, woraufhin die Objekte Sie haben keinen Bezug mehr auf das Dienstprogrammobjekt. Ich brauche diese Referenz. Wie würden Sie empfehlen, dies zu implementieren (Singletons funktionieren nicht, weil separate Graphen ihre eigene Instanz des Objekts benötigen)?DataContractJsonSerializer - teilen Sie eine Objektinstanz für das gesamte Diagramm?
Antwort
Eine Möglichkeit, dies zu erreichen, ist mit einer data contract surrogate. Mithilfe von Surrogate können Sie Ihre "echte" Factory während der Serialisierung durch einen Dummy-Stub ersetzen. Ersetzen Sie dann während der Deserialisierung den Dummy durch die gewünschte Fabrik.
Wenn also Ihre Klassen wie etwas aussehen:
public abstract class FactoryBase
{
}
public class Factory : FactoryBase
{
}
public interface IHasFactory
{
FactoryBase Factory { get; }
}
[DataContract]
public abstract class HasFactoryBase : IHasFactory
{
[DataMember(IsRequired = true)]
FactoryBase factory;
public FactoryBase Factory { get { return factory; } }
public HasFactoryBase(FactoryBase factory)
{
this.factory = factory;
}
}
[DataContract]
public class Foo : HasFactoryBase
{
public Foo(FactoryBase factory)
: base(factory)
{
this.Bars = new List<Bar>();
}
[DataMember]
public List<Bar> Bars { get; set; }
}
[DataContract]
public class Bar : HasFactoryBase
{
public Bar(FactoryBase factory) : base(factory) { }
}
Sie eine IDataContractSurrogate
definieren alle Vorkommen von FactoryBase
mit einem Surrogat zu ersetzen, wie folgt:
public class FactorySurrogateSelector : IDataContractSurrogate
{
[DataContract]
class FactorySurrogate
{
}
readonly FactoryBase factory;
public FactorySurrogateSelector(FactoryBase factory)
{
this.factory = factory;
}
#region IDataContractSurrogate Members
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public Type GetDataContractType(Type type)
{
if (typeof(FactoryBase).IsAssignableFrom(type))
return typeof(FactorySurrogate);
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj is FactorySurrogate)
return factory;
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj is FactoryBase)
{
return new FactorySurrogate();
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
#endregion
}
Dann serialisiert und deserialisiert wie folgt :
var factory = new Factory();
var test = new Foo(factory)
{
Bars = { new Bar(factory) },
};
var surrogate = new FactorySurrogateSelector(factory);
var serializer = new DataContractJsonSerializer(test.GetType(), Enumerable.Empty<Type>(), int.MaxValue, false, surrogate, false);
byte[] json;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, test);
json = stream.ToArray();
}
Foo test2;
using (var stream = new MemoryStream(json))
{
test2 = (Foo)serializer.ReadObject(stream);
}
if (test2.Factory != test.Factory)
throw new InvalidOperationException();
Hinweis tha t die gewünschte Fabrik wurde direkt in den Konstruktor der FactorySurrogateSelector
übergeben und dann innerhalb jedes Typs gesetzt, der Instanzen des Werkstyps enthält.
Die resultierende JSON wird wie folgt aussehen:
{
"factory": {},
"Bars": [
{
"factory": {}
}
]
}
Einige Qualifikationen:
Ihre Fabrik von einer gemeinsamen Basisklasse erben müssen, hier
FactoryBase
. Die Datenvertragserialisierer werden niemals serialisieren ein Schnittstellenelement, z.IFactory factory
wobeiIFactory
Ihre Factory-Schnittstelle ist, auch wenn ein entsprechender Ersatz vorhanden ist.Die leeren Objekte
"factory": {}
müssen im JSON erscheinen, damit das Ersatzgerät während der Deserialisierung den korrekten "echten" Fabrikwert eingibt. Daher die[DataMember(IsRequired = true)]
.
Ein anderer Weg ist zu erreichen, ein thread static oder thread local Factory-Objekt einführen, dann Klassen füllen damit ein [OnDeserializing]
callback verwenden.
Wenn Sie also Ihre Typen wie folgt definieren:
public interface IFactory
{
}
public class Factory : IFactory
{
}
public interface IHasFactory
{
IFactory Factory { get; }
}
[DataContract]
public abstract class HasFactoryBase : IHasFactory
{
[ThreadStatic]
static IFactory deserializedFactory;
static IFactory DeserializedFactory
{
get
{
return deserializedFactory;
}
set
{
deserializedFactory = value;
}
}
public static IDisposable SetDeserializedFactory(IFactory factory)
{
return new PushValue<IFactory>(factory,() => DeserializedFactory, val => DeserializedFactory = val);
}
IFactory factory;
public IFactory Factory { get { return factory; } }
public HasFactoryBase(IFactory factory)
{
this.factory = factory;
}
[OnDeserializing]
void OnDeserializing(StreamingContext context)
{
this.factory = DeserializedFactory;
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
[DataContract]
public class Foo : HasFactoryBase
{
public Foo(IFactory factory)
: base(factory)
{
this.Bars = new List<Bar>();
}
[DataMember]
public List<Bar> Bars { get; set; }
}
[DataContract]
public class Bar : HasFactoryBase
{
public Bar(IFactory factory) : base(factory) { }
}
können Sie serialisieren und deserialisieren wie folgt:
var factory = new Factory();
var test = new Foo(factory)
{
Bars = { new Bar(factory) },
};
var serializer = new DataContractJsonSerializer(test.GetType());
byte [] json;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, test);
json = stream.ToArray();
}
Foo test2;
using (HasFactoryBase.SetDeserializedFactory(factory))
using (var stream = new MemoryStream(json))
{
test2 = (Foo)serializer.ReadObject(stream);
}
if (test2.Factory != test.Factory)
throw new InvalidOperationException();
Und die JSON wird wie folgt aussehen:
{
"Bars": [
{}
]
}
Einige Anmerkungen:
Das Factory-Objekt wird im JSON überhaupt nicht angezeigt.
Die Factory-Objekte müssen nicht mehr von einer abstrakten Basisklasse erben, sie können einfach eine gemeinsame
IFactory
-Schnittstelle implementieren.
- 1. Graphviz: Schriftart für das gesamte Diagramm ändern?
- 2. Burnup-Diagramm für das gesamte Projekt erhalten
- 3. Javascript Diagramm füllt das gesamte Browserfenster
- 4. Objektinstanz zwischen Web-Service-Aufrufen teilen
- 5. Überprüfen Sie das Vorhandensein einer Objektinstanz
- 6. Erstellen eine unkonstruierten Objektinstanz
- 7. Teilen Sie das Fenster vertikal in XAML
- 8. Deserialisierung Problem mit DataContractJsonSerializer
- 9. ändere UILabels Textfarbe für das gesamte Projekt
- 10. undefined Methode `includes 'für Objektinstanz
- 11. Überprüfen Sie das gesamte Array für einzelne wenn Betrieb
- 12. Durchsuchen Sie das gesamte Projekt für Includes in Eclipse CDT
- 13. Erstellen Sie einen beliebigen Namen für das gesamte Skript
- 14. JavaScript exportiert eine neue Objektinstanz
- 15. Netbeans - Fix Importe für das gesamte Projekt
- 16. Entspricht AppConfig.ready() für das gesamte Projekt?
- 17. Legen Sie eine Zeitüberschreitung für eine bestimmte Funktion/einen Codeblock (nicht das gesamte Skript) fest?
- 18. Holen Sie sich iTunes-Link für das gesamte Konto, bevor Sie eine App senden
- 19. UglifyJs für das gesamte Node-Projekt verwenden?
- 20. ngÄnderungsähnliche Funktionalität für das gesamte Formular
- 21. django onchange senden für das gesamte Formular
- 22. Polarkoordinaten-/Kreislayout für das gesamte facet_grid
- 23. notifyDataSetChanged() Aktualisieren Sie das gesamte Layout
- 24. Fehler für das gesamte Skript unterdrücken
- 25. Nachhaken für das gesamte Feature in Gurke
- 26. C# MouseEnter-Event für das gesamte Fenster
- 27. Wie für das gesamte Wort grep
- 28. Ansible: Verschiedene Variablen für das gesamte Inventar
- 29. Werte in Diagramm in SSRS teilen
- 30. Fügen Sie eine zweite Legende in das Diagramm in R