wake-up-neo.com

Json.NET Custom JsonConverter mit Datentypen

Ich bin auf einen Dienst gestoßen, der JSON im folgenden Format ausgibt:

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

Datentypnamen werden als Eigenschaftsnamen beibehalten und ihre Werte sind die eigentlichen Objekte. Sie beginnen alle mit einem data.-Präfix.

Was ich danach gerne bekommen würde, ist ungefähr so:

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

mit folgenden Klassen:

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

Frage

Wie kann ich dies in hinzugefügte C # -Klassen deserialisieren, die Datentypen berücksichtigen und aus dem Baum entfernen?

Ich habe es mit einer benutzerdefinierten JsonConverter versucht, aber ich habe Schwierigkeiten mit der dynamischen Auswahl des Konverters, da der einfachste Weg wäre, ein Attribut auf die Eigenschaft zu setzen, dies wird jedoch nicht unterstützt.

Ein kleines Beispiel wäre toll.

9
Jaka Konda

Dieses JSON-Format ist zwar etwas ungewöhnlich und widersteht der Verwendung von Attributen aufgrund der dynamischen Eigenschaftennamen, es ist jedoch immer noch möglich, einen JsonConverter mit einer kleinen Änderung in die bevorzugte Klassenstruktur zu deserialisieren: Ich würde empfehlen, die Children-Eigenschaft in zu ändern Die Root-Klasse soll ein ICollection<object> sein, um die Children-Eigenschaft in der Type1-Klasse zu spiegeln. So wie es jetzt ist, stimmt es nicht mit der Struktur der gewünschten Ausgabe überein (wobei Children als Array und nicht als Objekt angezeigt wird) und würde andernfalls zusätzlichen Code im Konverter für die korrekte Verarbeitung erfordern.

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

Hier ist was ich für den Konverter gefunden habe (vorausgesetzt die oben genannte Änderung wird vorgenommen): 

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

Mit diesem Konverter können Sie Ihre Klassen folgendermaßen deserialisieren:

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

Sie können dann wie folgt in das neue Format serialisieren:

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

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

Geige: https://dotnetfiddle.net/ESNMLE

8
Brian Rogers

Sie sind sich nicht sicher, ob dies funktioniert, aber haben Sie mit Newtonsoft.Json versucht, das Objekt zu serialisieren und JsonProperty-Tags in die Klasseneigenschaften aufzunehmen? Ich weiß, dass dies funktionieren wird, wenn Json in eine Klasse deserialisiert wird.

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

Um mit Newtonsoft zu serialisieren, fügen Sie zuerst Newtonsoft in das Projekt ein

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