Swift 4 디코딩 가능한 프로토콜에서 JSON 사전 유형으로 속성을 디코딩하는 방법


103

고객 개체에 JSON 사전을 포함 할 수 Customer있는 metadata속성을 포함하는 데이터 유형이 있다고 가정 해 보겠습니다.

struct Customer {
  let id: String
  let email: String
  let metadata: [String: Any]
}

{  
  "object": "customer",
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}

metadata속성은 임의의 JSON 맵 오브젝트가 될 수 있습니다.

deserialized JSON에서 속성을 캐스팅하기 전에 NSJSONDeserialization새로운 Swift 4 Decodable프로토콜을 사용하면 여전히 그렇게 할 방법을 생각할 수 없습니다.

Decodable 프로토콜로 Swift 4에서 이것을 달성하는 방법을 아는 사람이 있습니까?

답변:


89

내가 찾은 이 요점 에서 영감을 얻어 UnkeyedDecodingContainer및에 대한 확장을 작성했습니다 KeyedDecodingContainer. 여기 에서 내 요점에 대한 링크를 찾을 수 있습니다 . 이제이 코드를 사용 하여 익숙한 구문으로 Array<Any>또는 모든 것을 디코딩 할 수 있습니다 Dictionary<String, Any>.

let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)

또는

let array: [Any] = try container.decode([Any].self, forKey: key)

편집 : 사전 배열을 디코딩하는 한 가지주의 사항 [[String: Any]]이 있습니다. 필요한 구문은 다음과 같습니다. 강제 캐스팅 대신 오류를 던지고 싶을 것입니다.

let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]

편집 2 : 단순히 전체 파일을 사전으로 변환하려는 경우 JSONDecoder 자체를 확장하여 사전을 직접 디코딩하는 방법을 찾지 못했기 때문에 JSONSerialization의 api를 사용하는 것이 좋습니다.

guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
  // appropriate error handling
  return
}

확장

// Inspired by https://gist.github.com/mbuchetics/c9bc6c22033014aa0c550d3b4324411a

struct JSONCodingKeys: CodingKey {
    var stringValue: String

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
        self.init(stringValue: "\(intValue)")
        self.intValue = intValue
    }
}


extension KeyedDecodingContainer {

