1

I want to parse a json from a http resource (it's my router so http is mandatory).

After I set the Info.plist App Security Transport that I do get a connection I get the data by 1st Attempted:

let sphDataAddress = "http://speedport.ip/data/status.json"
let url = URL(string: sphDataAddress)!
let jsonData = try! Data(contentsOf: url) // ! is just for testing reason and will be in real app by guard let 
print("data \(jsonData)") // shows that the received Data are 5077bytes


struct User {

    let vartype: String
    let varid: String
    let varvalue: Company

    init?(dict: [String: Any]) {
        guard
             let vartype = dict["vartype-data"] as? String,
            let varid = dict["valid-data"] as? String,
            let varvalueDict = dict["company"] as? [String: Any],
            let varvalue = Company(dict: varvalueDict)
            else {
                return nil
        }

         self.vartype = vartype
        self.varid = varid
         self.varvalue = varvalue
    }


    struct Company {
        let vartype: String
        let varid: String
        let varvalue: String

        init?(dict: [String: Any]) {
            guard
                let vartype = dict["vartype-sub"] as? String,
                let varid = dict["varid-sub"] as? String,
                let varvalue = dict["varvalue-Sub"] as? String else {
                    return nil
            }

            self.vartype = vartype
            self.varid = varid
            self.varvalue = varvalue
        }
    }
}

if let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) {
   if let jsonArray = json as? [[String: Any]] {
       let users = jsonArray.flatMap { $0.map { $0} }
       let zun = users.count

       print(users, zun)

   }
}

2nd Attempt: I ve also tried that one without success: // I put it into viewed load just to test it!!

 struct RouterData: Decodable {

let vartype: String?
let varid: String?
let varvalue: String?
}
override func viewDidLoad() {
    super.viewDidLoad()

    let jsonUrlString = "http://speedport.ip/data/status.json"
    guard let url = URL(string: jsonUrlString) else { return }

    URLSession.shared.dataTask(with: url) { (data, response, err) in
        //perhaps check err checked by print(response) 
        //also perhaps check response status 200 OK

        guard let data = data else { return }

        do {
            let courses = try JSONDecoder().decode([RouterData].self, from: data)
            print(courses)

        } catch let jsonErr {
            print("Error serializing json:", jsonErr)
        }  
    }.resume()

}

The Error in console:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 25", intValue: Optional(25)), JsonParseSwift4.RouterData.(CodingKeys in _DD16AFBB8A755D282DC27E60A66FDC03).varvalue], debugDescription: "Expected to decode String but found an array instead.", underlyingError: nil))

Thats the (full) json structure comming from source (Router)

 (
    {
    varid = "device_name";
    vartype = value;
    varvalue = "Speedport Hybrid";
},
    {
    varid = "provis_inet";
    vartype = value;
    varvalue = x03;
},
    {
    varid = "provis_voip";
    vartype = value;
    varvalue = xx3;
},
    {
    varid = "ppp_bnguser";
    vartype = value;
    varvalue = 0;
},
    {
    varid = bngscrat;
    vartype = value;
    varvalue = 0;
},
    {
    varid = "router_state";
    vartype = value;
    varvalue = OK;
},
    {
    varid = "support_https";
    vartype = value;
    varvalue = 0;
},
    {
    varid = title;
    vartype = "page_title";
    varvalue = "Speedport Hybrid Konfigurationsprogramm";
},
    {
    varid = onlinestatus;
    vartype = status;
    varvalue = online;
},
    {
    varid = "use_lte";
    vartype = option;
    varvalue = 1;
},
    {
    varid = "lte_status";
    vartype = value;
    varvalue = 10;
},
    {
    varid = "bonding_status";
    vartype = value;
    varvalue = Online;
},
    {
    varid = "lte_signal";
    vartype = value;
    varvalue = 5;
},
    {
    varid = loginstate;
    vartype = status;
    varvalue = 0;
},
    {
    varid = datetime;
    vartype = value;
    varvalue = "18.08.2017 14:54:30";
},
    {
    varid = "device_name";
    vartype = value;
    varvalue = "Speedport Hybrid";
},
    {
    varid = imei;
    vartype = value;
    varvalue = 1234567891230;
},
    {
    varid = "dsl_link_status";
    vartype = value;
    varvalue = online;
},
    {
    varid = "dsl_errnr";
    vartype = value;
    varvalue = "";
},
    {
    varid = status;
    vartype = value;
    varvalue = online;
},
    {
    varid = "fail_reason";
    vartype = value;
    varvalue = "";
},
    {
    varid = "inet_errnr";
    vartype = value;
    varvalue = "";
},
    {
    varid = connect;
    vartype = value;
    varvalue = 0;
},
    {
    varid = "dsl_downstream";
    vartype = value;
    varvalue = 8184;
},
    {
    varid = "dsl_upstream";
    vartype = value;
    varvalue = 2429;
},
    {
    varid = addphonenumber;
    vartype = template;
    varvalue =         (
                    {
            varid = id;
            vartype = value;
            varvalue = 1;
        },
                    {
            varid = "phone_number";
            vartype = value;
            varvalue = „*100“;
        },
                    {
            varid = failreason;
            vartype = value;
            varvalue = 0;
        },
                    {
            varid = status;
            vartype = value;
            varvalue = ok;
        },
                    {
            varid = "voip_errnr";
            vartype = value;
            varvalue = "";
        }
    );
},
    {
    varid = addphonenumber;
    vartype = template;
    varvalue =         (
                    {
            varid = id;
            vartype = value;
            varvalue = 2;
        },
                    {
            varid = "phone_number";
            vartype = value;
            varvalue = „*200";
        },
                    {
            varid = failreason;
            vartype = value;
            varvalue = 0;
        },
                    {
            varid = status;
            vartype = value;
            varvalue = ok;
        },
                    {
            varid = "voip_errnr";
            vartype = value;
            varvalue = "";
        }
    );
},
    {
    varid = addphonenumber;
    vartype = template;
    varvalue =         (
                    {
            varid = id;
            vartype = value;
            varvalue = 3;
        },
                    {
            varid = "phone_number";
            vartype = value;
            varvalue = „*300“;
        },
                    {
            varid = failreason;
            vartype = value;
            varvalue = 0;
        },
                    {
            varid = status;
            vartype = value;
            varvalue = ok;
        },
                    {
            varid = "voip_errnr";
            vartype = value;
            varvalue = "";
        }
    );
},
    {
    varid = adddect;
    vartype = template;
    varvalue =         (
                    {
            varid = id;
            vartype = value;
            varvalue = 1;
        }
    );
},
    {
    varid = adddect;
    vartype = template;
    varvalue =         (
                    {
            varid = id;
            vartype = value;
            varvalue = 2;
        }
    );
},
    {
    varid = adddect;
    vartype = template;
    varvalue =         (
                    {
            varid = id;
            vartype = value;
            varvalue = 3;
        }
    );
},
    {
    varid = "use_dect";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "wlan_ssid";
    vartype = value;
    varvalue = Speedport;
},
    {
    varid = "wlan_5ghz_ssid";
    vartype = value;
    varvalue = Speedport5;
},
    {
    varid = "use_wlan";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "use_wlan_5ghz";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "wlan_devices";
    vartype = value;
    varvalue = 0;
},
    {
    varid = "wlan_5ghz_devices";
    vartype = value;
    varvalue = 3;
},
    {
    varid = "lan1_device";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "lan2_device";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "lan3_device";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "lan4_device";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "use_wps";
    vartype = value;
    varvalue = 1;
},
    {
    varid = "hsfon_status";
    vartype = value;
    varvalue = 0;
},
    {
    varid = "firmware_version";
    vartype = value;
    varvalue = "050124.03.05.017";
},
    {
    varid = "serial_number";
    vartype = value;
    varvalue = Sp123456789;
})

