2017-12-08 2 views
1

die Schnittstelle Gegeben:Deserialisierung des Objekts in Klassenbibliothek unter Inkaufnahme eines Schnittstellenparameter (C#, MVC)

interface IObject 
{ 
    public string Name {get;set} 
} 

Und die folgenden Objekte:

class A: IObject 
{ 
    public string Name {get;set} 
    public int ID {get;set;} 
} 

class B: IObject 
{ 
    public string Name {get;set} 
    public Guid ID {get;set;} 
} 

Ich habe zwei nutzenden Anwendungen, die verwenden verschiedene Datenbankschema, aber machen Sie den gleichen Job. Dies ist eine Frage der Pflege einer alten App, während ein Team eine neue schreibt. Voraussetzung ist, dass beide dieselbe Bibliothek verwenden, weshalb dieses Problem besteht. Die Apps rufen beide die gleiche externe API, und dieser Ruf ist in seiner eigenen statischen Klasse und ist wie so zugegriffen:

public static IEnumerable<IObject> List(Guid listUniqueIdentifier) 
{ 
     //a custom class to call the API 
     using (var cmd = new WebApiCommand()) 
     { 
           //custom enum 
      cmd.CommandType = ApiComType.GET; 
      cmd.Command = "ListObjects"; 
      cmd.Filter = listUniqueIdentifier.ToString(); 
      IEnumerable<object> data = cmd.Execute(); 
      var result = new List<IObject>(); 
      foreach(var d in data) 
      { 
       result.add(JsonConvert.DeserializeObject<IObject>(Convert.ToString(d))); 
      } 
      return result; 
     } 

} 

Natürlich können Sie nicht eine Schnittstelle instanziiert, so

result.add(JsonConvert.DeserializeObject<IObject>(Convert.ToString(d))); 

Umbrüche . Ich möchte nicht nur versuchen, die GUID-verwendende Version zu erstellen, und wenn es fehlschlägt, verwende die Integer-Version. Nicht nur das ist ein bisschen hacky, aber es ist die neuere DB, die die ganze Zahl verwendet, also würde ich einen try Block als Flusskontrolle in nur dem schlechtesten möglichen Weg verwenden. Wie kann ich die Deserialisierung für den richtigen Objektinstanztyp (A oder B) durchführen, ohne für jeden Typ unterschiedliche Methoden zu schreiben? Edit: Die Lösungen, die ich gefunden habe, beinhalten normalerweise entweder das Serialisieren des $ -Typs oder das anderweitige Speichern des Instanztyps während der Serialisierung. Dies ist nicht möglich, da die API mit einer Drittanbieteranwendung verbunden ist und dieses Datum nicht speichern kann.

Antwort

0

Ihr Problem, soweit ich das beurteilen kann, ist, dass Sie keine Möglichkeit haben, durch "d" zu identifizieren, in welche Art von Klasse serialisiert werden soll. (Pseudo)

foreach(var d in data) 
    { 
     like if(Convert.ToString(d).All(char.IsDigit) && Convert.ToString(d).length <= Int32.MaxValue) 
     { 
      result.add(JsonConvert.DeserializeObject<A>(Convert.ToString(d))); 
     } 
     else 
     { 
      result.add(JsonConvert.DeserializeObject<B>(Convert.ToString(d))); 
     } 

    } 

EDIT: Ich denke, das ist der beste Ansatz, mit den begrenzten Informationen, die Sie gegeben haben. Wenn nicht, würde ich empfehlen, dass Sie den Datenaufruf selbst ändern, so dass die WebAPI Ihnen je nach aufrufender Anwendung zwei verschiedene Datensätze zur Verfügung stellen kann.

0

Ein Ansatz, den ich mir vorstellen kann, ist das Hinzufügen einer internen Hilfsklasse, die Sie zuerst deserialize und von dort Ihre A und B Objekte generieren. So ähnlich (Hinweis: Ich gehe davon aus, dass Ihre GUID ohne Trennzeichen ist).

internal class Helper 
{ 
    public string _name {get;set;} 
    public string _id {get;set;} 

    public IObject GetAOrBObject() 
    { 
     if (_id.Length == 32) // Assume GUID with no separator like '-' 
     { 
      return new A() { Name = _name, ID = Guid.Parse(_id) }; 
     } 
     else 
     { 
      return new B() { Name = _name, ID = int.Parse(_id) }; 
     } 
    } 
} 

Dann können Sie den Code in der Schleife, um diese Änderung:

foreach(var d in data) 
{ 
    var temp = JsonConvert.DeserializeObject<Helper>(Convert.ToString(d))); 
    result.add(temp.GetAOrBObject()); 
} 
return result; 
1

Sie einen benutzerdefinierten verwenden kann JsonConverter die Art der ID zu erkennen und dann instanziiert und die richtige Art von Objekt während der Deserialisierung bevöl :

class CustomIObjectConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(IObject).IsAssignableFrom(objectType); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject jo = JObject.Load(reader); 
     IObject target; 
     if (jo["ID"].Type == JTokenType.Integer) 
     { 
      target = Activator.CreateInstance<A>(); 
     } 
     else // Guid 
     { 
      target = Activator.CreateInstance<B>(); 
     } 
     serializer.Populate(jo.CreateReader(), target); 
     return target; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

den Konverter zu verwenden, nur eine Instanz davon zu DeserializeObject() passieren:

var converter = new CustomIObjectConverter(); 
... 
foreach(var d in data) 
{ 
    result.add(JsonConvert.DeserializeObject<IObject>(Convert.ToString(d), converter)); 
} 

Hier ist eine kurze Demo, um das Konzept zu beweisen: https://dotnetfiddle.net/p2HzsL

Verwandte Themen