Json.NET deserialize ou serialize la chaîne json et les propriétés de la carte aux différents noms de propriétés définis à l'exécution

j'ai la chaîne JSON suivante:

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

je vais le faire correspondre au modèle suivant:

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

je veux pouvoir mapper les noms de ces propriétés à différents noms à l'exécution lors de la desérialisation de cette chaîne JSON comme ceci:

JsonConvert.DeserializeObject<RootObject>(jsonString);

par exemple, dans le processus de désérialisation, je veux que le nom de "propriété1" soit désérialisé pour " differen_property_name1 "ou " differen_property_name2"." ou "differen_property_name3". Parce que je choisis le nouveau nom à l'exécution (le nouveau nom auquel je changerai le nom" propriety1 "En), Je ne peux pas utiliser la solution en utilisant JsonPropertyAttribute, comme suggéré ici:

.NET NewtonSoft JSON désérialiser carte à un autre nom de la propriété

L'une des réponses de la question ci-dessus (la réponse de Jack) utilise l'héritage de DefaultContractResolver mais il ne semble pas fonctionner dans ce cas.

mise à Jour

plus tard, j'ai eu besoin de sérialiser l'objet que j'ai obtenu à partir de la desérialisation et de mapper les propriétés aux différents noms de propriétés, définis à l'exécution. J'ai utilisé la même méthode que celle proposée par Brian pour faire la sérialisation:

j'ai utilisé le dictionnaire pour cartographier mes nouveaux noms de propriétés:

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

et puis J'ai utilisé Brian DynamicMappingResolver pour sérialiser l'objet comme ceci:

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

var root = JsonConvert.SerializeObject(myObjectInstance, settings);            
8
demandé sur Community 2016-06-30 01:50:32

4 réponses

vous pouvez utiliser une coutume ContractResolver pour faire cela. Fondamentalement, c'est la même idée que de mettre un attribut [JsonProperty] sur chaque membre de classe pour lequel vous voulez mapper un nom de propriété JSON différent, sauf que vous le faites programmatiquement via le résolveur. Vous pouvez passer un dictionnaire de vos correspondances souhaitées au résolveur lors de sa mise en place juste avant la désérialisation.

voici à quoi pourrait ressembler le code du résolveur personnalisé:

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

pour utiliser le résolveur, construisez d'abord un Dictionary<Type, Dictionary<string, string>> contenant vos correspondances. La clé du dictionnaire externe est le(S) type (S) de classe dont vous voulez cartographier les propriétés; le dictionnaire interne est un mappage des noms de propriétés de classe aux noms de propriétés JSON. Vous n'avez qu'à fournir un mappage pour les propriétés dont les noms ne correspondent pas déjà au JSON.

ainsi, par exemple, si votre JSON ressemblait à ceci (notez les noms changés des propriétés à l'intérieur du details de l'objet)...

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

...et vous avez voulu le mapper aux classes dans votre question, vous créeriez le dictionnaire comme ceci:

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

la dernière étape est de mettre en place les paramètres serializer avec une nouvelle instance de résolveur, lui donnant le dictionnaire de cartographie que vous venez de construire, puis passer les paramètres à JsonConvert.DeserializeObject() .

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

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

Voici une démo: https://dotnetfiddle.net/ULkB0J

7
répondu Brian Rogers 2016-06-30 01:01:36

Pourquoi faire cela en une seule étape? Pourquoi ne pas les désérialiser dans votre objet standard et ensuite les cartographier dynamiquement en utilisant Automapper ?

quelque chose comme:

Mapper.Initialize(c =>
{
    c.ReplaceMemberName("property1 ", "differen_property_name1");
});
1
répondu Ian Mercer 2016-06-30 03:43:18

si vous ne voulez pas utiliser une coutume ContractResolver pour le faire. Utilisez [JsonProperty("")] pour rechercher différentes variations du nom de la propriété et retourner avec une autre propriété comme celle-ci:

public class Details
{
    private string _property1;
    private string _property2;

    [JsonProperty("property1")]
    public string prop1 {get;set;}

    [JsonProperty("foo")]
    public string foo {get;set;}

    public string getProperty1 
    {
        get {_property1=prop1??foo;return _property1;}
        set{prop1=value;foo=value;}
    }

    [JsonProperty("property2")]
    public string prop2 {get;set;}

    [JsonProperty("bar")]
    public string bar {get;set;}

    public string getProperty2
    {
        get {_property2=prop2??bar;return _property2;}
        set {prop2=value;bar=value;}
    }
}

démo ici: https://dotnetfiddle.net/V17igc

1
répondu ismael. 2018-03-13 23:18:38

Je ne crois pas que JSON.net a tout le soutien que vous recherchez. Vous devriez plutôt desérialiser le JSON en JObject si vous ne connaissez pas le format ou un autre générique si vous le connaissez (par exemple si le JSON dit toujours property1 vous pouvez utiliser un objet générique pour le représenter).

une fois que vous avez votre objet générique, vous devez traduire les champs. Tout ce qui n'est pas modifiable peut être fait directement, mais pour tout le reste vous aurez besoin pour utiliser la Réflexion.

fondamentalement, il s'agit d'obtenir le type ( typeof(Details) ou obj.GetType() ) et ensuite la recherche de la propriété que vous voulez mettre à jour. Enfin, vous devriez être en mesure de trouver la méthode setter et de l'appeler en fournissant la valeur originale de votre objet générique.

0
répondu Guvante 2016-06-29 23:08:57