    func decode(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any> {
        let container = try self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
        return try container.decode(type)
    }

    func decodeIfPresent(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any>? {
        guard contains(key) else { 
            return nil
        }
        guard try decodeNil(forKey: key) == false else { 
            return nil 
        }
        return try decode(type, forKey: key)
    }

    func decode(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any> {
        var container = try self.nestedUnkeyedContainer(forKey: key)
        return try container.decode(type)
    }

    func decodeIfPresent(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any>? {
        guard contains(key) else {
            return nil
        }
        guard try decodeNil(forKey: key) == false else { 
            return nil 
        }
        return try decode(type, forKey: key)
    }

    func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
        var dictionary = Dictionary<String, Any>()

        for key in allKeys {
            if let boolValue = try? decode(Bool.self, forKey: key) {
                dictionary[key.stringValue] = boolValue
            } else if let stringValue = try? decode(String.self, forKey: key) {
                dictionary[key.stringValue] = stringValue
            } else if let intValue = try? decode(Int.self, forKey: key) {
                dictionary[key.stringValue] = intValue
            } else if let doubleValue = try? decode(Double.self, forKey: key) {
                dictionary[key.stringValue] = doubleValue
            } else if let nestedDictionary = try? decode(Dictionary<String, Any>.self, forKey: key) {
                dictionary[key.stringValue] = nestedDictionary
            } else if let nestedArray = try? decode(Array<Any>.self, forKey: key) {
                dictionary[key.stringValue] = nestedArray
            }
        }
        return dictionary
    }
}

extension UnkeyedDecodingContainer {

    mutating func decode(_ type: Array<Any>.Type) throws -> Array<Any> {
        var array: [Any] = []
        while isAtEnd == false {
            // See if the current value in the JSON array is `null` first and prevent infite recursion with nested arrays.
            if try decodeNil() {
                continue
            } else if let value = try? decode(Bool.self) {
                array.append(value)
            } else if let value = try? decode(Double.self) {
                array.append(value)
            } else if let value = try? decode(String.self) {
                array.append(value)
            } else if let nestedDictionary = try? decode(Dictionary<String, Any>.self) {
                array.append(nestedDictionary)
            } else if let nestedArray = try? decode(Array<Any>.self) {
                array.append(nestedArray)
            }
        }
        return array
    }

    mutating func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {

        let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self)
        return try nestedContainer.decode(type)
    }
}

재미있는,이 요점을 먹으면 @loudmouth 당신에게 결과를 업데이트합니다
Pitiphong Phongpattranont

@PitiphongPhongpattranont이 코드가 당신을 위해 작동 했습니까?
loudmouth

1
의 마지막 상태 @JonBrooks UnkeyedDecodingContainer의은 decode(_ type: Array<Any>.Type) throws -> Array<Any>A에 대한 검사되는 중첩 배열. 따라서 다음과 같은 데이터 구조가있는 경우 [true, 452.0, ["a", "b", "c"] ] 중첩 된 ["a", "b", "c"]배열을 가져 옵니다. decode방법 UnkeyedDecodingContainer컨테이너로부터 요소 OFF "팝". 무한 재귀를 일으키지 않아야합니다.
loudmouth

1
@loudmouth json :에서 키에 대해 nil 값을 가질 수 있습니다 {"array": null}. 그래서 당신 guard contains(key)은 통과하지만 키 "배열"에 대한 null 값을 디코딩하려고 할 때 나중에 몇 줄 충돌합니다. 따라서을 호출하기 전에 값이 실제로 null이 아닌지 확인하는 조건을 하나 더 추가하는 것이 좋습니다 decode.
chebur

2
수정 사항을 찾았습니다. } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)시도 대신 :} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Jon Brooks

23

나도이 문제를 가지고 놀았고 마침내 "일반 JSON"유형으로 작업하기위한 간단한 라이브러리를 작성했습니다 . (여기서 "일반"은 "사전에 알려진 구조 없음"을 의미합니다.) 요점은 구체적인 유형으로 일반 JSON을 나타내는 것입니다.

public enum JSON {
    case string(String)
    case number(Float)
    case object([String:JSON])
    case array([JSON])
    case bool(Bool)
    case null
}

그런 다음이 유형은 CodableEquatable.


13

아래와 같은 디코드 방법을 사용하여 Decodable프로토콜 을 확인하는 메타 데이터 구조체를 생성 하고 JSONDecoder클래스를 사용하여 데이터로부터 객체를 생성 할 수 있습니다.

let json: [String: Any] = [
    "object": "customer",
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com",
    "metadata": [
        "link_id": "linked-id",
        "buy_count": 4
    ]
]

struct Customer: Decodable {
    let object: String
    let id: String
    let email: String
    let metadata: Metadata
}

struct Metadata: Decodable {
    let link_id: String
    let buy_count: Int
}

let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)

let decoder = JSONDecoder()
do {
    let customer = try decoder.decode(Customer.self, from: data)
    print(customer)
} catch {
    print(error.localizedDescription)
}

10
아니요, metadata가치 의 구조를 모르기 때문에 할 수 없습니다 . 임의의 개체가 될 수 있습니다.
Pitiphong Phongpattranont

배열 또는 사전 유형이 될 수 있음을 의미합니까?
Suhit Patil

당신은 예를 들어 줄 또는 메타 데이터 구조에 대한 자세한 explaination를 추가 할 수 있습니다
Suhit 파틸에게

2
의 값은 metadata모든 JSON 개체가 될 수 있습니다. 따라서 빈 사전이나 임의의 사전이 될 수 있습니다. "메타 데이터"{} "메타 데이터": {user_id를 : "ID"} "메타 데이터": {설정 : {shows_value : 사실, 언어 : "엉"}} 등
Pitiphong Phongpattranont

