Laden...

JsonConvert.DeserializeObject mit <List<Type>> nicht möglich. Alternativen?

Erstellt von 1337-Cat vor 4 Jahren Letzter Beitrag vor 4 Jahren 3.270 Views
1
1337-Cat Themenstarter:in
7 Beiträge seit 2019
vor 4 Jahren
JsonConvert.DeserializeObject mit <List<Type>> nicht möglich. Alternativen?

Hallo,

ich hatte eine Klasse mit Getter und Setter erstellt und konnte einen JSON-String problemlos in eine Liste des Typs parsen.

List<MyClass> t = JsonConvert.DeserializeObject<List<MyClass>>(JSONstr);

.

Nun wollte ich die Methode kompatible zu mehreren Klassen machen. Versucht habe ich

var x = Type.GetType(tableType.GetType().ToString()); //tableType ist vom typ Type und teilt mit, in welche art von List<> der JSON-String geparst werden soll
List<Type> t = JsonConvert.DeserializeObject<List<Type>>(JSONstr);

Bekomme aber folgende Fehlermeldung:> Fehlermeldung:

Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Type' because the type requires a JSON string value to deserialize correctly.
To fix this error either change the JSON to a JSON string value or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.

Also kommt es nicht so ganz mit dem Typ List<Type> klar.

656 Beiträge seit 2008
vor 4 Jahren

Type ist auch ein Typ vom Framework, und nicht dein konkreter Typ. Vermutlich willst du eher eine generische Methode, wo tableType als Typ-Parameter reinkommt und du am Ende JsonConvert.DeserializeObject<List<TTable>>(JSONstr) machen kannst.

1
1337-Cat Themenstarter:in
7 Beiträge seit 2019
vor 4 Jahren

Type ist auch ein Typ vom Framework, und nicht dein konkreter Typ. Vermutlich willst du eher eine generische Methode, wo tableType als Typ-Parameter reinkommt und du am Ende JsonConvert.DeserializeObject<List<TTable>>(JSONstr) machen kannst.

Okay, danke. Ist das das C#-Pentant zu C++-Templates?
Weißt du vielleicht, wo ich das passende dazu nachlesen kann?

T
2.219 Beiträge seit 2008
vor 4 Jahren

@1337-Cat
Du kannst dies im einfachsten Fall lösen, in dem du dir eine Helper Klasse anlegst, die dann Wrapper Methoden für die De-/Serialisierung mit generics anbieten.
Diese besteht dann nur aus den zwei Methoden und dann kannst du per Typangabe den Typen festlegen.

Generics

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

1
1337-Cat Themenstarter:in
7 Beiträge seit 2019
vor 4 Jahren

In etwa so?

class GenericJSON<T>
    {
        public List<T> parse(string JSONstr)
        {
            return JsonConvert.DeserializeObject<List<T>>(JSONstr);
        }
    }

Nun weiß ich immernoch nicht, wie ich beim Aufruf einen erst zur Laufzeit bekannten Datentyp der Methode mitteilen kann.

src.GenericJSON<???> parser = new src.GenericJSON<???>();
var results = parser.parse(JSONstr);

Weiß lediglich nur, wie ich mit Type = typeof() oder GetType() Informationen zu einem Datentyp speichern kann. Aber diese werden nicht, wo "???" steht akzeptiert.

T
2.219 Beiträge seit 2008
vor 4 Jahren

@1337-Cat
Wieso sollten deine Datentypen erst zur Laufzeit bekannt sein bzw. warum willst du auch eine Liste von Typen übergeben?
So wirst du auch keine Daten von/in Json konvertieren.

Du willst immer nur Daten von Json in einen expliziten Typen konvertieren.
Json hat genau wie Xml und dein Code immer ein fixes Schema nach dem er aufgebaut ist.
Du kannst nicht der Serialisierung sagen, dass du hier eine Liste von X Typen hast aus der Json String geparst werden soll.
Du musst für jeden Typen einzeln eine De-/Serialisierung vornehmen.

Hier musst du dann auch den expliziten Typen, der aus dem Json generiert werden soll, angeben.
Der Json String wird dann auch auf deinen Typen gemappt.

Als Typen deiner Generic Methode musst du dann List<MyClass> hingeben, damit du auch eine Liste<MyClass> als Typen zurück bekommst.
Oder wie stellst du dir den Ablauf vor?


public class JsonHelper
{
    public static T Deserialize<T>(string json)
    {
        return JsonConvert.DeserializeObject<T>(json);
    }

