json swift4 how to set the struct?

2019-07-26 21:39发布

问题:

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.

回答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)