한 가지 가능한 옵션은 메타 데이터 구조체의 모든 매개 변수를 선택 사항으로 사용하고 메타 데이터 구조체의 가능한 모든 값을 struct metadata {var user_id : String? var 기본 설정 : 문자열? }
Suhit Patil

8

나는 약간 다른 해결책을 가지고 왔습니다.

[String: Any]Any가 배열, 중첩 된 사전 또는 배열의 사전 일 수 있다는 단순한 구문 분석 이상의 무언가가 있다고 가정 해 보겠습니다 .

이 같은:

var json = """
{
  "id": 12345,
  "name": "Giuseppe",
  "last_name": "Lanza",
  "age": 31,
  "happy": true,
  "rate": 1.5,
  "classes": ["maths", "phisics"],
  "dogs": [
    {
      "name": "Gala",
      "age": 1
    }, {
      "name": "Aria",
      "age": 3
    }
  ]
}
"""

글쎄, 이것이 내 해결책입니다.

public struct AnyDecodable: Decodable {
  public var value: Any

  private struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  public init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyDecodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyDecodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

사용해보십시오

let stud = try! JSONDecoder().decode(AnyDecodable.self, from: jsonData).value as! [String: Any]
print(stud)

6

이전 답변을 찾았을 때 간단한 JSON 객체 케이스 만 테스트했지만 @slurmomatic 및 @zoul과 같은 런타임 예외가 발생하는 빈 케이스는 테스트하지 않았습니다. 이 문제로 죄송합니다.

그래서 간단한 JSONValue 프로토콜을 사용하여 다른 방법을 시도하고 AnyJSONValue유형 삭제 구조체를 구현하고 대신 해당 유형을 사용합니다 Any. 다음은 구현입니다.

public protocol JSONType: Decodable {
    var jsonValue: Any { get }
}

extension Int: JSONType {
    public var jsonValue: Any { return self }
}
extension String: JSONType {
    public var jsonValue: Any { return self }
}
extension Double: JSONType {
    public var jsonValue: Any { return self }
}
extension Bool: JSONType {
    public var jsonValue: Any { return self }
}

public struct AnyJSONType: JSONType {
    public let jsonValue: Any

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let intValue = try? container.decode(Int.self) {
            jsonValue = intValue
        } else if let stringValue = try? container.decode(String.self) {
            jsonValue = stringValue
        } else if let boolValue = try? container.decode(Bool.self) {
            jsonValue = boolValue
        } else if let doubleValue = try? container.decode(Double.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Array<AnyJSONType>.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Dictionary<String, AnyJSONType>.self) {
            jsonValue = doubleValue
        } else {
            throw DecodingError.typeMismatch(JSONType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unsupported JSON tyep"))
        }
    }
}

디코딩 할 때 사용하는 방법은 다음과 같습니다.

metadata = try container.decode ([String: AnyJSONValue].self, forKey: .metadata)

이 문제의 문제는 value.jsonValue as? Int. 우리 Conditional Conformance는 Swift에 착륙 할 때까지 기다려야 합니다. 그러면이 문제가 해결되거나 적어도 더 나아질 수 있습니다.


[이전 답변]

이 질문을 Apple Developer 포럼에 게시했는데 매우 쉽습니다.

내가 할 수있는

metadata = try container.decode ([String: Any].self, forKey: .metadata)

이니셜 라이저에서.

애초에 그것을 놓친 것은 내 잘못이었습니다.


4
Apple Developer에 질문 링크를 게시 할 수 있습니다. Any준수하지 않으므로 Decodable이것이 어떻게 정답인지 잘 모르겠습니다.
Reza Shirazian

@RezaShirazian 그것이 내가 처음에 생각했던 것입니다. 그러나 Dictionary는 키가 Hashable을 따르고 값에 의존하지 않을 때 Encodable을 따르는 것으로 밝혀졌습니다. 사전 헤더를 열어 직접 확인할 수 있습니다. 확장 사전 : Encodable 경우 키 : 해쉬 확장 사전 : 복호 경우 키 : 해쉬 forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont

6
현재 이것은 작동하지 않습니다. "Dictionary <String, Any>는 Any가 Decodable을 따르지 않기 때문에 Decodable을 따르지 않습니다."
mbuchetics

작동하는 것으로 밝혀졌습니다. 내 코드에서 사용하고 있습니다. 지금은 "사전이 Decodable 프로토콜을 준수하도록하기 위해 사전 값이 Decodable 프로토콜을 준수해야합니다"라는 요구 사항을 표현할 방법이 없음을 이해해야합니다. 이것이 Swift 4에서 아직 구현되지 않은 "Conditional Conformance"입니다. Swift Type System (및 Generics)에 많은 제한이 있기 때문에 지금은 괜찮다고 생각합니다. 따라서 이것은 현재 작동하지만 향후 Swift Type System이 개선되면 (특히 Conditional Conformance가 구현 될 때) 작동하지 않아야합니다.
Pitiphong Phongpattranont

3
Xcode 9 베타 5에서는 작동하지 않습니다. 컴파일하지만 런타임에 폭발합니다. Dictionary <String, Any>는 Any가 Decodable을 따르지 않기 때문에 Decodable을 따르지 않습니다.
zoul

6

당신이 사용하는 경우 SwiftyJSON를 JSON을 구문 분석, 당신은 업데이트 할 수 있습니다 4.1.0Codable프로토콜 지원합니다. 선언 만하면 metadata: JSON모든 준비가 완료됩니다.

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}

