2017-12-22 10 views
2

때때로 JSON에서 특정 키 (이 경우 id)를 Int로 반환하고 다른 경우에는 동일한 키를 반환하는 API가 있습니다. String. JSON을 구문 분석하는 코드 가능 코드는 어떻게 사용합니까?가끔은 Int 인 키와 코드를 사용하는 코드 가능

struct GeneralProduct: Codable { 
    var price:Double! 
    var id:String? 
    var name:String! 

    private enum CodingKeys: String, CodingKey { 
     case price = "p" 
     case id = "i" 
     case name = "n" 
    } 

    init(price:Double? = nil, id: String? = nil, name: String? = nil) { 
     self.price = price 
     self.id = id 
     self.name = name 
    } 
} 

이 오류 메시지가 계속 나타납니다. Expected to decode String but found a number instead. id 필드가 비어 있고 id 필드가 비어 있으면 기본적으로 숫자로 식별 할 수있는 코드로 0을 반환합니다. 나는 기본적으로 ID 키를 무시할 수 있지만 코드 가능한 것은 내 지식에 무시할 수있는 옵션을주지 않습니다. 이것을 처리하는 가장 좋은 방법은 무엇입니까?

다음은 JSON입니다. 그것은

{ 
    "p":2.12, 
    "i":"3k3mkfnk3", 
    "n":"Blue Shirt" 
} 

오류 작업

매우 간단합니다 - 어떤 ID가 시스템에 없기 때문에, 그것은 분명히 codable 문자열에 반대 숫자로 보는 기본값으로 0을 반환합니다.

{ 
    "p":2.19, 
    "i":0, 
    "n":"Black Shirt" 
} 
+0

내가 당신에게 우리를 제안 :

struct GeneralProduct: Codable { var price:Double? var id:MetadataType? var name:String? private enum CodingKeys: String, CodingKey { case price = "p" case id = "i" case name = "n" } init(price:Double? = nil, id: MetadataType? = nil, name: String? = nil) { self.price = price self.id = id self.name = name } } enum MetadataType: Codable { case int(Int) case string(String) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() do { self = try .int(container.decode(Int.self)) } catch DecodingError.typeMismatch { do { self = try .string(container.decode(String.self)) } catch DecodingError.typeMismatch { throw DecodingError.typeMismatch(MetadataType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload not of an expected type")) } } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .int(let int): try container.encode(int) case .string(let string): try container.encode(string) } } } 

테스트입니다 대신에 SwiftyJSON 라이브러리. – t4nhpt

+1

왜 그런 말을합니까? 코드화가 훨씬 더 좋으므로 SwiftyJSON을 사용하는 것이 좋습니다. 솔루션이 없다면 분명히 SwiftyJSON을 사용할 것입니다. 그러나이 작업을 수행 할 방법이 없다면 나는 놀랄 것입니다. –

+0

어떤 JSON이 작동하고 어떤 JSON이 아닌지에 대한 예를 들려 줄 수 있습니까? –

답변

1
struct GeneralProduct: Codable { 
    var price: Double? 
    var id: String? 
    var name: String? 
    private enum CodingKeys: String, CodingKey { 
     case price = "p", id = "i", name = "n" 
    } 
    init(price: Double? = nil, id: String? = nil, name: String? = nil) { 
     self.price = price 
     self.id = id 
     self.name = name 
    } 
    init(from decoder: Decoder) throws { 
     let container = try decoder.container(keyedBy: CodingKeys.self) 
     price = try container.decode(Double.self, forKey: .price) 
     name = try container.decode(String.self, forKey: .name) 
     if let value = try? container.decode(Int.self, forKey: .id) { 
      id = String(value) 
     } else { 
      id = try container.decode(String.self, forKey: .id) 
     } 
    } 
} 

let json1 = """ 
{ 
"p":2.12, 
"i":"3k3mkfnk3", 
"n":"Blue Shirt" 
} 
""" 

let json2 = """ 
{ 
"p":2.12, 
"i":0, 
"n":"Blue Shirt" 
} 
""" 

do { 
    let product = try JSONDecoder().decode(GeneralProduct.self, from: Data(json2.utf8)) 
    print(product.price ?? "") 
    print(product.id ?? "") 
    print(product.name ?? "") 
} catch { 
    print(error) 
} 

편집/업데이트 :

또한 단순히을 할당 할 수 있습니다 당신의 id-는 API는 0 반환 할 때 :

if let _ = try? container.decode(Int.self, forKey: .id) { 
    id = nil 
} else { 
    id = try container.decode(String.self, forKey: .id) 
} 
+0

나는 이것이 내가 찾고있는 것이라고 믿지만, 실제 시나리오는 훨씬 더 많은 변수를 가지고 훨씬 더 복잡하다. 각 변수에 대한 사용자 정의 초기화 프로그램을 작성하지 않고이 작업을 수행 할 수있는 방법은 없습니다. 아주 작은 코드를위한 여분의 코드가 많이 있습니다. –

+0

사용자 정의 디코더 없이도 문제를 해결할 수 있다고 생각하지 않습니다. –

+0

괜찮습니다. 완전히 이해합니다.게시 된 다른 답변에 대한 생각은 무엇입니까? –

1

이것은 MetadataType와 가능한 해결책이 좋은 것은 그것이하지 GeneralProduct 만, 그러나 모든 struct 같은 모호성을 가지고에 대한 일반적인 해결책이 될 수있다 :

let decoder = JSONDecoder() 
var json = "{\"p\":2.19,\"i\":0,\"n\":\"Black Shirt\"}" 
var product = try! decoder.decode(GeneralProduct.self, from: json.data(using: .utf8)!) 
if let id = product.id { 
    print(id) // 0 
} 

json = "{\"p\":2.19,\"i\":\"hello world\",\"n\":\"Black Shirt\"}" 
product = try! decoder.decode(GeneralProduct.self, from: json.data(using: .utf8)!) 
if let id = product.id { 
    print(id) // hello world 
}