Daten speichern und laden mit JSON

    • VB.NET

    Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      Daten speichern und laden mit JSON

      Hi,

      in diesem Tutorial möchte ich Euch das Arbeiten mit JSON-Dateien näher bringen. JSON ist die Abkürzung für JavaScript Object Notation. JSON ist ähnlich zu XML und dient dem Zweck, Daten strukturiert und lesbar zu speichern. Die meisten Webanwendungen nutzen JSON, um Daten zu versenden, bzw. zu empfangen. Auch .NET hat Klassen, um mit JSON zu arbeiten. Jedoch gibt es eine 3rd-Party LIbrary, die ich bevorzugt einsetze newtonsoft.com/json Dieses Tutorial basiert auf dieser Library.

      Wie sieht eine JSON-Datei aus?

      Quellcode

      1. {
      2. "Color": "Black",
      3. "Engine": {
      4. "CylinderCount": 6,
      5. "FuelType": 1
      6. },
      7. "Brand": "BMW",
      8. "Wheels": [
      9. {
      10. "Brand": "Dunlop"
      11. },
      12. {
      13. "Brand": "Dunlop"
      14. },
      15. {
      16. "Brand": "Dunlop"
      17. },
      18. {
      19. "Brand": "Dunlop"
      20. }
      21. ]
      22. }


      Dies ist die Abbildung einer Car-Instanz in JSON, welche in VB so aussieht.

      VB.NET-Quellcode

      1. Public Class Car
      2. Public Property Color As Color
      3. Public Property Engine As Engine
      4. Public Property Brand As String
      5. Public Property Wheels As Wheel()
      6. Public Sub New(color As Color, engine As Engine, brand As String, wheels As Wheel())
      7. Me.Color = color
      8. Me.Engine = engine
      9. Me.Brand = brand
      10. Me.Wheels = wheels
      11. End Sub
      12. End Class
      13. Public Class Wheel
      14. Public Brand As String
      15. Public Sub New(brand As String)
      16. Me.Brand = brand
      17. End Sub
      18. End Class
      19. Public Class Engine
      20. Public Property CylinderCount As Integer
      21. Public Property FuelType As FuelTypes
      22. Public Sub New(cylinderCount As Integer, fuelType As FuelTypes)
      23. Me.CylinderCount = cylinderCount
      24. Me.FuelType = fuelType
      25. End Sub
      26. End Class
      27. Public Enum FuelTypes
      28. Diesel = 0
      29. Gas = 1
      30. Electric = 2
      31. End Enum


      Wie in der JSON-Datei zu sehen ist, hat die Car-Instanz die Property Color, welche mit Blackfestgelegt wurde, eine Engine-Instanz, welche selbst wieder ein komplexer Typ ist (zu erkennen an den geschweiften Klammern), eine Property Brand vom Typ Stringmit dem Wert BMW und ein Array vom Typ Wheel (ein Array ist mit eckigen Klammern gekennzeichnet). Die Color-Property ist vom Typ Color, wird aber als String in der JSON-Datei gespeichert.

      Die Grundlegende Schreibweise ist:

      "Property": "Wert"

      Wobei Werte vom Typ String mit Anführungszeichen und numerische Werte ohne geschrieben werden. Die jeweiligen "Property": "Wert" Paare werden durch Kommas voneinander getrennt. Der Speichervorgang einer JSON-Datei wird Serializeund der Lesevorgang Deserialize genannt.

      Soweit zur Theorie, nun zur Praxis...

      Um Newtonsoft.JSON im Projekt nutzen zu können, ist es notwendig, die Library zu importieren...







      Zusätzlich muss der Namespace importiert werden.

      VB.NET-Quellcode

      1. Imports Newtonsoft.Json



      Der Serialisierungsvorgang

      VB.NET-Quellcode

      1. Imports Newtonsoft.Json
      2. Imports System.IO
      3. Public Class Form1
      4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      5. Dim wheels1 As Wheel() = {New Wheel("Dunlop"), New Wheel("Dunlop"), New Wheel("Dunlop"), New Wheel("Dunlop")}
      6. Dim engine1 As New Engine(6, FuelTypes.Gas)
      7. Dim car1 As New Car(Color.Black, engine1, "BMW", wheels1)
      8. Dim json As String = JsonConvert.SerializeObject(car1)
      9. File.WriteAllText("car1.json", json)
      10. End Sub
      11. End Class


      Zu sehen ist die Erzeugung eines Arrays vom Typ Wheel, eine Instanz vom Typ Engine und die Car-Instanz selbst. Der Aufruf vom JsonConvert.SerializeObject(car1) gibt einen String zurück, welcher genau dem am Anfang gezeigten entspricht. Dieser String kann nun in eine Datei geschrieben werden. Die Instanz ist somit serialisiert.


      Der Deserialisierungsvorgang

      VB.NET-Quellcode

      1. Imports Newtonsoft.Json
      2. Imports System.IO
      3. Public Class Form1
      4. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
      5. Dim json As String = File.ReadAllText("car1.json")
      6. Dim car1 As Car = JsonConvert.DeserializeObject(Of Car)(json)
      7. End Sub
      8. End Class


      Der zuvor gespeicherete JSON-String wird in die Variable json eingelesen. Nun folgt der Aufruf von JsonConvert.DeserializeObject(Of Car)(json). Dies ist eine generische Funktion, zu erkennen an dem Schlüsselwort Of. Hier muss der Typ angegeben werden, welcher am Ende des Deserialiserungsvorgang herauskommen soll. Passen die JSON-Daten nicht auf diesen Typ, wird eine Ausnahme ausgelöst. Dies alles geschieht intern in der Newtonsoft.JSON-Library. War der Vorgang erfolgreich, so habt ihr das zuvor gespeicherte Objekt wiederhergestellt. Der Deserialisierungsvorgang ist beendet.


      Serialisieren/Deserialisieren von Listen/Arrays

      VB.NET-Quellcode

      1. Imports Newtonsoft.Json
      2. Imports System.IO
      3. Public Class Form1
      4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      5. Dim wheels1 As Wheel() = {New Wheel("Dunlop"), New Wheel("Dunlop"), New Wheel("Dunlop"), New Wheel("Dunlop")}
      6. Dim engine1 As New Engine(6, FuelTypes.Gas)
      7. Dim car1 As New Car(Color.Black, engine1, "BMW", wheels1)
      8. Dim wheels2 As Wheel() = {New Wheel("Bridgestone"), New Wheel("Bridgestone"), New Wheel("Bridgestone"), New Wheel("Bridgestone")}
      9. Dim engine2 As New Engine(0, FuelTypes.Electric)
      10. Dim car2 As New Car(Color.White, engine2, "Tesla", wheels2)
      11. Dim wheels3 As Wheel() = {New Wheel("Continental"), New Wheel("Continental"), New Wheel("Continental"), New Wheel("Continental")}
      12. Dim engine3 As New Engine(4, FuelTypes.Diesel)
      13. Dim car3 As New Car(Color.Blue, engine3, "Mazda", wheels3)
      14. Dim carList As New List(Of Car) From {car1, car2, car3}
      15. Dim json As String = JsonConvert.SerializeObject(carList)
      16. File.WriteAllText("cars.json", json)
      17. End Sub
      18. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
      19. Dim json As String = File.ReadAllText("cars.json")
      20. Dim carList As List(Of Car) = JsonConvert.DeserializeObject(Of List(Of Car))(json)
      21. End Sub
      22. End Class


      Erzeugt werden 3 Car-Instanzen, welche in eine Liste gepackt werden. Zum serialisieren wird nun einfach die Liste carList an die Funktion JsonConvert.SerializeObject(carList) übergeben und der erzeugte JSON-String gespeichert. Um nun wieder eine Liste zurückzubekommen, ändert man in der generischen Funktion JsonConvert.DeserializeObject(Of List(Of Car))(json) den Zieltyp auf List(Of Car).

      Die erzeugten JSON-Daten sehen so aus:

      Quellcode

      1. [
      2. {
      3. "Color": "Black",
      4. "Engine": {
      5. "CylinderCount": 6,
      6. "FuelType": 1
      7. },
      8. "Brand": "BMW",
      9. "Wheels": [
      10. {
      11. "Brand": "Dunlop"
      12. },
      13. {
      14. "Brand": "Dunlop"
      15. },
      16. {
      17. "Brand": "Dunlop"
      18. },
      19. {
      20. "Brand": "Dunlop"
      21. }
      22. ]
      23. },
      24. {
      25. "Color": "White",
      26. "Engine": {
      27. "CylinderCount": 0,
      28. "FuelType": 2
      29. },
      30. "Brand": "Tesla",
      31. "Wheels": [
      32. {
      33. "Brand": "Bridgestone"
      34. },
      35. {
      36. "Brand": "Bridgestone"
      37. },
      38. {
      39. "Brand": "Bridgestone"
      40. },
      41. {
      42. "Brand": "Bridgestone"
      43. }
      44. ]
      45. },
      46. {
      47. "Color": "Blue",
      48. "Engine": {
      49. "CylinderCount": 4,
      50. "FuelType": 0
      51. },
      52. "Brand": "Mazda",
      53. "Wheels": [
      54. {
      55. "Brand": "Continental"
      56. },
      57. {
      58. "Brand": "Continental"
      59. },
      60. {
      61. "Brand": "Continental"
      62. },
      63. {
      64. "Brand": "Continental"
      65. }
      66. ]
      67. }
      68. ]


      Die eckige Klammer am Anfang gibt zu erkennen, dass es sich hier um ein Liste, bzw. Array handelt.


      Erzeugen von Klassen für nicht selbsterstellte JSON-Daten

      Hat man einen fremden JSON-String und will man mit diesen Daten arbeiten, so bietet es sich an, sich Klassen für diese Daten erzeugen zu lassen. Ich nutze dazu diesen Service https://jsonutils.com/ und diese Testdaten developer.mozilla.org/en-US/do…n/JavaScript/Objects/JSON Die JSON-Daten werden kopiert und bei jsonutils eingefügt. Auf Basis der Daten werden nun Klassen generiert, welche man in sein Projekt einfügen kann. Lediglich sollte man dem Rootobject auf der jsonutils-Seite Example genannt einen anderen Namen geben. Ich lege diesen auf SuperheroSquad fest und speichere den JSON-String in einer Datei superhero.json. Die JSON-Daten und die generierten Klassen sehen so aus:

      Quellcode

      1. {
      2. "squadName": "Super hero squad",
      3. "homeTown": "Metro City",
      4. "formed": 2016,
      5. "secretBase": "Super tower",
      6. "active": true,
      7. "members": [
      8. {
      9. "name": "Molecule Man",
      10. "age": 29,
      11. "secretIdentity": "Dan Jukes",
      12. "powers": [
      13. "Radiation resistance",
      14. "Turning tiny",
      15. "Radiation blast"
      16. ]
      17. },
      18. {
      19. "name": "Madame Uppercut",
      20. "age": 39,
      21. "secretIdentity": "Jane Wilson",
      22. "powers": [
      23. "Million tonne punch",
      24. "Damage resistance",
      25. "Superhuman reflexes"
      26. ]
      27. },
      28. {
      29. "name": "Eternal Flame",
      30. "age": 1000000,
      31. "secretIdentity": "Unknown",
      32. "powers": [
      33. "Immortality",
      34. "Heat Immunity",
      35. "Inferno",
      36. "Teleportation",
      37. "Interdimensional travel"
      38. ]
      39. }
      40. ]
      41. }


      VB.NET-Quellcode

      1. Public Class Member
      2. Public Property name As String
      3. Public Property age As Integer
      4. Public Property secretIdentity As String
      5. Public Property powers As String()
      6. End Class
      7. Public Class SuperheroSquad
      8. Public Property squadName As String
      9. Public Property homeTown As String
      10. Public Property formed As Integer
      11. Public Property secretBase As String
      12. Public Property active As Boolean
      13. Public Property members As Member()
      14. End Class


      Um die JSON-Daten auf die Klassen zu mappen genügt es, das bereits gelernte anzuwenden:

      VB.NET-Quellcode

      1. Imports Newtonsoft.Json
      2. Imports System.IO
      3. Public Class Form1
      4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      5. Dim json As String = File.ReadAllText("superheroes.json")
      6. Dim squad As SuperheroSquad = JsonConvert.DeserializeObject(Of SuperheroSquad)(json)
      7. End Sub
      8. End Class



      Abweichende Property-Namen

      Der Deserialisierungsvorgang kann nur korrekt ablaufen, wenn die Property-Namen in den VB-Klassen auf die Property-Namen in den JSON-Daten passen. Sonst schlägt der Vorgang fehl. Basierend auf dem SuperheroSquad-Beispiel ändere ich in der Klasse Member die Property secretIdentity auf secretName

      VB.NET-Quellcode

      1. Public Class Member
      2. Public Property name As String
      3. Public Property age As Integer
      4. Public Property secretName As String
      5. Public Property powers As String()
      6. End Class
      7. Public Class SuperheroSquad
      8. Public Property squadName As String
      9. Public Property homeTown As String
      10. Public Property formed As Integer
      11. Public Property secretBase As String
      12. Public Property active As Boolean
      13. Public Property members As Member()
      14. End Class




      Wie zu sehen ist, hat secretName keine Wert. Es wurde während des Deserialisierungsvorgangs keine Ausnahme ausgelöst, sondern die fehlende Property einfach ignoriert. Es sollte stets geprüft werden, ob die erzeugten Klassen auch wirklich auf die JSON-Daten passen. Die Klassenerzeugung auf jsonutils ist nicht immer 100% korrekt.

      Um dies nun zu beheben, nimmt man sich Attribute zur Hilfe.

      VB.NET-Quellcode

      1. Public Class Member
      2. Public Property name As String
      3. Public Property age As Integer
      4. <JsonProperty("secretIdentity")>
      5. Public Property secretName As String
      6. Public Property powers As String()
      7. End Class
      8. Public Class SuperheroSquad
      9. Public Property squadName As String
      10. Public Property homeTown As String
      11. Public Property formed As Integer
      12. Public Property secretBase As String
      13. Public Property active As Boolean
      14. Public Property members As Member()
      15. End Class


      Der Property secretName wurde das Attribut <JsonProperty("secretIdentity")> hinzugefügt. Nun wird wieder korrekt gemappt. Bei Deserialisierungsvorgang wird das Attribut, falls vorhanden, berücksichtigt.



      Abweichende Property-Namen sind somit kein Problem.


      Arbeiten mit JSON-Daten ohne Mapping auf Klassen

      Falls man keine VB-Klassen für die JSON-Daten erzeugen möchte, gibt es folgende Möglichkeit (basierend auf SuperheroSquad)

      VB.NET-Quellcode

      1. Imports Newtonsoft.Json.Linq
      2. Imports Newtonsoft.Json
      3. Imports System.IO
      4. Public Class Form1
      5. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      6. Dim json As String = File.ReadAllText("superheroes.json")
      7. Dim data As JObject = DirectCast(JsonConvert.DeserializeObject(json), JObject)
      8. Dim squadName As JToken = data("squadName")
      9. Dim members As JToken = data("members")
      10. Debug.Print(squadName.ToString())
      11. For Each x As JToken In members
      12. Debug.Print(x("secretIdentity").ToString() & " " & x("age").ToString())
      13. Next
      14. End Sub
      15. End Class


      Man nutzt DirectCast(JsonConvert.DeserializeObject(json), JObject) um den JSON-String zu parsen. Diese Funktion gibt den Typ Object zurück und sollte daher auf den Typ JObject gecasted werden. Dieses JObject enthält JTokens, welche die einzelnen Properties mit ihren Werten darstellen. Hier aufpassen, es wird der Newtonsoft.Json.Linq-Namespace benötigt. Aus meiner Sicht ist diese Vorgehensweise nicht die eleganteste, aber bietet sich an, wenn man nur einen bestimmten Wert in einer JSON-Datei benötigt. Ich bevorzuge das Mapping auf Klassen.


      .NET Implementierung von JSON

      Wer keine externe LIbrary benutzen möchte, dem bietet das .NET-Framework eine eigene Möglichkeit, mit JSON-Daten zu arbeiten. Benötigt wird ein Verweis auf das .NET-Assembly System.Web.Extensions und der Import des System.Web.Script.Serialization-Namespaces.



      Der Serialisierungs- Deserialisierungsvorgang ist fast der selbe wie bei Newtonsoft.Json.

      VB.NET-Quellcode

      1. Imports System.Web.Script.Serialization
      2. Imports System.IO
      3. Public Class Form1
      4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      5. Dim json As String = File.ReadAllText("superheroes.json")
      6. Dim jsonSerializer As New JavaScriptSerializer()
      7. Dim squad As SuperheroSquad = jsonSerializer.Deserialize(Of SuperheroSquad)(json)
      8. json = jsonSerializer.Serialize(json)
      9. End Sub
      10. End Class
      11. Public Class Member
      12. Public Property name As String
      13. Public Property age As Integer
      14. Public Property secretIdentity As String
      15. Public Property powers As String()
      16. End Class
      17. Public Class SuperheroSquad
      18. Public Property squadName As String
      19. Public Property homeTown As String
      20. Public Property formed As Integer
      21. Public Property secretBase As String
      22. Public Property active As Boolean
      23. Public Property members As Member()
      24. End Class


      Auch hier gibt es die Möglichkeit, untypisiert zu arbeiten, hierauf werde ich aber nicht näher eingehen, da mir hier selbst die Erfahrung fehlt und ich die interne Implementierung nicht nutze. Falls Bedarf besteht, werde ich mich damit befassen.

      Dieses Thema ist hier nicht erschöpft behandelt worden, jedoch sollte eine grundlegende Übersicht geschaffen worden sein, wie mit JSON-Daten gearbeitet werden kann. Sollten Fehler in meinen Ausführungen sein, so bitte ich um Korrektur. Ich bedanke mich fürs Lesen!

      Danny!
      Bilder
      • extensions.png

        122,48 kB, 1.915×1.079, 512 mal angesehen
      Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
      Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Marcus Gräfe“ ()

      Zu: Arbeiten mit JSON-Daten ohne Mapping auf Klassen

      Ich hab neulich einen Json-Editor gebastelt (auch mit NewtonSoft). Dabei lernte ich die JToken.Parse()-Methode kennen. Die liest json direkt ein, analog wie auch ein XmlDocument xml direkt liest - also ohne dass ein deserialisiertes (Zwischen-)Objekt erstellt würde.
      Statt

      SpaceyX schrieb:


      VB.NET-Quellcode

      1. Dim json As String = File.ReadAllText("superheroes.json")
      2. Dim data As JObject = DirectCast(JsonConvert.DeserializeObject(json), JObject)


      könnte es heissen:

      VB.NET-Quellcode

      1. Dim json As String = File.ReadAllText("superheroes.json")
      2. Dim data As JObject = DirectCast(JToken.Parse(json), JObject)

      Dabei muss man aber doch bischen was beachten, weil gültiges Json hat als Root-Token nicht immer ein JObject, sondern kann auch ein JArray als Root haben.
      Also dieses wäre auch gültiges Json, und sein Root ist ein Array (erkennbar an den umschliessenden []):

      Quellcode

      1. [
      2. {
      3. "name": "Molecule Man",
      4. "age": 29,
      5. "secretIdentity": "Dan Jukes",
      6. "powers": [
      7. "Radiation resistance",
      8. "Turning tiny",
      9. "Radiation blast"
      10. ]
      11. },
      12. {
      13. "name": "Madame Uppercut",
      14. "age": 39,
      15. "secretIdentity": "Jane Wilson",
      16. "powers": [
      17. "Million tonne punch",
      18. "Damage resistance",
      19. "Superhuman reflexes"
      20. ]
      21. },
      22. {
      23. "name": "Eternal Flame",
      24. "age": 1000000,
      25. "secretIdentity": "Unknown",
      26. "powers": [
      27. "Immortality",
      28. "Heat Immunity",
      29. "Inferno",
      30. "Teleportation",
      31. "Interdimensional travel"
      32. ]
      33. }
      34. ]
      Solch müsste man dann folgendermassen Parsen:

      VB.NET-Quellcode

      1. Dim data As JArray = DirectCast(JToken.Parse(jsonString), JArray)

      Deserialisieren kann man es (unter Bezug auf obige Beispielklassen) in ein Member-Array, oder auch in eine List(Of Member)

      VB.NET-Quellcode

      1. Dim members = JsonConvert.DeserializeObject(Of List(Of Member))(jsonString)

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