이 답변이 반대표를받은 이유를 모르겠습니다. 완전히 유효하며 문제를 해결합니다.
Leonid Usov

SwiftyJSON에서 복호에 마이그레이션을위한 좋은 것 같다
마이클 Ziobro에게

이것은 원래 문제였던 메타 데이터 json을 구문 분석하는 방법을 해결하지 못합니다.
llamacorn

1

BeyovaJSON보셨을 것입니다.

import BeyovaJSON

struct Customer: Codable {
  let id: String
  let email: String
  let metadata: JToken
}

//create a customer instance

customer.metadata = ["link_id": "linked-id","buy_count": 4]

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted 
print(String(bytes: try! encoder.encode(customer), encoding: .utf8)!)

오, 정말 좋아요. 이를 사용하여 일반 JSON을 JToken으로 수신하고 일부 값을 추가하고 서버로 반환합니다. 참으로 아주 좋습니다. 즉, :)했던 멋진 일이다
빅토르 위고 Schwaab

1

가장 쉽고 제안되는 방법은 JSON 형식의 각 사전 또는 모델에 대해 별도의 모델만드는 것입니다 .

여기 내가하는 일

//Model for dictionary **Metadata**

struct Metadata: Codable {
    var link_id: String?
    var buy_count: Int?
}  

//Model for dictionary **Customer**

struct Customer: Codable {
   var object: String?
   var id: String?
   var email: String?
   var metadata: Metadata?
}

//Here is our decodable parser that decodes JSON into expected model

struct CustomerParser {
    var customer: Customer?
}

extension CustomerParser: Decodable {

//keys that matches exactly with JSON
enum CustomerKeys: String, CodingKey {
    case object = "object"
    case id = "id"
    case email = "email"
    case metadata = "metadata"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CustomerKeys.self) // defining our (keyed) container

    let object: String = try container.decode(String.self, forKey: .object) // extracting the data
    let id: String = try container.decode(String.self, forKey: .id) // extracting the data
    let email: String = try container.decode(String.self, forKey: .email) // extracting the data

   //Here I have used metadata model instead of dictionary [String: Any]
    let metadata: Metadata = try container.decode(Metadata.self, forKey: .metadata) // extracting the data

    self.init(customer: Customer(object: object, id: id, email: email, metadata: metadata))

    }
}

용법:

  if let url = Bundle.main.url(forResource: "customer-json-file", withExtension: "json") {
        do {
            let jsonData: Data =  try Data(contentsOf: url)
            let parser: CustomerParser = try JSONDecoder().decode(CustomerParser.self, from: jsonData)
            print(parser.customer ?? "null")

        } catch {

        }
    }

