Json auslesen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Json auslesen

    Für eine eigenes Projekt, wollte ich eine Quiz App erstellen, die mit
    einer MySQL Datenbank verbunden ist. Ich verstehe aber nicht ganz, wie ich auf die Daten zugreifen kann, um sie einem Label zuzuweisen.

    Meine Frage:
    Ich bekomme Daten mit

    1. let dataAsString = String(data: datas, encoding: .utf8)
    2. print(dataAsString)


    ausgegeben.

    Ausgabe:

    2018-12-08 13:34:05.696392+0100 jsonPflegonautTableView[90401:4382088] libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
    Optional("[{\"ID\":\"1\",\"Frage\":\"1\",\"Antwort1\":\"2\",\"Antwort2\":\"3\",\"Antwort3\":\"4\",\"Antwort4\":\"5\",\"Correct\":\"1\",\"Notiz\":\" 1234\",\"LernsektorID\":\"0\",\"LerneinheitID\":\"1\",\"LernbereichID\":\"1\",\"SchwierigkeitID\":\"1\",\"Redakteur\":\"\",\"created_at\":\"0000-00-00 00:00:00\"},{\"ID\":\"51\",\"Frage\":\" Welche der drei genannten Werte steuert den Atemantrieb?\",\"Antwort1\":\"pO2\",\"Antwort2\":\"pCO2\",\"Antwort3\":\"pH\",\"Antwort4\":\"K+\",\"Correct\":\"2\",\"Notiz\":\" Gesteuert wird die Atmung im wesentlichen durch das Gehirn beziehungsweise das Atemzentrum in der Medulla oblongata. Ausschlaggebend ist dabei die Reaktion von Chemorezeptoren auf den Kohlendioxid-Gehalt, beziehungsweise den Kohlendioxid-Partialdruck des Blutes. \\u00dcbersteigt dieser einen gewissen Schwellenwert, setzt der Atemreiz ein. Rezeptoren, die auf den pH-Wert des arteriellen Blutes sowie einen Sauerstoffmangel (Hypoxie) reagieren, haben nur eine zweitrangige Bedeutung als Atemreiz. \",\"LernsektorID\":\"3\",\"LerneinheitID\":\"0\",\"LernbereichID\":\"1\",\"SchwierigkeitID\":\"2\",\"Redakteur\":\"Valentin\",\"created_at\":\"15.03.2018 - 07:17\"},{\"ID\":\"52\",\"Frage\":\" sdfsdfg\",\"Antwort1\":\"adfg\",\"Antwort2\":\"asdf\",\"Antwort3\":\"asdf\",\"Antwort4\":\"asf\",\"Correct\":\"2\",\"Notiz\":\" Eselsbruecke\",\"LernsektorID\":\"1\",\"LerneinheitID\":\"1\",\"LernbereichID\":\"1\",\"SchwierigkeitID\":\"1\",\"Redakteur\":\"Valentin\",\"created_at\":\"30.10.2018 - 06:30\"}]")

    Aber irgendwie kann ich nicht auf die Daten im Dictionary zugreifen. Ich bekomme nur nil zurück. Ich glaube das liegt an dieser Zeile:
    1. let decoder = JSONDecoder()
    2. let downloadedQuestions = try decoder.decode([Question].self, from: datas)
    3. print(downloadedQuestions)
    Ausgabe:
    [jsonPflegonautTableView.Question(id: nil, frage: nil, antwort1: nil, antwort2: nil, antwort3: nil, antwort4: nil, korrekteAntwort: nil, notiz: nil, lernSektorId: nil, lernEinheitId: nil, lernBereichId: nil, schwierigkeitId: nil, redakteur: nil, zeit: nil), jsonPflegonautTableView.Question(id: nil, frage: nil, antwort1: nil, antwort2: nil, antwort3: nil, antwort4: nil, korrekteAntwort: nil, notiz: nil, lernSektorId: nil, lernEinheitId: nil, lernBereichId: nil, schwierigkeitId: nil, redakteur: nil, zeit: nil), jsonPflegonautTableView.Question(id: nil, frage: nil, antwort1: nil, antwort2: nil, antwort3: nil, antwort4: nil, korrekteAntwort: nil, notiz: nil, lernSektorId: nil, lernEinheitId: nil, lernBereichId: nil, schwierigkeitId: nil, redakteur: nil, zeit: nil)]

    Wie kann ich auf die Daten zugreifen und sie in einem Label anzeigen lassen?

    Danke im Voraus! Das nächste mal schöner eingebettet! :)
    LG Valentin


    gesamter Code:
    ViewController.swift
    1. import UIKit
    2. struct Question: Decodable {
    3. let id: Int?
    4. let frage: String?
    5. let antwort1: String?
    6. let antwort2: String?
    7. let antwort3: String?
    8. let antwort4: String?
    9. let korrekteAntwort: Int?
    10. let notiz: String?
    11. let lernSektorId: Int?
    12. let lernEinheitId: Int?
    13. let lernBereichId: Int?
    14. let schwierigkeitId: Int?
    15. let redakteur: String?
    16. let zeit: String?
    17. }
    18. class ViewController: UIViewController {
    19. @IBOutlet weak var listTableView: UITableView!
    20. override func viewDidLoad() {
    21. super.viewDidLoad()
    22. // MARK:-- JSON Connection
    23. let urlPath: String = "https://www.redaktion.pflegonaut.de/service.php"
    24. guard let url = URL(string: urlPath) else { return }
    25. URLSession.shared.dataTask(with: url) { (datas, urlResponse, error) in
    26. // MARK:-- JSON Data
    27. guard let datas = datas else {
    28. return print("something is wrong")
    29. }
    30. let dataAsString = String(data: datas, encoding: .utf8)
    31. print(dataAsString)
    32. do {
    33. let decoder = JSONDecoder()
    34. let downloadedQuestions = try decoder.decode([Question].self, from: datas)
    35. print(downloadedQuestions)
    36. // var countDatas = downloadedQuestions.count
    37. // print(countDatas)
    38. }
    39. catch let jsonErr {
    40. print("Error serializing json:", jsonErr)
    41. }
    42. }.resume()
    43. }
  • Das Problem könnte sein, dass die "Keys" im JSON Dictionary nicht mit den Variablenname in Deiner Question Struktur übereinstimmen.

    Versuche doch mal der Question Struktur eine passende “CodingKeys” Enumeration hinzuzufügen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MCDan ()

  • Sowas kannst Du gut in Playground ausprobieren.

    Quellcode

    1. // Playground
    2. // Nur ein Test!!!
    3. import Foundation
    4. struct Quiz: Codable {
    5. var ID: String
    6. var Frage: String
    7. var Antwort1: String
    8. }
    9. let url = URL(string: "https://www.redaktion.pflegonaut.de/service.php")!
    10. let task = URLSession.shared.dataTask(with: url) { data, response, error in
    11. if let error = error {
    12. print(error.localizedDescription)
    13. return
    14. }
    15. guard let httpResponse = response as? HTTPURLResponse,
    16. (200...299).contains(httpResponse.statusCode) else {
    17. print(response.debugDescription)
    18. return
    19. }
    20. if let mimeType = httpResponse.mimeType, mimeType == "text/html", let data = data, let string = String(data: data, encoding: .utf8) {
    21. print(string)
    22. let decoder = JSONDecoder()
    23. do {
    24. let arr = try decoder.decode([Quiz].self, from: data)
    25. guard let q = arr.first else {
    26. print("No item found")
    27. return
    28. }
    29. print(q.ID )
    30. print(q.Frage)
    31. print(q.Antwort1)
    32. } catch {
    33. print("Unexpected error: \(error).")
    34. }
    35. }
    36. }
    37. task.resume()
    Alles anzeigen
  • Das hat mir sehr geholfen! Vielen Dank euch beiden.

    Der aktuelle Code:

    Quellcode

    1. struct Question:Codable {
    2. var ID: String?
    3. var Frage: String?
    4. var Antwort1: String?
    5. var Antwort2: String?
    6. var Antwort3: String?
    7. var Antwort4: String?
    8. var Correct: String?
    9. var Notiz: String?
    10. var LernsektorID: String?
    11. var LerneinheitID: String?
    12. var LernbereichID: String?
    13. var SchwierigkeitID: String?
    14. var Redakteur: String?
    15. var created_at: String?
    16. enum CodingKeys: String, CodingKey {
    17. case ID = "ID"
    18. case Frage = "Frage"
    19. case Antwort1 = "Antwort1"
    20. case Antwort2 = "Antwort2"
    21. case Antwort3 = "Antwort3"
    22. case Antwort4 = "Antwort4"
    23. case Correct = "Correct"
    24. case Notiz = "Notiz"
    25. case LernsektorID = "LernsektorID"
    26. case LerneinheitID = "LerneinheitID"
    27. case LernbereichID = "LernbereichID"
    28. case SchwierigkeitID = "SchwierigkeitID"
    29. }
    30. }
    31. // JSON holen
    32. let url = URL(string: "https://www.redaktion.pflegonaut.de/service.php")!
    33. let task = URLSession.shared.dataTask(with: url) { data, response, error in
    34. if let error = error {
    35. print(error.localizedDescription)
    36. return
    37. }
    38. }
    39. func downloadData () {
    40. URLSession.shared.dataTask(with: url) { (data, urlResponse, error) in
    41. // MARK:-- JSON Data
    42. guard let datas = data else {
    43. return print("something is wrong")
    44. }
    45. do {
    46. let decoder = JSONDecoder()
    47. let downloadedQuestions = try decoder.decode([Question].self, from: datas)
    48. // Ausgabe der empfangenen Daten, die endlich funktioniert
    49. print(downloadedQuestions[1].Frage!)
    50. }
    51. catch let jsonErr {
    52. print("Error serializing json:", jsonErr)
    53. }
    54. }.resume()
    55. }
    56. downloadData()
    Alles anzeigen



    PS: manohs Variante hab ich mir auch mal hinterlegt zwecks dazulernen :)
  • Noch eine weitere Frage:

    Ich würde gerne aus dem Dictionary "downloadedQuestions" nur die Arrays abrufen, bei denen z.B. LerneinheitID = "1" ist.

    Also das Quiz praktisch so modifizieren, dass erst alle Fragen aus Lerneinheit 1, 2, 3 ... und so weiter im Quiz abgefragt werden.

    Wie bekomme ich also bestimmte Arrays mit einer for Schleife als Return aus der "downloadData()"-Funktion, so dass ich sie z.B. via delegate an einen anderen View schicken kann?





    Ich hoffe ich habe mich einigermaßen verständlich ausgedrückt.

    LG Valevale
  • Ich komme irgendwie zu oben genanntem Thema immer noch nicht weiter. Befürchte, dass das an meinem rudimentären Verständnis von OOP liegt.

    Ich würde gerne auf den Inhalt eines mit Json Daten befüllten Arrays außerhalb eines Blocks zugreifen, um ein Quiz mit zufälligen Fragen zu erstellen.
    Wie erreiche ich in 29.,30. das Array questionJsonVar? Damit ich eine zufällige Frage im Label ausgeben kann?


    Eigene Idee zur Lösung:
    Muss ich Objekte erstellen und sie in einem Array speichern? Wenn ja, haben die Objekte direkt alle Properties (Frage, Antwort1-4, ...)?

    Vielen Dank im Voraus an die nette und vor allem geduldige Community :)

    Quellcode

    1. class QuizVC: UIViewController {
    2. // Outlets
    3. @IBOutlet weak var questionTextOutlet: UILabel!
    4. // MARK:-- let & var
    5. var countOfQuestions = 1
    6. var questionBank = QuestionBank()
    7. override func viewDidLoad() {
    8. super.viewDidLoad()
    9. // MARK:- JSON
    10. let url = "https://redaktion.pflegonaut.de/service.php"
    11. let urlObj = URL(string: url)
    12. func jsonDataRequest () {
    13. URLSession.shared.dataTask(with: urlObj!) { (data, response, error) in
    14. do {
    15. // Json to Array
    16. let questionsJsonVar = try JSONDecoder().decode([Question].self, from: data!)
    17. print(questionsJsonVar.count)
    18. print(questionsJsonVar[1].Frage)
    19. print("Eintrag an der Stelle 0 = \(questionsJsonVar[0].Frage)")
    20. } catch {
    21. print(error)
    22. }
    23. }.resume()
    24. }
    25. // MARK:-- OutletConnection
    26. jsonDataRequest()
    27. // FRAGE----------------------------------------------------------
    28. // Wie erreiche ich hier das Array questionJsonVar? Damit ich eine zufällige Frage im Label ausgeben kann?
    29. let number = Int.random(in: 1 ... countOfQuestions)
    30. questionTextOutlet.text = questionJsonVar[number].Frage
    31. // FRAGE----------------------------------------------------------
    32. }
    33. }
    Alles anzeigen



    Quellcode

    1. import Foundation
    2. struct Question:Codable {
    3. var iD:String
    4. var frage:String
    5. var antwort1:String
    6. var antwort2:String
    7. var antwort3:String
    8. var antwort4:String
    9. var correct:String
    10. var notiz:String?
    11. var lernsektorID:String
    12. var lerneinheitID:String
    13. var lernbereichID:String
    14. var schwierigkeitID:String
    15. enum CodingKeys: String, CodingKey {
    16. case iD = "ID"
    17. case frage = "Frage"
    18. case antwort1 = "Antwort1"
    19. case antwort2 = "Antwort2"
    20. case antwort3 = "Antwort3"
    21. case antwort4 = "Antwort4"
    22. case correct = "Correct"
    23. case notiz = "Notiz"
    24. case lernsektorID = "LernsektorID"
    25. case lerneinheitID = "LerneinheitID"
    26. case lernbereichID = "LernbereichID"
    27. case schwierigkeitID = "SchwierigkeitID"
    28. }
    29. // functions must be mutating functions in a struct
    30. //
    31. }
    Alles anzeigen


    Quellcode

    1. import Foundation
    2. import UIKit
    3. struct QuestionBank: Codable {
    4. var questions = [Question]()
    5. }
  • 1. Ich hab die var für questionJsonVar jetzt mal unter direkt ganz oben in der Klasse deklariert und initialisiert. Trotzdem hab ich nach Initialisierung in der Funktion keinen Zugriff auf das befüllte Array s. Kommentar

    2. Außerdem hab ich versucht alle var in struct zu Konstanten/ let zu machen.

    Für das Verständnis:
    1.Meine Klasse (QuizVC) erbt von der Superklasse UIViewController.
    2.Ich deklariere die var questionJsonVar vom Typ [Question] und initialisiere sie mit einem leeren Array ([])
    3.Der Scope der var questionJsonVar erstreckt sich von 14-Ende der class QuizVC.
    4.Ich erstelle eine Funktion in QuizVC, in der ich die var questionJsonVar (jetzt mit self., da closure) neu initialisiere und mittels .decode befülle.
    5.Ich rufe die Funktion nach dem Laden des Views auf.
    6.Die var questionJsonVar mit dem Array ist "beladen".
    7.Ich weise dem Outlet ein zufälligen String aus dem Array zu s. Kommentar


    Danke!

    Quellcode

    1. import UIKit
    2. class QuizVC: UIViewController {
    3. // Outlets
    4. @IBOutlet weak var questionTextOutlet: UILabel!
    5. @IBOutlet weak var btnOutlet: UIButton!
    6. // Btns
    7. @IBAction func nextQuestionBtnAction(_ sender: UIButton) {
    8. }
    9. // MARK:-- let & var
    10. var countOfQuestions = 1
    11. lazy var questionBank : [Question] = []
    12. private var questionsJsonVar: [Question] = []
    13. var counterForQuestionsJsonVarTest = 0
    14. override func viewDidLoad() {
    15. super.viewDidLoad()
    16. // MARK:- JSON
    17. // JSON-----------------------------------------------------------------
    18. let url = "https://redaktion.pflegonaut.de/service.php"
    19. let urlObj = URL(string: url)
    20. func jsonDataRequest () {
    21. URLSession.shared.dataTask(with: urlObj!) { (data, response, error) in
    22. do {
    23. // Json to Array
    24. self.questionsJsonVar = try JSONDecoder().decode([Question].self, from: data!)
    25. // prints data correctly
    26. self.countOfQuestions = self.questionsJsonVar.count
    27. } catch {
    28. print(error)
    29. }
    30. }.resume()
    31. }
    32. // JSON---------------------------------------------------------------
    33. // MARK:-- OutletConnection
    34. jsonDataRequest()
    35. print("Array:", questionsJsonVar)
    36. // prints: Array: []
    37. // kein Zugriff auf Array, bzw. Array nur innerhalb der Funktion/ des Closures befuellt?
    38. // -- Randomize Question Outlet ToDo
    39. // let number = Int.random(in: 1 ... countOfQuestions)
    40. // print(questionsJsonVar[number].Frage)
    41. // questionTextOutlet.text = questionsJsonVar[number].Frage
    42. }
    43. }
    Alles anzeigen

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Valevale ()

  • URLSession arbeitet asynchron, d.h. du hast dort, wo dein Kommentar steht schon Zugriff auf das Array, aber du greifst viel zu früh darauf zu. Wenn du deine Funktion jsonDataRequest() aufrufst, wird dort die URLSession erstellt und gestartet, aber die Funktion kehrt unmittelbar zurück, d.h. der Code nach dem Funktionsaufruf wird bereits ausgeführt, währen die URLSession noch am werkeln ist. Deshalb hast du direkt nach dem Funktionsaufruf auch noch keine Daten in dem questionsJsonVar Array.
  • Danke Michael.

    Hab es über einen return versucht, aber leider: non void function. Auf Stackoverflow hab ich dann gelesen, dass es genau an dieser Asynchronität liegt.
    Wie kann man das am besten lösen? Mit einem Completion Handler? Das hab ich nicht ganz gerafft.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Valevale ()

  • C-Quellcode

    1. override func viewDidLoad() {
    2. super.viewDidLoad()
    3. // MARK:- JSON
    4. // JSON-----------------------------------------------------------------
    5. let url = "https://redaktion.pflegonaut.de/service.php"
    6. let urlObj = URL(string: url)
    7. func jsonDataRequest () {
    8. URLSession.shared.dataTask(with: urlObj!) { (data, response, error) in
    9. do {
    10. // Json to Array
    11. self.questionsJsonVar = try JSONDecoder().decode([Question].self, from: data!)
    12. // prints data correctly
    13. self.countOfQuestions = self.questionsJsonVar.count
    14. print("Array:", questionsJsonVar)
    15. // -- Randomize Question Outlet ToDo
    16. let number = Int.random(in: 1 ... countOfQuestions)
    17. print(questionsJsonVar[number].Frage)
    18. // Die nächste Code-Zeile muss im Main Thread ausgeführt werden.
    19. // Da musst Du mal schauen, wie dies in Swift geht. ;-)
    20. questionTextOutlet.text = questionsJsonVar[number].Frage
    21. } catch {
    22. print(error)
    23. }
    24. }.resume()
    25. }
    26. // JSON---------------------------------------------------------------
    27. // MARK:-- OutletConnection
    28. jsonDataRequest()
    29. }
    Alles anzeigen
    War ja jetzt nicht so schwer, oder? ;)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MCDan ()

  • Leider bekomme ich den unten angegebenen Fehler zurück. Außerdem ist das Array glaube ich leer (s. Bild).

    Brainfuck-Quellcode

    1. import UIKit
    2. class QuizVC: UIViewController {
    3. // Outlets
    4. @IBOutlet weak var questionTextOutlet: UILabel!
    5. @IBOutlet weak var btnOutlet: UIButton!
    6. // Btns
    7. @IBAction func nextQuestionBtnAction(_ sender: UIButton) {
    8. }
    9. // MARK:-- let & var
    10. var countOfQuestions = 1
    11. var randNumber = 1
    12. // var questionsJsonVar: [Question] = []
    13. var counterForQuestionsJsonVarTest = 0
    14. override func viewDidLoad() {
    15. super.viewDidLoad()
    16. // MARK:- JSON
    17. // JSON-----------------------------------------------------------------
    18. let url = "https://redaktion.pflegonaut.de/service.php"
    19. let urlObj = URL(string: url)
    20. func jsonDataRequest () {
    21. URLSession.shared.dataTask(with: urlObj!) { (data, response, error) in
    22. do {
    23. // Json to Array
    24. self.questionsJsonVar = try JSONDecoder().decode([Question].self, from: data!)
    25. // prints data correctly
    26. self.countOfQuestions = self.questionsJsonVar.count
    27. // -- Randomize Question Outlet
    28. let randNumber = Int.random(in: 1 ... self.countOfQuestions - 1)
    29. print(self.questionsJsonVar[randNumber].Frage)
    30. } catch {
    31. print(error)
    32. }
    33. }.resume()
    34. }
    35. // JSON---------------------------------------------------------------
    36. // MARK:-- OutletConnection
    37. jsonDataRequest()
    38. DispatchQueue.main.async{
    39. self.questionTextOutlet.text = self.questionsJsonVar[self.randNumber].Frage
    40. // Thread 1: Fatal error: Index out of range
    41. }
    42. }
    43. }
    Alles anzeigen
    Bildschirmfoto 2019-01-02 um 13.29.08.png

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Valevale ()

  • Dann hat sich aber das Problem noch nicht geloest oder? Und wie bekomme ich das Array das Array dann da raus mit oder ohne dispatchQueue :) Habe gesehen, dass jemand folgendes gemacht hat:

    Macht das Sinn, eine eigene Klasse mit einem weiteren Array zu erstellen, um da das sowieso schon bestehende JsonArray zu speichern? Auf jeden Fall scheint man hier Zugriff zu haben.

    Quellcode

    1. import UIKit
    2. class Actors: Codable {
    3. let actors: [Actor]
    4. init(actors: [Actor]) {
    5. self.actors = actors
    6. }
    7. }
    8. class Actor: Codable {
    9. let name: String
    10. let description: String
    11. let dob: String
    12. let country: String
    13. let height: String
    14. let spouse: String
    15. let children: String
    16. let image: String
    17. init(name: String, description: String, dob: String, country: String, height: String, spouse: String, children: String, image: String) {
    18. self.name = name
    19. self.description = description
    20. self.dob = dob
    21. self.country = country
    22. self.height = height
    23. self.spouse = spouse
    24. self.children = children
    25. self.image = image
    26. }
    27. }
    Alles anzeigen


    Brainfuck-Quellcode

    1. import UIKit
    2. class ViewController: UIViewController, UITableViewDataSource {
    3. final let url = URL(string: "http://microblogging.wingnity.com/JSONParsingTutorial/jsonActors")
    4. //--------------------------------
    5. private var actors = [Actor]()
    6. //--------------------------------
    7. @IBOutlet var tableView: UITableView!
    8. override func viewDidLoad() {
    9. super.viewDidLoad()
    10. downloadJson()
    11. tableView.tableFooterView = UIView()
    12. }
    13. func downloadJson() {
    14. guard let downloadURL = url else { return }
    15. URLSession.shared.dataTask(with: downloadURL) { data, urlResponse, error in
    16. guard let data = data, error == nil, urlResponse != nil else {
    17. print("something is wrong")
    18. return
    19. }
    20. print("downloaded")
    21. do
    22. {
    23. let decoder = JSONDecoder()
    24. let downloadedActors = try decoder.decode(Actors.self, from: data)
    25. self.actors = downloadedActors.actors
    26. DispatchQueue.main.async {
    27. self.tableView.reloadData()
    28. }
    29. } catch {
    30. print("something wrong after downloaded")
    31. }
    32. }.resume()
    33. }
    Alles anzeigen