2016-06-29 38 views
6

definiert Serialisierung Ich habe folgende JSON-String:Json.NET deserialisieren oder JSON-String und Karteneigenschaften auf unterschiedlichen Eigenschaftsnamen zur Laufzeit

{ 
    "values": { 
     "details": { 
      "property1": "94", 
      "property2": "47", 
      "property3": "32", 
      "property4": 1 
     }, 
     count: 4 
    } 
}  

ich das folgende Modell abzubilden werde:

public class Details 
{ 
    public string property1 { get; set; } 
    public string property2 { get; set; } 
    public string property3 { get; set; } 
    public int property4 { get; set; } 
} 

public class Values 
{ 
    public Details details { get; set; } 
    public int count { get; set; } 
} 

public class RootObject 
{ 
    public Values values { get; set; } 
} 

ich möchte verschiedene Namen zur Laufzeit die diese Eigenschaftsnamen zuordnen können, wenn diese JSON-String wie folgt Deserialisieren:

JsonConvert.DeserializeObject<RootObject>(jsonString); 

Zum Beispiel möchte ich im Deserialisierungsprozess den Namen von "property1" zu "differen_property_name1" oder "differen_property_name2" oder "differen_property_name3" deserialisieren. Da mir den neuen Namen zur Laufzeit Wahl (der neue Name des „property1“ Namen Ich werde das ändern), kann ich nicht die Lösung verwendet JsonPropertyAttribute verwenden, wie hier vorgeschlagen:

.NET NewtonSoft JSON deserialize map to a different property name

Eine der Antworten der obigen Frage (Jacks Antwort) verwendet die Vererbung von DefaultContractResolver, aber es scheint in diesem Fall nicht zu funktionieren.

aktualisiert

Später, ich brauchte das Objekt, das ich von der Deserialisierung bekam serialisiert werden und die Eigenschaften zu unterschiedlichen Eigenschaftsnamen zuordnen, zur Laufzeit definiert. habe ich die gleiche Methode wie Brian die Serialisierung zu tun vorgeschlagen:

ich das Wörterbuch verwendet meine neue Eigenschaftsnamen zur Karte:

var map = new Dictionary<Type, Dictionary<string, string>> 
{ 
    { 
     typeof(Details), 
     new Dictionary<string, string> 
     { 
      {"property1", "myNewPropertyName1"}, 
      {"property2", "myNewPropertyName2"}, 
      {"property3", "myNewPropertyName3"}, 
      {"property4", "myNewPropertyName4"} 
     } 
    } 
};  

und dann habe ich Brians DynamicMappingResolver das Objekt wie folgt zu serialisiert:

var settings = new JsonSerializerSettings 
{ 
    ContractResolver = new DynamicMappingResolver(map) 
}; 

var root = JsonConvert.SerializeObject(myObjectInstance, settings);    
+0

Weil ich in der Laufzeit entscheiden wollen, welche neuen Namen ich auf die Eigenschaft geben wollen und mit JsonPropertyAttribute Sie haben eine „hart codiert“ Namen zu verwenden. –

+0

Ein Weg (obwohl es hässlich sein könnte) wäre, den JSON zuerst zu deserialisieren und dann das deserialisierte Objekt dem Objekt mit den neuen Namen zuzuordnen. Es mag einen eleganteren Weg geben. – Tim

+0

@Tim, wie würden Sie das deserialisierte Objekt einem Objekt mit den neuen Namen zuordnen, wenn die neuen Namen zur Laufzeit definiert werden müssen? –

Antwort

5

Sie könnten eine benutzerdefinierte ContractResolver verwenden, um dies zu tun. Im Prinzip ist es die gleiche Idee wie das Setzen eines [JsonProperty] Attributs für jedes Klassenmitglied, für das Sie einen anderen JSON-Eigenschaftsnamen zuordnen möchten, außer dass Sie dies programmgesteuert über den Resolver tun. Sie können ein Wörterbuch mit den gewünschten Zuordnungen an den Resolver übergeben, wenn Sie es vor der Deserialisierung einrichten.