** 파싱하는 동안 안전한쪽에 있도록 선택 사항을 사용했으며 필요에 따라 변경할 수 있습니다.

이 주제에 대해 자세히 알아보기


1
귀하의 답변은 확실히 Swift 4.1에 적합한 답변이며 게시물의 첫 번째 줄은 끝났습니다! 데이터가 웹 서비스에서 온다고 가정합니다. 간단한 중첩 객체를 모델링 한 다음 도트 구문을 사용하여 각각을 잡을 수 있습니다. 아래 suhit의 답변을 참조하십시오.
David H

1

나는 디코딩 +는 인코딩 방법을 용이하게 포드를 만들었습니다 [String: Any], [Any]. 그리고 이것은 여기 https://github.com/levantAJ/AnyCodable 에서 선택적 속성을 인코딩하거나 디코딩합니다.

pod 'DynamicCodable', '1.0'

사용 방법:

import DynamicCodable

struct YourObject: Codable {
    var dict: [String: Any]
    var array: [Any]
    var optionalDict: [String: Any]?
    var optionalArray: [Any]?

    enum CodingKeys: String, CodingKey {
        case dict
        case array
        case optionalDict
        case optionalArray
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        dict = try values.decode([String: Any].self, forKey: .dict)
        array = try values.decode([Any].self, forKey: .array)
        optionalDict = try values.decodeIfPresent([String: Any].self, forKey: .optionalDict)
        optionalArray = try values.decodeIfPresent([Any].self, forKey: .optionalArray)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(dict, forKey: .dict)
        try container.encode(array, forKey: .array)
        try container.encodeIfPresent(optionalDict, forKey: .optionalDict)
        try container.encodeIfPresent(optionalArray, forKey: .optionalArray)
    }
}

0

여기에 더 일반적인 (뿐만 아니라 [String: Any],하지만 [Any]디코딩 할 수 있습니다) 및 @loudmouth 대답에서 영감을 (별도의 엔티티가 사용됩니다) 접근 방식을 캡슐화.

그것을 사용하면 다음과 같이 보일 것입니다.

extension Customer: Decodable {
  public init(from decoder: Decoder) throws {
    let selfContainer = try decoder.container(keyedBy: CodingKeys.self)
    id = try selfContainer.decode(.id)
    email = try selfContainer.decode(.email)
    let metadataContainer: JsonContainer = try selfContainer.decode(.metadata)
    guard let metadata = metadataContainer.value as? [String: Any] else {
      let context = DecodingError.Context(codingPath: [CodingKeys.metadata], debugDescription: "Expected '[String: Any]' for 'metadata' key")
      throw DecodingError.typeMismatch([String: Any].self, context)
    }
    self.metadata = metadata
  }

  private enum CodingKeys: String, CodingKey {
    case id, email, metadata
  }
}

JsonContainer확장하지 않고 JSON 데이터 디코딩을 JSON 객체 (배열 또는 사전)로 래핑하는 데 사용하는 도우미 엔티티입니다. *DecodingContainer따라서 JSON 객체가를 의미하지 않는 드문 경우를 방해하지 않습니다 [String: Any].

struct JsonContainer {

  let value: Any
}

extension JsonContainer: Decodable {

