2016-08-29 3 views
3

Ich versuche, diese Art von Objekt mit protobuf-net zu serialisiert:Protobuf-net: Wie komplexe Sammlung zu serialisieren?

[ProtoContract] 
public class RedisDataObject 
{ 
    [ProtoMember(1)] 
    public string DataHash; 
    [ProtoMember(2, DynamicType = true)] 
    public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value; 
} 

[Serializable] 
public enum ContextActions 
{ 
    Insert, 
    Update, 
    Delete 
} 

ich List<object> bin mit, weil ich es unterschiedliche Klassen-Instanzen anderer Klassen zu speichern ich in meinem Code.

Aber ich bin immer diese Fehlermeldung:

Unable to resolve a suitable Add method for System.Collections.Generic.Dictionary... 

Dies ist eindeutig, weil das Wörterbuch, aber ich konnte keine Lösung, wie zu lösen dieses Problem finden.

Antwort

5

Ihr grundlegendes Problem ist, dass DynamicType = true nur für diese bestimmte Eigenschaft gilt und Typinformationen nur für den Wert dieser bestimmten Eigenschaft serialisiert. Es gilt nicht rekursiv für eine der Eigenschaften des Objekts. Allerdings ist Ihr object Wert tief in mehreren Ebenen der Behälter verschachtelt:

public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value; 

Was Sie tun müssen, ist Typinformationen für jeden object in diesem Wörterbuch von Tupeln von Listen serialisieren. Sie können dies tun, indem Sie einen Ersatzwert Typ Einführung:

[ProtoContract] 
public struct DynamicTypeSurrogate<T> 
{ 
    [ProtoMember(1, DynamicType = true)] 
    public T Value { get; set; } 
} 

public static class DynamicTypeSurrogateExtensions 
{ 
    public static List<DynamicTypeSurrogate<T>> ToSurrogateList<T>(this IList<T> list) 
    { 
     if (list == null) 
      return null; 
     return list.Select(i => new DynamicTypeSurrogate<T> { Value = i }).ToList(); 
    } 

    public static List<T> FromSurrogateList<T>(this IList<DynamicTypeSurrogate<T>> list) 
    { 
     if (list == null) 
      return null; 
     return list.Select(i => i.Value).ToList(); 
    } 
} 

Und dann RedisDataObject Modifizieren eines Surrogat-Wörterbuch zur Serialisierung wie folgt:

[ProtoContract] 
public class RedisDataObject 
{ 
    [ProtoMember(1)] 
    public string DataHash; 

    [ProtoIgnore] 
    public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value; 

    [ProtoMember(2)] 
    private Dictionary<ContextActions, List<Tuple<string, List<DynamicTypeSurrogate<object>>>>> SurrogateValue 
    { 
     get 
     { 
      if (Value == null) 
       return null; 
      var dictionary = Value.ToDictionary(
       p => p.Key, 
       p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.ToSurrogateList())).ToList())); 
      return dictionary; 
     } 
     set 
     { 
      if (value == null) 
       Value = null; 
      else 
      { 
       Value = value.ToDictionary(
        p => p.Key, 
        p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.FromSurrogateList())).ToList())); 
      } 
     } 
    } 
} 

Beachten Sie auch die Beschränkungen für DynamicTypehere erwähnt:

DynamicType - speichert zusätzliche Type Informationen mit dem Typ (standardmäßig enthält es die AssemblyQualifiedName, obwohl dies vom Benutzer kontrolliert werden kann). Dies macht es möglich, schwache Modelle zu serialisieren, dh wo object für Eigenschaft Mitglieder verwendet wird, aber derzeit ist dies auf Vertrag Typen (nicht Primitiven) beschränkt, und funktioniert nicht für Typen mit Vererbung (diese Einschränkungen können zu einem späteren Zeitpunkt entfernt werden Zeit). Wie bei AsReference verwendet diese ein ganz anderes Layout-Format

Während die obige Dokumentation auf der former project site existiert und wurde auf die current site nicht bewegt, die Beschränkung auf Nicht-Vertragsarten besteht definitiv noch ab Version 2.0.0.668 . (I getestet, dass das Hinzufügen eines int Wert auf den List<object> versagt;. Ich habe nicht geprüft, ob die Beschränkung auf Vererbung noch existiert)

+0

ich ein 'InvalidOperationException' sagen _Dynamic Typ ist kein Vertrag Typ: _, Objektlisten verdoppelt, Dezimalzahlen, Daten und Zeichenketten enthalten. –

+0

@ andrei.ciprian - das klingt wie das in [diese Frage] beschriebene Problem (https://stackoverflow.com/questions/17656133). – dbc

+0

Ich mag Ihren Ansatz, aber es funktioniert nicht, für jeden Typ. Die Frage, die Sie vorgeschlagen haben, würde ich mir vorstellen (http://stackoverflow.com/a/38914841/2239678) –

0

Mit Hilfe von dbc, und alle Links in seiner Antwort erwähnt und kommentiert

[ProtoContract] 
[ProtoInclude(1, typeof(ObjectWrapper<int>))] 
[ProtoInclude(2, typeof(ObjectWrapper<decimal>))] 
[ProtoInclude(3, typeof(ObjectWrapper<DateTime>))] 
[ProtoInclude(4, typeof(ObjectWrapper<string>))] 
[ProtoInclude(5, typeof(ObjectWrapper<double>))] 
[ProtoInclude(6, typeof(ObjectWrapper<long>))] 
[ProtoInclude(8, typeof(ObjectWrapper<Custom>))] 
[ProtoInclude(9, typeof(ObjectWrapper<CustomType[]>))] 
public abstract class ObjectWrapper 
{ 
    protected ObjectWrapper() { } 
    abstract public object ObjectValue { get; set; } 

    public static ObjectWrapper Create(object o) { 
    Type objectType = o.GetType(); 
    Type genericType = typeof(ObjectWrapper<>); 
    Type specializedType = genericType.MakeGenericType(objectType); 
    return (ObjectWrapper)Activator.CreateInstance(specializedType, new object[] { o }); 
    } 
} 

Nachteil ist, dass Sie alle Typen, die Sie verwenden, in Ihrer Objektliste registrieren müssen. Jedes Mal, wenn ein neuer Typ, der nicht in der ProtoInclude Serie enthalten ist, auftaucht, erhalten Sie eine InvalidOperationException mit der Nachricht Unerwarteter Untertyp: ObjectWrapper`1 [[NewType]].

[ProtoContract] 
public class RedisDataObjectWrapper { 
    [ProtoMember(1)] public string DataHash; 
    [ProtoIgnore] public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value; 

[ProtoMember(2)] 
private Dictionary<ContextActions, List<Tuple<string, List<ObjectWrapper>>>> AdaptedValue { 
    get { 
    if (Value == null) return null; 
    var dictionary = Value.ToDictionary(
     p => p.Key, 
     p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>ObjectWrapper.Create(x)).ToList())).ToList())); 
    return dictionary; 
    } 
    set { 
    if (value == null) Value = null; 
    else { 
     Value = value.ToDictionary(
      p => p.Key, 
      p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>x.ObjectValue).ToList())).ToList())); 
    } } } } 
Verwandte Themen