我正在尝试解码从网络请求中接收到的JSON,但它仅解码第一个对象。 JSON看起来像这样
{
"lastUpdatedOn": "2018-08-21T14:38:38.251Z",
"players": [
{
"player": {
"id": 10138,
"firstName": "Alex",
"lastName": "Abrines",
"primaryPosition": "SG",
"alternatePositions": [],
"jerseyNumber": 8,
"currentTeam": {
"id": 96,
"abbreviation": "OKL"
},
"currentRosterStatus": "ROSTER",
"currentInjury": null,
"height": "6'6\"",
"weight": 190,
"birthDate": "1993-08-01",
"age": 25,
"birthCity": "Palma de Mallorca",
"birthCountry": "Spain",
"rookie": false,
"highSchool": null,
"college": null,
"handedness": {
"shoots": "R"
},
"officialImageSrc": "https://ak-static.cms.nba.com/wp-content/uploads/headshots/nba/latest/260x190/203518.png",
"socialMediaAccounts": [],
"currentContractYear": null,
"drafted": {
"year": 2013,
"team": {
"id": 96,
"abbreviation": "OKL"
},
"pickTeam": {
"id": 96,
"abbreviation": "OKL"
},
"round": 2,
"roundPick": 2,
"overallPick": 32
},
"externalMappings": [
{
"source": "NBA.com",
"id": 203518
}
]
},
"teamAsOfDate": {
"id": 96,
"abbreviation": "OKL"
}
},
{
"player": {
"id": 9466,
"firstName": "Quincy",
"lastName": "Acy",
"primaryPosition": "PF",
"alternatePositions": [],
"jerseyNumber": 13,
"currentTeam": {
"id": 84,
"abbreviation": "BRO"
},
"currentRosterStatus": "ROSTER",
"currentInjury": null,
"height": "6'7\"",
"weight": 240,
"birthDate": "1990-10-06",
"age": 27,
"birthCity": "Tyler, TX",
"birthCountry": "USA",
"rookie": false,
"highSchool": "Mesquite Horn",
"college": "Baylor University",
"handedness": {
"shoots": "R"
},
"officialImageSrc": "https://ak-static.cms.nba.com/wp-content/uploads/headshots/nba/latest/260x190/203112.png",
"socialMediaAccounts": [
{
"mediaType": "TWITTER",
"value": "QuincyAcy"
}
],
"currentContractYear": {
"seasonStartYear": 2017,
"baseSalary": 1709538,
"minorsSalary": 0,
"signingBonus": 0,
"otherBonuses": 0,
"capHit": 1709538,
"fullNoTradeClause": false,
"modifiedNoTradeClause": false,
"noMovementClause": false,
"overallContract": {
"signingTeam": {
"id": 84,
"abbreviation": "BRO"
},
"signedOn": null,
"totalYears": 2,
"totalSalary": 3499630,
"totalBonuses": 0,
"expiryStatus": "UFA",
"annualAverageSalary": 1749815
}
},
"drafted": {
"year": 2012,
"team": {
"id": 81,
"abbreviation": "TOR"
},
"pickTeam": {
"id": 81,
"abbreviation": "TOR"
},
"round": 2,
"roundPick": 7,
"overallPick": 37
},
"externalMappings": [
{
"source": "NBA.com",
"id": 203112
}
]
},
"teamAsOfDate": {
"id": 84,
"abbreviation": "BRO"
}
},
{
"player": {
"id": 9301,
"firstName": "Jordan",
"lastName": "Adams",
"primaryPosition": "SG",
"alternatePositions": [],
"jerseyNumber": 3,
"currentTeam": null,
"currentRosterStatus": "RETIRED",
"currentInjury": null,
"height": "6'5\"",
"weight": 209,
"birthDate": "1994-07-08",
"age": 24,
"birthCity": "Atlanta, GA",
"birthCountry": "USA",
"rookie": false,
"highSchool": "Oak Hill Academy",
"college": "University of California, Los Angeles",
"handedness": {
"shoots": "R"
},
"officialImageSrc": "https://ak-static.cms.nba.com/wp-content/uploads/headshots/nba/latest/260x190/203919.png",
"socialMediaAccounts": [
{
"mediaType": "TWITTER",
"value": "jordanadams1231"
}
],
"currentContractYear": null,
"drafted": {
"year": 2014,
"team": {
"id": 107,
"abbreviation": "MEM"
},
"pickTeam": {
"id": 107,
"abbreviation": "MEM"
},
"round": 1,
"roundPick": 22,
"overallPick": 22
},
"externalMappings": [
{
"source": "NBA.com",
"id": 203919
}
]
},
"teamAsOfDate": null
}
],
"references": {
"teamReferences": [
{
"id": 84,
"city": "Brooklyn",
"name": "Nets",
"abbreviation": "BRO",
"homeVenue": {
"id": 78,
"name": "Barclays Center"
},
"teamColoursHex": [],
"socialMediaAccounts": [],
"officialLogoImageSrc": null
},
{
"id": 96,
"city": "Oklahoma City",
"name": "Thunder",
"abbreviation": "OKL",
"homeVenue": {
"id": 90,
"name": "Chesapeake Energy Arena"
},
"teamColoursHex": [],
"socialMediaAccounts": [],
"officialLogoImageSrc": null
}
]
}
}
我的结构目前看起来像这样,我正在尝试使用自定义解码器,我能够使用嵌套结构使其工作,但随后我必须遍历解码后的数据并将所有播放器添加到数组中。
struct Player: Codable {
let firstName: String
let lastName: String
enum RootKeys: String, CodingKey {
case players
}
enum PlayerKeys: String, CodingKey {
case player
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKeys.self)
var playersUnkeyedContainer = try container.nestedUnkeyedContainer(forKey: .players)
let playersContainer = try playersUnkeyedContainer.nestedContainer(keyedBy: PlayerKeys.self)
let playerContainer = try playersContainer.nestedContainer(keyedBy: Player.CodingKeys.self, forKey: .player)
firstName = try playerContainer.decode(String.self, forKey: .firstName)
lastName = try playerContainer.decode(String.self, forKey: .lastName)
}
}
当我尝试这样做
let decodedPlayers = try jsonDecoder.decode(Player.self, from: data)
它可以工作,但是decodedPlayers只是一个播放器,如果我尝试这样做:
let decodedPlayers = try jsonDecoder.decode([Player].self, from: data)
我收到此错误消息: “希望对Array进行解码,但找到了字典。”
答案 0 :(得分:0)
这对您有帮助!!
// To parse the JSON, add this file to your project and do:
//
// let welcome = try? newJSONDecoder().decode(Welcome.self, from: jsonData)
import Foundation
struct Welcome: Codable {
let lastUpdatedOn: String
let players: [PlayerElement]
let references: References
}
struct PlayerElement: Codable {
let player: PlayerPlayer
let teamAsOfDate: TeamAsOfDate?
}
struct PlayerPlayer: Codable {
let id: Int
let firstName, lastName, primaryPosition: String
let alternatePositions: [JSONAny]
let jerseyNumber: Int
let currentTeam: TeamAsOfDate?
let currentRosterStatus: String
let currentInjury: JSONNull?
let height: String
let weight: Int
let birthDate: String
let age: Int
let birthCity, birthCountry: String
let rookie: Bool
let highSchool, college: String?
let handedness: Handedness
let officialImageSrc: String
let socialMediaAccounts: [SocialMediaAccount]
let currentContractYear: CurrentContractYear?
let drafted: Drafted
let externalMappings: [ExternalMapping]
}
struct CurrentContractYear: Codable {
let seasonStartYear, baseSalary, minorsSalary, signingBonus: Int
let otherBonuses, capHit: Int
let fullNoTradeClause, modifiedNoTradeClause, noMovementClause: Bool
let overallContract: OverallContract
}
struct OverallContract: Codable {
let signingTeam: TeamAsOfDate
let signedOn: JSONNull?
let totalYears, totalSalary, totalBonuses: Int
let expiryStatus: String
let annualAverageSalary: Int
}
struct TeamAsOfDate: Codable {
let id: Int
let abbreviation: String
}
struct Drafted: Codable {
let year: Int
let team, pickTeam: TeamAsOfDate
let round, roundPick, overallPick: Int
}
struct ExternalMapping: Codable {
let source: String
let id: Int
}
struct Handedness: Codable {
let shoots: String
}
struct SocialMediaAccount: Codable {
let mediaType, value: String
}
struct References: Codable {
let teamReferences: [TeamReference]
}
struct TeamReference: Codable {
let id: Int
let city, name, abbreviation: String
let homeVenue: HomeVenue
let teamColoursHex, socialMediaAccounts: [JSONAny]
let officialLogoImageSrc: JSONNull?
}
struct HomeVenue: Codable {
let id: Int
let name: String
}
// MARK: Encode/decode helpers
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
return true
}
public var hashValue: Int {
return 0
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
class JSONCodingKey: CodingKey {
let key: String
required init?(intValue: Int) {
return nil
}
required init?(stringValue: String) {
key = stringValue
}
var intValue: Int? {
return nil
}
var stringValue: String {
return key
}
}
class JSONAny: Codable {
let value: Any
static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
return DecodingError.typeMismatch(JSONAny.self, context)
}
static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
return EncodingError.invalidValue(value, context)
}
static func decode(from container: SingleValueDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if container.decodeNil() {
return JSONNull()
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if let value = try? container.decodeNil() {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer() {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
if let value = try? container.decode(Bool.self, forKey: key) {
return value
}
if let value = try? container.decode(Int64.self, forKey: key) {
return value
}
if let value = try? container.decode(Double.self, forKey: key) {
return value
}
if let value = try? container.decode(String.self, forKey: key) {
return value
}
if let value = try? container.decodeNil(forKey: key) {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer(forKey: key) {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
var arr: [Any] = []
while !container.isAtEnd {
let value = try decode(from: &container)
arr.append(value)
}
return arr
}
static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
var dict = [String: Any]()
for key in container.allKeys {
let value = try decode(from: &container, forKey: key)
dict[key.stringValue] = value
}
return dict
}
static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
for value in array {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer()
try encode(to: &container, array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
try encode(to: &container, dictionary: value)
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
}
static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
for (key, value) in dictionary {
let key = JSONCodingKey(stringValue: key)!
if let value = value as? Bool {
try container.encode(value, forKey: key)
} else if let value = value as? Int64 {
try container.encode(value, forKey: key)
} else if let value = value as? Double {
try container.encode(value, forKey: key)
} else if let value = value as? String {
try container.encode(value, forKey: key)
} else if value is JSONNull {
try container.encodeNil(forKey: key)
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer(forKey: key)
try encode(to: &container, array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
try encode(to: &container, dictionary: value)
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
}
static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
public required init(from decoder: Decoder) throws {
if var arrayContainer = try? decoder.unkeyedContainer() {
self.value = try JSONAny.decodeArray(from: &arrayContainer)
} else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
self.value = try JSONAny.decodeDictionary(from: &container)
} else {
let container = try decoder.singleValueContainer()
self.value = try JSONAny.decode(from: container)
}
}
public func encode(to encoder: Encoder) throws {
if let arr = self.value as? [Any] {
var container = encoder.unkeyedContainer()
try JSONAny.encode(to: &container, array: arr)
} else if let dict = self.value as? [String: Any] {
var container = encoder.container(keyedBy: JSONCodingKey.self)
try JSONAny.encode(to: &container, dictionary: dict)
} else {
var container = encoder.singleValueContainer()
try JSONAny.encode(to: &container, value: self.value)
}
}
}
答案 1 :(得分:0)
您的JSON不是数组。因此,您不能使用:
let decodedPlayers = try jsonDecoder.decode([Player].self, from: data)
您需要:
[{"id": 10138, "firstName": "Alex"}, {"id": 10139, "firstName": "Alex2"} ]
OR
struct YourData: Codable {
let lastUpdatedOn: String
let players: [Player]
}
extension YourData: Decodable {
enum CodingKeys: CodingKey { case lastUpdatedOn, players }
enum NodeKeys: CodingKey { case nodes }
init(from decoder: Decoder) throws {
let rootContainer = try decoder.container(keyedBy: CodingKeys.self)
try lastUpdatedOn = rootContainer.decode(String.self, forKey: .lastUpdatedOn)
let playersNode = try rootContainer.nestedContainer(keyedBy: NodeKeys.self, forKey: .players)
var nodes = try playersNode(forKey: .nodes)
var playerArray: [Player] = []
while !nodes.isAtEnd {
let ply = try nodes.decode(Player.self)
playerArray += [ply]
}
players = playerArray
}
}
最后:
let decodedPlayers = try jsonDecoder.decode(YourData.self, from: data)
参考文献:
答案 2 :(得分:0)
使用嵌套容器的解决方案似乎很麻烦
我的建议是使用嵌套结构以传统方式解码结构
struct Root: Decodable {
let lastUpdatedOn : String
let players : [PlayerData]
}
struct PlayerData: Decodable {
let player : Player
let teamAsOfDate : TeamAsOfDate?
}
struct TeamAsOfDate: Decodable {
let id : Int
let abbreviation : String
}
struct Player: Decodable {
let firstName, lastName : String
}
do {
let result = try JSONDecoder().decode(Root.self, from: data)
print(result)
} catch { print(error) }
答案 3 :(得分:0)
这有效:
struct Player: Decodable {
let firstName: String
let lastName: String
}
struct AllPlayers: Decodable {
let players: [NBAPlayer]
}
struct NBAPlayer: Decodable {
let player: Player
}
但是如果我想要一个包含所有Player对象的数组,则必须这样做:
var players = [Player]()
do {
let decodedPlayers = try jsonDecoder.decode(AllPlayers.self, from: data)
for player in decodedPlayers.players {
players.append(player.player)
}
print(players)
} catch {
NSLog("Error decoding players :\(error)")
}