Hier ist, was der benutzerdefinierte Resolver-Code aussehen könnte:

class DynamicMappingResolver : DefaultContractResolver 
{ 
    private Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap; 

    public DynamicMappingResolver(Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap) 
    { 
     this.memberNameToJsonNameMap = memberNameToJsonNameMap; 
    } 

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     JsonProperty prop = base.CreateProperty(member, memberSerialization); 
     Dictionary<string, string> dict; 
     string jsonName; 
     if (memberNameToJsonNameMap.TryGetValue(member.DeclaringType, out dict) && 
      dict.TryGetValue(member.Name, out jsonName)) 
     { 
      prop.PropertyName = jsonName; 
     } 
     return prop; 
    } 
} 

die Resolver zu verwenden, konstruiert zunächst ein Dictionary<Type, Dictionary<string, string>> Ihre Zuordnungen enthalten. Der Schlüssel des äußeren Wörterbuchs sind die Klassentypen, deren Eigenschaften Sie zuordnen möchten. Das innere Wörterbuch ist eine Zuordnung der Klasseneigenschaftsnamen zu JSON-Eigenschaftsnamen. Sie müssen nur eine Zuordnung für die Eigenschaften bereitstellen, deren Namen nicht bereits mit dem JSON übereinstimmen.

So zum Beispiel, wenn Ihr JSON sah wie folgt aus (beachten Sie die geänderten Namen der Eigenschaften im Inneren des details Objekt) ...

{ 
    "values": { 
     "details": { 
      "foo": "94", 
      "bar": "47", 
      "baz": "32", 
      "quux": 1 
     }, 
     count: 4 
    } 
} 

... und man wollte es auf die Klassen abzubilden in Ihrer Frage, würden Sie das Wörterbuch wie folgt erstellen:

var map = new Dictionary<Type, Dictionary<string, string>> 
{ 
    { 
     typeof(Details), 
     new Dictionary<string, string> 
     { 
      {"property1", "foo"}, 
      {"property2", "bar"}, 
      {"property3", "baz"}, 
      {"property4", "quux"} 
     } 
    } 
}; 

Der letzte Schritt der Serializer-Einstellungen mit einem neuen Resolver-Instanz einzurichten ist, ist es das Mapping Wörterbuch geben Sie einfach aufgebaut, und übergeben Sie dann die Einstellungen zu JsonConvert.DeserializeObject(). Hier

var settings = new JsonSerializerSettings 
{ 
    ContractResolver = new DynamicMappingResolver(map) 
}; 

var root = JsonConvert.DeserializeObject<RootObject>(json, settings); 

ist eine Demo: https://dotnetfiddle.net/ULkB0J

+0

Genau das, was ich gesucht habe. Vielen Dank!! –

0

Ich glaube nicht, dass JSON.net hat keine Unterstützung für das, was Sie suchen. Sie sollten stattdessen das JSON in JObject deserialisieren, wenn Sie das Format nicht kennen oder ein generisches, wenn Sie das tun (wenn beispielsweise der JSON immer property1 sagt, können Sie ein generisches Objekt verwenden, um es darzustellen).

Sobald Sie Ihr generisches Objekt als nächstes haben, müssen Sie die Felder übersetzen. Alle, die nicht veränderbar sind, können direkt ausgeführt werden, aber für alles andere müssen Sie Reflection verwenden.

Grundsätzlich bezieht es sich auf den Typ (typeof(Details) oder obj.GetType()) und dann nach der Eigenschaft suchen, die Sie aktualisieren möchten. Schließlich sollten Sie in der Lage sein, die Setter-Methode zu finden und sie aufzurufen, um den ursprünglichen Wert aus Ihrem generischen Objekt zu liefern.

1

Warum tun dies in einem Schritt? Warum nicht in Ihr Standardobjekt deserialisieren und dann dynamisch mit Automapper überlagern?

so etwas wie:

Mapper.Initialize(c => 
{ 
    c.ReplaceMemberName("property1 ", "differen_property_name1"); 
});