2016-06-29 13 views
6

ho la seguente stringa JSON:Json.NET deserializzare o serializzare JSON proprietà di stringa e della mappa a diversi nomi di proprietà definite in fase di esecuzione

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

Ho intenzione di mappare questo per il seguente modello:

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; } 
} 

voglio essere in grado di mappare i questi nomi di proprietà di nomi diversi in fase di esecuzione, quando la deserializzazione questa stringa JSON in questo modo:

JsonConvert.DeserializeObject<RootObject>(jsonString); 

Ad esempio, nel processo di deserializzazione, voglio deserializzare il nome di "proprietà1" in "differen_property_name1" o "differen_property_name2" o "differen_property_name3". Perché ho scelto il nuovo nome in fase di esecuzione (il nuovo nome a cui cambierà il nome "proprietà1" a), non posso utilizzare la soluzione con JsonPropertyAttribute, come suggerito qui:

.NET NewtonSoft JSON deserialize map to a different property name

Una delle risposte della domanda precedente (la risposta di Jack) utilizza l'ereditarietà di DefaultContractResolver ma in quel caso non sembra funzionare.

Aggiornamento

Più tardi, avevo bisogno di serializzare l'oggetto che ho ricevuto dal deserializzazione e mappare le proprietà a diversi nomi di proprietà, definiti in fase di esecuzione. Ho usato lo stesso metodo di Brian ha proposto di fare la serializzazione:

ho usato il dizionario per mappare i miei nuovi nomi di proprietà:

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

e poi ho usato DynamicMappingResolver di Brian per serializzare l'oggetto in questo modo:

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

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

Perché voglio decidere in fase di esecuzione che il nuovo nome voglio dare alla proprietà e con JsonPropertyAttribute è necessario utilizzare un nome di "hard coded". –

+0

Un modo (anche se potrebbe essere brutto) sarebbe deserializzare prima il JSON, quindi mappare l'oggetto deserializzato all'oggetto con i nuovi nomi. Ci può essere un modo più elegante, però. – Tim

+0

@Tim, in che modo eseguire il mapping dell'oggetto deserializzato a un oggetto con i nuovi nomi quando è necessario definire i nuovi nomi in fase di runtime? –

risposta

5

Per eseguire questa operazione è possibile utilizzare un valore personalizzato ContractResolver. Fondamentalmente è la stessa idea di mettere un attributo [JsonProperty] su ciascun membro della classe per il quale si desidera eseguire il mapping su un nome di proprietà JSON diverso, tranne che lo si fa a livello di programmazione tramite il resolver. È possibile passare un dizionario dei mapping desiderati al risolutore quando lo si imposta prima della deserializzazione.

Ecco ciò che il codice di risoluzione personalizzato potrebbe essere simile:

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; 
    } 
} 

Per utilizzare il resolver, prima di costruire un Dictionary<Type, Dictionary<string, string>> contenente le mappature. La chiave del dizionario esterno è il tipo di classe le cui proprietà si desidera mappare; il dizionario interno è una mappatura dei nomi delle proprietà delle classi ai nomi delle proprietà JSON. Devi solo fornire una mappatura per le proprietà i cui nomi non corrispondono già al JSON.

Così, per esempio, se il vostro JSON si presentava così (notare i nomi modificati delle proprietà all'interno dell'oggetto details) ...

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

... e di voler mappare alle classi nella tua domanda, si potrebbe creare il dizionario come questo:

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

l'ultimo passo è quello di configurare le impostazioni serializzatore con una nuova istanza resolver, dandogli il dizionario mappatura che avete appena costruito, e poi passare le impostazioni per JsonConvert.DeserializeObject().

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

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

Ecco una demo: https://dotnetfiddle.net/ULkB0J

+0

Esattamente quello che stavo cercando. Grazie!! –

0

Non credo che JSON.net abbia alcun supporto per quello che stai cercando. Dovresti invece deserializzare il JSON in JObject se non conosci il formato o qualche generico se lo fai (per esempio se il JSON dice sempre property1 puoi usare un oggetto generico per rappresentarlo).

Una volta ottenuto l'oggetto generico, è necessario convertire i campi. Tutto ciò che non è modificabile può essere fatto direttamente, ma per qualsiasi altra cosa sarà necessario utilizzare Reflection.

Fondamentalmente si tratta di ottenere il tipo (typeof(Details) o obj.GetType()) e quindi cercare la Proprietà che si desidera aggiornare. Infine dovresti riuscire a trovare il metodo setter e chiamarlo fornendo il valore originale fuori dal tuo oggetto generico.

1

Perché fare questo in un unico passaggio? Perché non deserializzare nel tuo oggetto standard e quindi mapparli dinamicamente usando Automapper?

qualcosa di simile:

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