    public static string Serialize(object obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
}

// Beispiel Aufrufe
List<MyClass> liste = new List<MyClass>();
string json = JsonHelper.Serialize(liste);
liste = JsonHelper.Deserialize<List<MyClass>>(json);

Nachtrag:
Falls du beim deserialisieren wirklich nicht den Typen kennst, dann musst du die Deserialize Methode mit Überladung mit Type parameter nutzen.

Link

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

1
1337-Cat Themenstarter:in
7 Beiträge seit 2019
vor 4 Jahren

@T-Virus

In der Anwendung sollen Datenbanken abgeglichen werden und je nach Tabelle eine Liste von Objekten derdazugehörigen Klasse erstellt werden. Aber welcher Typ von Objekten das ist, wird erst zur Laufzeit bestimmt, abhängig davon, welche Tabellen abgeglichen werden.

Habs in etwa so gelöst, was aber auch nicht gerade schön ist.

switch (comboBox_select_table.SelectedIndex)
                {
                    case 0:
                        tableSelect = "artikel";
                        src.GenericJSON<Artikel> parser = new src.GenericJSON<Artikel>();
                        var test = parser.parse(JSONstr);
                        break;
                    case 1: tableSelect = "stueckliste";
                        src.GenericJSON<Stueckliste> p2 = new src.GenericJSON<Stueckliste>();
                        var test2 = p2.parse(JSONstr);
                        break;
                    case 2: tableSelect = "kunde";
                        src.GenericJSON<Kunde> p3 = new src.GenericJSON<Kunde>();
                        var test3 = p3.parse(JSONstr);
                        break;
                }
T
2.219 Beiträge seit 2008
vor 4 Jahren

@1337-Cat
Du hättest auch die static Variante nehmen können, dann musst du nur den Typen beim Deserialisieren an die Methode mitgeben.
Oder du rufst direkt nach dem Instanzieren die Parse Methode auf.


var text = new src.GenericJSON<Artikel>().parse(JSONstr);

Ich sehe hier auch Magic Numbers in deinem Code.
Nutze am besten ein Enum für deine ComboBox Werte, damit du eine einheitliche Basis hast.
Wenn einmal case 0 Artikel ist und dann einmal Kunde, welcher Wert stimmt dann?
Durch ein Enum hast du Sicherheit was der Wert bedeutet.

Nachtrag:
In deinem aktuellen Code werden die Daten zwar deserialisiert, aber dann passiert nichts mit den var test1-3.
Werden diese bei dir nur auf Gültigkeit geprüft oder passiert hier noch was?
Für Gültigkeit wäre eher ein Schema zuständig.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

1
1337-Cat Themenstarter:in
7 Beiträge seit 2019
vor 4 Jahren

Du hättest auch die static Variante nehmen können, dann musst du nur den Typen beim Deserialisieren an die Methode mitgeben.

Das war auch mein erster Plan. Aber wie soll ich das Übergeben des Datentyps dznamisch gestalten?

In deinem aktuellen Code werden die Daten zwar deserialisiert, aber dann passiert nichts mit den var test1-3.
Werden diese bei dir nur auf Gültigkeit geprüft oder passiert hier noch was?
Für Gültigkeit wäre eher ein Schema zuständig.

Habe diese bis jetzt nur auf Korrektheit geprüft, und da stimmt alles so weit.

16.806 Beiträge seit 2008
vor 4 Jahren

Bei der Serialisierung des Json kannst Du das TypeNameHandling angeben.
Dann wird der konkrete Typ durch Json.NET anhand von Metadaten im Json mit angeben.

Und dann kannst Du mit simplen OOP Basics wie Interfaces arbeiten.
Alternativ musst Du bei der Deserialisierung manuell den Typ bekannt machen (durch eigene Werte oder durch Schnittstelleninformationen).

Json ist nun mal kein Typsicherer Datenaustauschkanal; fokussiert also diesen Anwendungsfall gar nicht.
Wenn Du sowas willst, dann musst Du das Protokoll wechseln (zB auf GRPC) oder Du musst Deine Kommunikationsarchitektur anpassen.

1
1337-Cat Themenstarter:in
7 Beiträge seit 2019
vor 4 Jahren

Hab es irgendwie gelöst bekommen. Undzwar schreibe ich die Ergebnisse der Liste in eine DataTable. Nicht elegant, aber es funktioniert