  public init(from decoder: Decoder) throws {
    if let keyedContainer = try? decoder.container(keyedBy: Key.self) {
      var dictionary = [String: Any]()
      for key in keyedContainer.allKeys {
        if let value = try? keyedContainer.decode(Bool.self, forKey: key) {
          // Wrapping numeric and boolean types in `NSNumber` is important, so `as? Int64` or `as? Float` casts will work
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Int64.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Double.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(String.self, forKey: key) {
          dictionary[key.stringValue] = value
        } else if (try? keyedContainer.decodeNil(forKey: key)) ?? false {
          // NOP
        } else if let value = try? keyedContainer.decode(JsonContainer.self, forKey: key) {
          dictionary[key.stringValue] = value.value
        } else {
          throw DecodingError.dataCorruptedError(forKey: key, in: keyedContainer, debugDescription: "Unexpected value for \(key.stringValue) key")
        }
      }
      value = dictionary
    } else if var unkeyedContainer = try? decoder.unkeyedContainer() {
      var array = [Any]()
      while !unkeyedContainer.isAtEnd {
        let container = try unkeyedContainer.decode(JsonContainer.self)
        array.append(container.value)
      }
      value = array
    } else if let singleValueContainer = try? decoder.singleValueContainer() {
      if let value = try? singleValueContainer.decode(Bool.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Int64.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Double.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(String.self) {
        self.value = value
      } else if singleValueContainer.decodeNil() {
        value = NSNull()
      } else {
        throw DecodingError.dataCorruptedError(in: singleValueContainer, debugDescription: "Unexpected value")
      }
    } else {
      let context = DecodingError.Context(codingPath: [], debugDescription: "Invalid data format for JSON")
      throw DecodingError.dataCorrupted(context)
    }
  }

  private struct Key: CodingKey {
    var stringValue: String

    init?(stringValue: String) {
      self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
      self.init(stringValue: "\(intValue)")
      self.intValue = intValue
    }
  }
}

숫자 및 부울 유형은에서 지원되며 NSNumber, 그렇지 않으면 다음과 같이 작동하지 않습니다.

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil

autoDecoding으로 충분한 15 개의 속성과 사용자 지정 디코딩 처리가 필요한 3 개의 속성이 있으므로 선택한 속성 만 디코딩하고 다른 속성은 자동으로 디코딩 된 상태로 둘 수 있습니까?
Michał Ziobro

@ MichałZiobro 데이터의 일부를 JSON 객체로 디코딩하고 일부를 별도의 인스턴스 변수로 디코딩 하시겠습니까? 아니면 객체의 일부에 대해서만 부분 디코딩 이니셜 라이저를 작성하는 것에 대해 질문하고 있습니까 (그리고 구조와 같은 JSON과 공통점이 없음)? 내가 아는 한 첫 번째 질문에 대한 대답은 '예'이고 두 번째 질문에 대한 대답은 '아니오'입니다.
Alexey Kozhevnikov

난 단지 사용자 디코딩 일부 속성과 표준 기본 디코딩 나머지하고 싶은
마이클 Ziobro

@ MichałZiobro 내가 당신을 이해한다면 그것은 불가능합니다. 어쨌든 귀하의 질문은 현재 SO 질문과 관련이 없으며 별도의 질문에 해당합니다.
Alexey Kozhevnikov

0

디코더 및 코딩 키를 사용하여 디코딩

public let dataToDecode: [String: AnyDecodable]

enum CodingKeys: CodingKey {
    case dataToDecode
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.dataToDecode = try container.decode(Dictionary<String, AnyDecodable>.self, forKey: .dataToDecode) 
}    

-1
extension ViewController {

    func swiftyJson(){
        let url = URL(string: "https://itunes.apple.com/search?term=jack+johnson")
        //let url = URL(string: "http://makani.bitstaging.in/api/business/businesses_list")

        Alamofire.request(url!, method: .get, parameters: nil).responseJSON { response in
            var arrayIndexes = [IndexPath]()
            switch(response.result) {
            case .success(_):

                let data = response.result.value as! [String : Any]

                if let responseData =  Mapper<DataModel>().map(JSON: data) {
                    if responseData.results!.count > 0{
                        self.arrayExploreStylistList = []
                    }
                    for i in 0..<responseData.results!.count{
                        arrayIndexes.append(IndexPath(row: self.arrayExploreStylistList.count + i, section: 0))
                    }
                    self.arrayExploreStylistList.append(contentsOf: responseData.results!)

                    print(arrayIndexes.count)

                }

                //                    if let arrNew = data["results"] as? [[String : Any]]{
                //                        let jobData = Mapper<DataModel>().mapArray(JSONArray: arrNew)
                //                        print(jobData)
                //                        self.datamodel = jobData
                //                    }
                self.tblView.reloadData()
                break

            case .failure(_):
                print(response.result.error as Any)
                break

            }
        }

    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.