Fehler beim Deserialisieren von JSON - System.InvalidCastException

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 59 Antworten in diesem Thema. Der letzte Beitrag () ist von siycah.

    DTF schrieb:

    Nein so ist es nicht ganz.


    Du hast natürlich Recht, dass die Semantik nicht identisch ist.

    Ich habe in diesem Fall nicht den Sinn gesehen, da so in die Tiefe zu steigen, denn am Ende des Tages ist es für die IL nichts anderes, als ein Pointer mit ein paar speziellen Regeln.
    Nichtsdestotrotz hast du Recht.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Ich hab letztens mit dem Studio-Konverter für XML-Dateien große Erfolge gefeiert.
    Mit Deinem JSon-Text sollte das genau so gut gehen (egal, ob C# oder VB):
    Leere Klasse anlegen,
    den JSon-Text in die Zwischenablage kopieren,
    in die leere Klasse klicken,
    Studio-Bearbeiten => Inhalte einfügen
    und (fast) feddich.

    Ich hab dann mit der Klasse so lange rumgespielt, bis die von ihr gespeicherte Datei äquivalent zur Vorlage in der Zwischenablage war.
    Bei mir in XML, bei Dir in JSon.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Studio-Konverter


    Das ist tatsächlich ein sehr interessantes Feature. Wusste nicht, dass es das gibt.
    QuickType wird aber glaube ich mein Favorit bleiben. Ich nutze VS mittlerweile sehr wenig. Eigentlich nur für UI Sachen. Den Rest mache ich in VS Code (vor allem da ich eigentlich nur unter/für Linux entwickle).
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)

    siycah schrieb:

    Das ist tatsächlich ein sehr interessantes Feature.
    Ich hab da ein Makro-Bildverarbeitungs-Projekt auf Arbeit, da haben schon Generationen von Entwicklern Hand dran gelegt.
    Die Makros sind XML-Dateien, die mit nem XmlReader eingelesen und "interpretiert" werden.
    Als @VaporiZed mir dieses Studio-Feature offenbarte, Klassenstruktur zu einer XML Datei gesucht
    habe ich über eine Woche an der XML-Struktur gedreht, bis ich mit der Klassenstruktur zufrieden war.
    Jetzt muss ich zwar die alten XML-Dateien updaten, aber danach ging alles wesentlich einfacher.
    ==================
    @kafffee Bei mir gibt es Integer, Double, Boolean und String-Parameter
    sowie
    Integer-Array, Double-Array, Boolean-Array und String-Array-Parameter.
    Die wurden inhaltlich alle gleich behandelt, deswegen das "Interpretieren".
    Ich hab mir iterativ eine geeignete XML-Vorlage generiert, wo alles vorkam, und als die so geschrieben wurde wie die Vorlage aussah, war ich fertig.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich hab ma TestApp gemacht für bisserl gründlichere Untersuchigung.
    Da sind alle 3 Jsons enthalten (post#1, post#37, post#16), und alle 3 generierten Datenstrukturen.
    man sieht, dass post#1 eine Untermenge von post#37 zu sein scheint - also die Datenstruktur von #37 ist eiglich dieselbe, aber einige Klassen haben noch mehr properties.
    generiert habich die Classes mit json2csharp.com/
    Zur besseren Laufzeit-Unterstützung der Class-Viewer habich bei einigen Properties in den Classes das [TypeConverter(typeof(ExpandableObjectConverter))] - Attribut spendiert.
    Ansonsten habich nix rumgefummelt an den Klassen.
    wie du deserialisiert hast mit json2csharp?
    nach Schema F:

    VB.NET-Quellcode

    1. Dim root1 = JsonConvert.DeserializeObject(Of Post1.Root)(File.ReadAllText("..\..\Post1.json"))
    2. Dim root16 = JsonConvert.DeserializeObject(Of Post16.Root)(File.ReadAllText("..\..\Post16.json"))
    3. Dim root37 = JsonConvert.DeserializeObject(Of Post37.Root)(File.ReadAllText("..\..\Post37.json"))
    4. Dim QuickType37 = JsonConvert.DeserializeObject(Of QuickType.Welcome)(File.ReadAllText("..\..\Post37.json"))

    Also Post1, Post16, Post37 sind Namespaces im c#-Projekt, wo ich jeweils die von json2csharp.com/ generierten Classes eingepastet hab.
    Der Namespace QuickType wird ja eh generiert.



    Jeder Class-Generator fährt da bischen eigenen Stiefel:
    • json2csharp generiert eine Class Root
    • VisualStudion generiert eine Class RootObject (und kann die JsonProperty-Attribute nicht generieren)
    • quicktype generiert eine Class Welcome
      und noch einiges mehr, also Ansätze, wie man mit JsonSettings den Prozess (überaus vielfältig) beeinflussen kann.
      Leider macht er Fehler, also er generiert zwar einen JsonConverter für das Date-Dingsbums, aber versäumt, diesen Converter dann an die Property auch dran-zuattributieren
      Habich nachgebessert, dann gibts kein Laufzeitfehler.
    Wie gesagt habich hier und da ein [TypeConverter(typeof(ExpandableObjectConverter))] fallenlassen, zur Unterstützung meiner ObjectViewer.
    Also meine App deserialisiert verschiedene Jsonse in verschiedenen Varianten, und präsentiert jede als eigenes ObjectViewer-Form - wholeStory:

    VB.NET-Quellcode

    1. Imports Newtonsoft.Json, System.IO, System.Threading.Tasks
    2. Public Module modMain
    3. <STAThread>
    4. Public Sub Main(args As String())
    5. Dim root1 = JsonConvert.DeserializeObject(Of Post1.Root)(File.ReadAllText("..\..\Post1.json"))
    6. Dim root16 = JsonConvert.DeserializeObject(Of Post16.Root)(File.ReadAllText("..\..\Post16.json"))
    7. Dim root37 = JsonConvert.DeserializeObject(Of Post37.Root)(File.ReadAllText("..\..\Post37.json"))
    8. Dim QuickType37 = JsonConvert.DeserializeObject(Of QuickType.Welcome)(File.ReadAllText("..\..\Post37.json"))
    9. Application.EnableVisualStyles()
    10. Application.SetCompatibleTextRenderingDefault(False)
    11. ShowFormProps("QuickType37", QuickType37)
    12. ShowFormProps("Post#1", root1)
    13. ShowFormProps("Post#16", root16)
    14. ShowFormProps2("Post#37", root37)
    15. My.Settings.Save()
    16. End Sub
    17. Private Async Sub ShowFormProps(txt$, obj As Object)
    18. Await Task.Run(Sub() ShowFormProps2(txt, obj))
    19. End Sub
    20. Private Sub ShowFormProps2(txt$, obj As Object)
    21. Dim frm1 = New frmProps
    22. frm1.Text = txt
    23. frm1.PropertyGrid1.SelectedObject = obj
    24. frm1.ShowDialog()
    25. End Sub
    26. End Module


    Wenn mans kapiert hat, ist QuickType wohl am dollsten, eben weil man da auch lernen kann, wie man JsonSettings für eine bessere Typisierung basteln kann.
    Ansonsten erhält man ja nur String, Bool, Integer.

    Mit den JsonSettings unterstützt er auch Enums und löst sogar das lausige Problem, dass Date man korrekt angegeben wird, mal aber nur ein Integer (1994) ist.
    Die generierte Lösung ist zwar ziemlich komisch, aber auf ihre Weise pfiffig und lehrreich.
    Dateien
    • JsonClasses01.zip

      (35,14 kB, 160 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    quicktype generiert eine Class Welcome


    Das ist tatsächlich ein Fehler vom @kafffee, dass QuickType die Klasse so benannt hat.


    Oben gibt man einen Namen ein. So wird die Klasse am Ende heißen.

    Gibt man dort einen anderen Namen ein, wird die Klasse auch genau so heißen.


    ErfinderDesRades schrieb:

    Wenn mans kapiert hat, ist QuickType wohl am dollsten


    QuickType erlaubt noch sehr viel mehr als das.
    Auf der Arbeit (vornehmlich C++ Entwicklung), verwende ich JSON Schemata, die dann mit der TUI-Version von QuickType dynamisch (zur Makefile generation time) zu C++/C# Klassen konvertiert werden.

    Aber auch für Kundenprojekte meiner Firma verwende ich das. An QuickType habe ich bereits auch schon rumgebastelt :D
    Daher weiß ich, dass ich niemals TypeScript mögen werde :D
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    ah.
    Mir ist noch ein Fehler meinerseits aufgefallen: Besser ist, man nutzt die .FromJson() - Methode, also

    VB.NET-Quellcode

    1. Dim QuickType37 = QuickType.Welcome.FromJson(File.ReadAllText("..\..\Post37.json"))
    2. 'statt
    3. 'Dim QuickType37 = JsonConvert.DeserializeObject(Of QuickType.Welcome)(File.ReadAllText("..\..\Post37.json"))
    Dann braucht man nichts nachbessern mittm JsonConverter(typeof(xxxxxx))]-Attribut.



    siycah schrieb:

    An QuickType habe ich bereits auch schon rumgebastelt
    Wie jetzt - du hast da beim Quellcode der Site mitgewirkt?

    ErfinderDesRades schrieb:

    Wie jetzt - du hast da beim Quellcode der Site mitgewirkt?


    Siehe hier :)

    Es gab einen ziemlich nervigen "Bug" in dem C++-Teil den ich gefixt habe. Rohe Pointer für optionale Werte <X
    Bilder
    • WhatsApp Image 2024-01-02 at 17.49.09.jpeg

      107,06 kB, 921×2.048, 33 mal angesehen
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)

    ErfinderDesRades schrieb:

    nach Schema F:
    VB.NET-Quellcode
    Dim root1 = JsonConvert.DeserializeObject(Of Post1.Root)(File.ReadAllText("..\..\Post1.json"))
    Dim root16 = JsonConvert.DeserializeObject(Of Post16.Root)(File.ReadAllText("..\..\Post16.json"))
    Dim root37 = JsonConvert.DeserializeObject(Of Post37.Root)(File.ReadAllText("..\..\Post37.json"))
    Dim QuickType37 = JsonConvert.DeserializeObject(Of QuickType.Welcome)(File.ReadAllText("..\..\Post37.json"))


    Da ham wir ja schon den Übeltäter: Ich hab die falsche Überladung benutzt:

    VB.NET-Quellcode

    1. Dim AlbumObjekt As New MusicBrainzAlbumData.Root
    2. AlbumObjekt = CType(JsonConvert.DeserializeObject(ResultAlbumSearch), MusicBrainzAlbumData.Root)


    ErfinderDesRades schrieb:

    Wenn mans kapiert hat, ist QuickType wohl am dollsten,


    Yep zu dem Schluss bin ich auch gekommen.

    siycah schrieb:

    Das ist tatsächlich ein Fehler vom @kafffee, dass QuickType die Klasse so benannt hat.

    Ah jetzt ja, das hab ich völlig übersehen.

    Ich glaube ich kann den Thread jetzt zumachen.

    Vielen Dank wie immer an alle die mit rumgewerkelt haben :)
    na, du solltest noch meine TestApp runterladen - allein wegen des legendären GUIs, was mehrere Alternativen abklopft.
    Das QuickType-Gedöhns ist klar Favorit, besonders wenn mans benutzt wie gedacht (also post#48 beachten)

    Und im Nachgang habich jetzt auch die bekloppte QT-Date-Structure (teilweise) rausgeworfen, und durch richtiges DateTime ersetzt.
    Dazu habich einen eigenen DateTimeConverter eingebaut.
    Was nicht weiter schwer war - ich hab den QT-generierten Date-Konverter umkopiert und bisserl umgefrickelt.

    Man muss halt bedenken, dass generierte Json-Klassen das Ergebnis einer Heuristik sind, also es wird anhand der Json-Werte "geraten", was die Klassen-Property wohl für einen Datentyp haben mag.
    Und da wird auch mal falsch geraten, oder es kommt was eigentümliches bei raus, wie das jetzt mit der QT-Date-Structure


    Aber vielleicht gibt es zu den Json-Results der Web-Api, die du benutzst, ja auch Json-Schemata. Dann bräuchte man nicht mehr raten, sondern aus JSchema kann man die Klassen so generieren, wie sie vom Anbieter der Web-Api gemeint sind.
    Dateien
    • JsonClasses02.zip

      (35,89 kB, 156 mal heruntergeladen, zuletzt: )
    @ErfinderDesRades

    Yep runterladen kommt noch :)

    Diesen QT-eigenen Konverter hab ich auch schon probiert umzuschreiben, aber ohne Erfolg. Da wusste ich aber auch nicht dass im JSON statt einem DateTime ein Integer steckt, hab gedacht das wäre einfach eine falsche Culture...

    Ist dein eigener DateTime Konverter schon in deinem Testprogramm enthalten so wie du es hochgeladen hast, weil du ja schreibst "im Nachgang", das würde mich noch interessieren

    .
    Edit @ErfinderDesRades Ah ja, wahrscheinlich in JsonClasses02. zip?

    Edit @ErfinderDesRades

    Ah hab deinen Konverter gefunden. Den bau ich bei mir auch noch ein, dann geht im Falle eines Fehlers nicht der komplette Datensatz deswegen flöten...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    und denke daran, die Properties, die DateTime sind, auch so zu deklarieren.

    Guck dir die generierten Klassen sorgfältig an - auf die Schnelle finde ich folgendes komisch:

    C#-Quellcode

    1. public partial class Media {
    2. public Welcome[] Discs { get; set; }
    3. }
    4. public partial class Release {
    5. public string Disambiguation { get; set; } // an anderer Stelle gibts eine Disambiguation-Enumeration - also ent- oder weder.
    6. public Date Date { get; set; }// die komische QT-Date-Structure
    7. }
    8. public partial class Recording {
    9. public Disambiguation Disambiguation { get; set; }//die komische QT-Disambiguation-Enumeration
    10. public Date FirstReleaseDate { get; set; }// die komische QT-Date-Structure
    11. }
    12. public partial class ReleaseEvent {
    13. public Date Date { get; set; }// die komische QT-Date-Structure
    14. }
    15. public partial class Area {
    16. public string Disambiguation { get; set; }
    17. }
    18. // komische Dinge:
    19. public enum Disambiguation { Empty, Live }; // ist dieses Enum wirklich nötig??
    20. public partial struct Date { // dieses bestimmt nicht
    21. public DateTimeOffset? DateTime;
    22. public long? Integer;
    23. public static implicit operator Date(DateTimeOffset DateTime) => new Date { DateTime = DateTime };
    24. public static implicit operator Date(long Integer) => new Date { Integer = Integer };
    25. }

    ErfinderDesRades schrieb:

    auf die Schnelle finde ich folgendes komisch:


    Ich habe mir das gerade nochmal angeschaut und ich glaube ich kann dir erklären, woher das kommt. Unter Linux ist es üblich, den Timestamp (Unix Epoch) zu verwenden, dieser wird (eigentlich) immer als ​long übergeben.
    Vorstellbar wäre es, dass die diese Strukturen bauen, um eine Art POSIX-Kompatibilität zu bekommen.

    Wurde hier implementiert: github.com/glideapps/quicktype/pull/312/files
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    ich bin noch unüberzeugt.
    Weil wenn im Json "2008-04-01" steht, dann wird Date.DateTime befüllt und Date.Integer bleibt leer.
    Und wenn im Json "2008" steht, dann wird Date.Integer befüllt und Date.DateTime bleibt leer.

    Von einer Kompatiblitäts-Klasse täte ich Umrechnungs-Funktionalität erwarten.

    Auch Prop-Namen wie Recording.FirstReleaseDate deuten auf Datumse hin - nicht auf Timestamps

    ErfinderDesRades schrieb:

    Von einer Kompatiblitäts-Klasse täte ich Umrechnungs-Funktionalität erwarten.


    Hm, nun gut. Ich schaue mir das bei Gelegenheit mal an. Vielleicht kann ich den Fehler finden und fixen.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    guck dir auch meine Lösung an.
    Ich versteh dieses Date-Dingens nämlich so, dass er "2008" nicht als Date parsen kann - (obwohl das iso8601-konform ist).
    Daher denkt sich die QT-Heuristik: "Da steht ja mal ein Datum, mal ein Long drin - also bastel ich eine Spezial-Struct, wo Datum oder Long drin stehen kann."

    Also nix mit posix-Kompatiblität, sondern ein Fehler der Heuristik.

    Und guck dir auch das Json an - da gehts ganz bestimmt nicht um Timestamps (zumindest was ich darunter verstehe).

    Und meine Lösung besteht darin, dass ich eben händisch einen DateTimeJsonConverter bereitstelle, der "2008" korrekt als DateTime liest.
    Wäre natürlich cool, wenn du die QT-Heuristik ertüchtigen könntest, zu erkennen, dass wenn in einem Json-Feld mal "2008-02-02", mal "2008" auftaucht, dass dann ein iso8601-Datum-Feld vorliegt.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @ErfinderDesRades
    Hab jetzt einfach das json2csharp-erzeugte genommen, in VB übersetzt und die Datums auskommentiert. Somit keine Probleme, die Datums brauch ich nicht, und ich muss nicht den Umweg über eine DLL gehen, falls ich doch noch mal was ändern will...


    @ErfinderDesRades
    Aber nochmal ne andere Frage:
    Was ist hieran falsch?:

    VB.NET-Quellcode

    1. Private TrackList As New List(Of String)
    2. Private ReleaseList As New List(Of TrackList)


    Da sagt er mir in Zeile2: Der Typ TrackList ist nicht definiert? Wieso das denn?

    Ich möchte nämlich das hier machen. Ich möchte gleiche Datensätze TrackList nicht übernehmen in ReleaseList

    VB.NET-Quellcode

    1. For Each Release in AlbumObject.Releases
    2. TrackList.Clear()
    3. For Each item In AlbumContentObject.media(0).tracks
    4. TrackList.Add(item.title)
    5. Next
    6. If Not ReleaseList.Contains(TrackList) Then
    7. ReleaseList.Add(TrackList)
    8. End If
    9. Next


    Aber ich hab so im Gefühl, dass das auf diese Weise nicht klappen wird...

    Edit @ErfinderDesRades

    ah jetzt ja, mit bisschen Kreativität mit den Suchbegriffen bei Google hab ichs:

    VB.NET-Quellcode

    1. Private TrackList As New List(Of String)
    2. Private ReleaseList As New List(Of List(Of String))

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „kafffee“ ()

    @ErfinderDesRades Ich werde mir das auf jeden Fall mal anschauen.

    Ich will noch das Verhalten zwischen C# und C++ vergleichen. Da habe ich gesehen, dass der da immer nur Strings verwendet. Bin mir unsicher ob das so sein soll oder nicht. Der könnte nämlich ganz einfach die tm-Struktur nehmen. Die müsste es auch unter Windows geben.

    Gib mir ein paar Tage Zeit, ich schaue mal dass ich dazu komme.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)