web-dev-qa-db-fra.com

JsonConverter Json.NET personnalisé avec types de données

Je suis tombé sur un service qui génère du JSON au format suivant:

{
    "Author": "me",
    "Version": "1.0.0",
    "data.Type1": {
        "Children": [
            {
                "data.Type1": {
                    "Children": [
                        {
                            "data.Type2": {
                                "name": "John",
                                "surname": "Doe"
                            }
                        }
                    ]
                }
            },
            {
                "data.Type3": {
                    "dob": "1990-01-01"
                }
            }
        ]
    }
}

Les noms de type de données sont conservés en tant que noms de propriété et leurs valeurs sont les objets réels. Ils commencent tous par un préfixe data..

Ce que j'aimerais avoir après, c'est quelque chose comme ceci:

{ // Root
    "Author": "me",
    "Version": "1.0.0",
    "Children": [ // Type1
        {
            "Children": [ // Type1
                { // Type2
                    "Name": "John",
                    "Surname": "Doe"
                }
            ]
        },
        { // Type3
            "DoB": "1990-01-01"
        }
    ]
}

avec les classes suivantes:

class Type1 {
    ICollection<object> Children { get; set; }
}

class Type2 {
    public string Name { get; set; }
    public string Surname { get; set; }
}

class Type3 {
    public DateTime DoB { get; set; }
}

class Root 
{
    public string Author { get; set; }
    public string Version { get; set; }
    public Type1 Children { get; set; }
}

Question

Comment puis-je désérialiser cela dans des classes C # ajoutées, en prenant en compte les types de données et en les supprimant de l'arborescence?

J'ai essayé avec une variable JsonConverter personnalisée, mais je ne sais pas comment choisir le convertisseur de manière dynamique, car le moyen le plus simple serait de définir un attribut sur la propriété, mais il n'est pas pris en charge.

Un petit exemple serait génial.

9
Jaka Konda

Bien que ce format JSON soit quelque peu inhabituel et résiste à l'utilisation d'attributs en raison des noms de propriété dynamiques, il est toujours possible de créer une variable JsonConverter pour la désérialiser dans votre structure de classe préférée avec une petite modification: je vous recommande de modifier la propriété Children la classe Root doit être un ICollection<object> pour refléter la propriété Children dans la classe Type1. Dans l'état actuel des choses, il ne correspond pas à la structure de la sortie souhaitée (où Children est affiché sous forme de tableau et non d'objet) et nécessiterait sinon un code supplémentaire dans le convertisseur pour pouvoir être traité correctement.

class Root
{
    public string Author { get; set; }
    public string Version { get; set; }
    public ICollection<object> Children { get; set; }
}

Voici ce que je propose pour le convertisseur (en supposant que le changement ci-dessus est effectué): 

class CustomConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Root));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);
        Root root = new Root();
        root.Author = (string)obj["Author"];
        root.Version = (string)obj["Version"];
        root.Children = ((Type1)DeserializeTypeX(obj, serializer)).Children;
        return root;
    }

    private object DeserializeTypeX(JObject obj, JsonSerializer serializer)
    {
        JProperty prop = obj.Properties().Where(p => p.Name.StartsWith("data.")).First();
        JObject child = (JObject)prop.Value;
        if (prop.Name == "data.Type1")
        {
            List<object> children = new List<object>();
            foreach (JObject jo in child["Children"].Children<JObject>())
            {
                children.Add(DeserializeTypeX(jo, serializer));
            }
            return new Type1 { Children = children };
        }
        else if (prop.Name == "data.Type2")
        {
            return child.ToObject<Type2>(serializer);
        }
        else if (prop.Name == "data.Type3")
        {
            return child.ToObject<Type3>(serializer);
        }
        throw new JsonSerializationException("Unrecognized type: " + prop.Name);
    }

    public override bool CanWrite
    {
        get { return false; }
    }

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

Armé de ce convertisseur, vous pouvez désérialiser vos classes comme ceci:

Root root = JsonConvert.DeserializeObject<Root>(json, new CustomConverter());

Vous pouvez alors sérialiser au nouveau format comme ceci:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    DateFormatString = "yyyy-MM-dd",
    Formatting = Formatting.Indented
};

Console.WriteLine(JsonConvert.SerializeObject(root, settings));

Fiddle: https://dotnetfiddle.net/ESNMLE

8
Brian Rogers

Vous ne savez pas si cela fonctionnera, mais avez-vous essayé d'utiliser Newtonsoft.Json pour sérialiser l'objet et inclure les balises JsonProperty dans les propriétés de la classe? Je sais que cela fonctionnera lors de la désérialisation de Json dans une classe.

<JsonProperty("user_id")>
Public Property UserID As String
//Converts Json {user_id: 123} to class.UserID = 123

Pour sérialiser avec Newtonsoft, commencez par inclure Newtonsoft dans le projet, puis

Dim stringJson As String = Newtonsoft.Json.JsonConvert.SerializeObject(root)
0
Lee Reitz