the Data comming from the router are:

the JSON is only just a snipped what I want to show is that varvalue is once a string and once a [] with sub data but the same var's.

2
  • Your code seems to be more more complex that it needs to be. JSON decoding with Swift 4 can be very simple. Can you post your data model and the full JSON to your question? Commented Aug 20, 2017 at 14:32
  • Please post raw JSON, what you posted is invalid. Commented Aug 21, 2017 at 2:29

1 Answer 1

1

Assuming this is the JSON structure you intended to paste:

[
  {
    "varid": "dsl_downstream",
    "vartype": "value",
    "varvalue": 11111
  },
  {
    "varid": "adddect",
    "vartype": "template",
    "varvalue": [
      {
        "varid": "id",
        "vartype": "value",
        "varvalue": "some_value"
      }
    ]
  }
]

Here's a possible solution:

// let jsonData = "...".data(using: .utf8)!

enum Either<A,B> where A: Decodable, B: Decodable {
    case left(A)
    case right(B)
}

struct RouterData: Decodable {
    let type: String
    let id: String
    let value: Either<String,[RouterData]>

    enum CodingKeys: String, CodingKey {
      case type  = "vartype"
      case id    = "varid"
      case value = "varvalue"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        type = try container.decode(String.self, forKey: .type)
        id = try container.decode(String.self, forKey: .id)

        if let elementValue = try? container.decode(String.self, forKey: .value) {
            value = .left(elementValue)
        } else if let elementValue = try? container.decode(Int.self, forKey: .value) {
            value = .left(String(elementValue))
        } else {
            let childData = try container.decode([RouterData].self, forKey: .value)
            value = .right(childData)
        }
    }
}

let decoded = try JSONDecoder().decode([RouterData].self, from: jsonData)
print(decoded)
Sign up to request clarification or add additional context in comments.

5 Comments

As I understand that Code you set in varvalue a string and an array and catched it with enum "either". And double checked that ind the if statement elementValue. 2 Questions. 1st wouldn't you do the same in swift 3 ? 2nd for me it seams there is no benefit of swift 4 in that particular case m I right?
Codable was introduced in Swift 4
You are right. So my mistake was not to set the enum "either" and let run it through each key, each by each. And that s because decode can only work with int and string automatically and no array. I just try to understand where is my mistake of thoughts. If you watch that video it look so easy youtube.com/watch?v=YY3bTxgxWss and even to do without the init stuff.
Decodable can work with almost any swift type by default, including sequences. This case is special because it’s a multi type variable
@nathan, How can we access varvalue which is type array (in second object) decoded[1].value. ... ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.