Cách giải mã một thuộc tính với loại từ điển JSON trong giao thức có thể giải mã Swift 4


103

Giả sử tôi có Customerkiểu dữ liệu chứa thuộc metadatatính có thể chứa bất kỳ từ điển JSON nào trong đối tượng khách hàng

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
  }
}

Các metadatabất động sản có thể được bất kỳ đối tượng bản đồ JSON tùy ý.

Trước khi tôi có thể truyền thuộc tính từ JSON được deserialized từ NSJSONDeserializationnhưng với Decodablegiao thức Swift 4 mới , tôi vẫn không thể nghĩ ra cách để thực hiện điều đó.

Có ai biết cách đạt được điều này trong Swift 4 với giao thức Có thể giải mã không?

Câu trả lời:


88

Với một số cảm hứng từ ý chính mà tôi tìm thấy, tôi đã viết một số tiện ích mở rộng cho UnkeyedDecodingContainerKeyedDecodingContainer. Bạn có thể tìm thấy một liên kết đến ý chính của tôi ở đây . Bằng cách sử dụng mã này, bây giờ bạn có thể giải mã bất kỳ Array<Any>hoặc Dictionary<String, Any>bằng cú pháp quen thuộc:

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

hoặc là

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

Chỉnh sửa: có một cảnh báo tôi đã tìm thấy đó là giải mã một mảng từ điển [[String: Any]]Cú pháp bắt buộc như sau. Bạn có thể muốn tạo ra một lỗi thay vì ép buộc truyền:

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

CHỈNH SỬA 2: Nếu bạn chỉ muốn chuyển đổi toàn bộ tệp sang từ điển, tốt hơn hết bạn nên sử dụng api từ JSONSerialization vì tôi chưa tìm ra cách mở rộng chính JSONDecoder để giải mã trực tiếp từ điển.

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

Các phần mở rộng

// 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)
    }
}

Thật thú vị, tôi sẽ thử ý chính này và sẽ cập nhật kết quả cho bạn @loudmouth
Pitiphong Phongpattranont

@PitiphongPhongpattranont mã này có hiệu quả với bạn không?
tomouth

1
@JonBrooks điều kiện cuối cùng trong trong UnkeyedDecodingContainerdecode(_ type: Array<Any>.Type) throws -> Array<Any>được kiểm tra cho một lồng mảng. Vì vậy, nếu bạn có một cấu trúc dữ liệu giống như sau: [true, 452.0, ["a", "b", "c"] ] Nó sẽ kéo ["a", "b", "c"]mảng lồng nhau . Các decodephương pháp của một UnkeyedDecodingContainer"pops" tắt các yếu tố từ container. Nó không nên gây ra đệ quy vô hạn.
tomouth,

1
@loudmouth, có thể có giá trị nil cho các khóa trong json : {"array": null}. Vì vậy, ý chí của bạn guard contains(key)sẽ vượt qua nhưng nó sẽ bị lỗi vài dòng sau đó khi cố gắng giải mã giá trị null cho khóa "mảng". Vì vậy, tốt hơn là thêm một điều kiện nữa để kiểm tra xem giá trị có thực sự không phải là null hay không trước khi gọi decode.
chebur

2
Tôi đã tìm thấy một bản sửa lỗi: Thay vì } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)hãy thử:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Jon Brooks

23

Tôi cũng đã từng giải quyết vấn đề này và cuối cùng đã viết một thư viện đơn giản để làm việc với các loại "JSON chung" . (Trong đó “chung chung” có nghĩa là “không có cấu trúc nào được biết trước”.) Điểm chính là đại diện cho JSON chung với một kiểu cụ thể:

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

Loại này sau đó có thể thực hiện CodableEquatable.


13

Bạn có thể tạo cấu trúc siêu dữ liệu xác nhận Decodablegiao thức và sử dụng JSONDecoderlớp để tạo đối tượng từ dữ liệu bằng cách sử dụng phương thức giải mã như bên dưới

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
Không, tôi không thể, vì tôi không biết cấu trúc của metadatagiá trị. Nó có thể là bất kỳ đối tượng tùy ý nào.
Pitiphong Phongpattranont

Ý bạn là nó có thể là kiểu Array hoặc Dictionary?
Suhit Patil

bạn có thể cho ví dụ hoặc giải thích thêm về cấu trúc siêu dữ liệu không
Suhit Patil

2
Giá trị của metadatacó thể là bất kỳ đối tượng JSON nào. Vì vậy, nó có thể là từ điển trống hoặc bất kỳ từ điển nào. "metadata": {} "metadata": {user_id: "id"} "metadata": {favourite: {shows_value: true, language: "en"}}, v.v.
Pitiphong Phongpattranont

một tùy chọn khả thi là sử dụng tất cả các tham số trong cấu trúc siêu dữ liệu làm tùy chọn và liệt kê tất cả các giá trị có thể có trong cấu trúc siêu dữ liệu như siêu dữ liệu struct {var user_id: String? sở thích var: Chuỗi? }
Suhit Patil

8

Tôi đã đến với một giải pháp hơi khác.

Giả sử chúng ta có một cái gì đó không chỉ đơn giản [String: Any]để phân tích cú pháp là Bất kỳ có thể là một mảng hoặc một từ điển lồng nhau hoặc một từ điển các mảng.

Một cái gì đó như thế này:

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
    }
  ]
}
"""

Vâng, đây là giải pháp của tôi:

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"))
    }
  }
}

Hãy thử nó bằng cách sử dụng

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

6

Khi tôi tìm thấy câu trả lời cũ, tôi chỉ thử nghiệm trường hợp đối tượng JSON đơn giản chứ không phải trường hợp trống sẽ gây ra ngoại lệ thời gian chạy như @slurmomatic và @zoul được tìm thấy. Xin lỗi vì vấn đề này.

Vì vậy, tôi thử một cách khác bằng cách có một giao thức JSONValue đơn giản, triển khai AnyJSONValuecấu trúc xóa kiểu và sử dụng kiểu đó thay vì Any. Đây là một triển khai.

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"))
        }
    }
}

Và đây là cách sử dụng nó khi giải mã

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

Vấn đề với vấn đề này là chúng tôi phải gọi value.jsonValue as? Int. Chúng tôi cần đợi cho đến khi Conditional Conformancehạ cánh trong Swift, điều đó sẽ giải quyết được vấn đề này hoặc ít nhất là giúp nó tốt hơn.


[Câu trả lời cũ]

Tôi đăng câu hỏi này trên diễn đàn Nhà phát triển Apple và hóa ra nó rất dễ dàng.

tôi có thể làm

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

trong trình khởi tạo.

Thật là tệ khi tôi bỏ lỡ điều đó ngay từ đầu.


4
Có thể đăng liên kết đến câu hỏi trên Apple Developer. Anykhông phù hợp với Decodablevì vậy tôi không chắc đây là câu trả lời chính xác.
Reza Shirazian,

@RezaShirazian Đó là những gì tôi nghĩ ngay từ đầu. Nhưng hóa ra Từ điển phù hợp với Mã hóa khi các khóa của nó phù hợp với Có thể sửa chữa và không phụ thuộc vào giá trị của nó. Bạn có thể mở tiêu đề Từ điển và tự mình xem. mở rộng từ điển: Encodable nơi chính: mở rộng Hashable điển: giải mã nơi chính: Hashable forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont

6
hiện tại điều này không hoạt động. "Từ điển <String, Bất kỳ> không phù hợp với giải mã vì Bất kỳ không phù hợp với giải mã"
mbuchetics

Hóa ra nó hoạt động. Tôi đang sử dụng nó trong mã của mình. Bạn cần hiểu rằng không có cách nào để diễn đạt yêu cầu rằng "Giá trị của Từ điển phải phù hợp với giao thức Có thể giải mã để làm cho Từ điển phù hợp với giao thức Có thể giải mã". Đó là "Tuân thủ có điều kiện" vẫn chưa được triển khai trong Swift 4 Tôi nghĩ bây giờ vẫn ổn vì có rất nhiều hạn chế trong Hệ thống kiểu Swift (và Generics). Vì vậy, điều này hoạt động ngay bây giờ nhưng khi Hệ thống kiểu Swift cải thiện trong tương lai (đặc biệt là khi Tuân thủ có điều kiện được triển khai), điều này sẽ không hoạt động.
Pitiphong Phongpattranont

3
Không hoạt động với tôi kể từ Xcode 9 beta 5. Biên dịch, nhưng bị nổ tung trong thời gian chạy: Từ điển <Chuỗi, Bất kỳ> không phù hợp với Có thể giải mã vì Bất kỳ không phù hợp với Có thể giải mã.
zoul

6

Nếu bạn sử dụng SwiftyJSON để phân tích cú pháp JSON, bạn có thể cập nhật lên 4.1.0Codablehỗ trợ giao thức. Chỉ cần khai báo metadata: JSONvà bạn đã sẵn sàng.

import SwiftyJSON

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

Tôi không biết tại sao câu trả lời này bị phản đối. Nó hoàn toàn hợp lệ và giải quyết được vấn đề.
Leonid Usov

Nó có vẻ là tốt để di chuyển từ SwiftyJSON để giải mã
Michał Ziobro

Điều này không giải quyết được cách phân tích cú pháp json siêu dữ liệu là vấn đề ban đầu.
llamacorn

1

Bạn có thể xem qua 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)!)

Ồ, rất tuyệt. Sử dụng nó để nhận JSON chung là JToken, thêm một số giá trị và quay trở lại máy chủ. Thực sự rất tốt. Đó là công việc tuyệt vời mà bạn đã làm :)
Vitor Hugo Schwaab

1

Cách dễ nhất và được đề xuất là tạo mô hình riêng biệt cho từng từ điển hoặc mô hình trong JSON .

Đây là những gì tôi làm

//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))

    }
}

Sử dụng:

  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 {

        }
    }

** Tôi đã sử dụng tùy chọn để ở bên an toàn trong khi phân tích cú pháp, có thể được thay đổi khi cần thiết.

Đọc thêm về chủ đề này


1
Câu trả lời của bạn chắc chắn là câu trả lời thích hợp cho Swift 4.1 và dòng đầu tiên của bài đăng của bạn đã chết! Giả sử dữ liệu đến từ một dịch vụ web. bạn có thể mô hình hóa các đối tượng lồng nhau đơn giản sau đó sử dụng cú pháp dấu chấm để lấy từng đối tượng. Xem câu trả lời của suhit bên dưới.
David H

1

Tôi đã tạo một nhóm để tạo thuận lợi cho cách giải mã + mã hóa [String: Any], [Any]. Và điều này cung cấp mã hóa hoặc giải mã các thuộc tính tùy chọn, tại đây https://github.com/levantAJ/AnyCodable

pod 'DynamicCodable', '1.0'

Làm thế nào để sử dụng nó:

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

Đây là cách tiếp cận chung chung hơn (không chỉ [String: Any], mà [Any]có thể giải mã) và cách tiếp cận đóng gói (thực thể riêng biệt được sử dụng cho điều đó) lấy cảm hứng từ câu trả lời @loudmouth.

Sử dụng nó sẽ giống như sau:

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
  }
}

JsonContainerlà một thực thể trợ giúp mà chúng tôi sử dụng để bọc dữ liệu JSON giải mã thành đối tượng JSON (mảng hoặc từ điển) mà không cần mở rộng *DecodingContainer(vì vậy nó sẽ không gây trở ngại trong các trường hợp hiếm khi không có đối tượng 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
    }
  }
}

Lưu ý rằng các kiểu numberic và boolean được hỗ trợ bởi NSNumbernhững thứ khác như thế này sẽ không hoạt động:

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

Tôi có thể chỉ giải mã các thuộc tính đã chọn và để lại các thuộc tính khác được giải mã tự động vì tôi có 15 thuộc tính đủ để tự động Giải mã và có thể 3 thuộc tính cần một số xử lý giải mã tùy chỉnh không?
Michał Ziobro

@ MichałZiobro Bạn có muốn một phần dữ liệu được giải mã thành đối tượng JSON và một phần của nó được giải mã thành các biến phiên bản riêng biệt không? Hoặc bạn đang hỏi về việc viết bộ khởi tạo giải mã từng phần chỉ cho một phần của đối tượng (và nó không có điểm chung nào với cấu trúc như JSON)? Theo hiểu biết của tôi, câu trả lời cho câu hỏi đầu tiên là có, câu hỏi thứ hai là không.
Alexey Kozhevnikov 20/02/18

Tôi chỉ muốn có một số thuộc tính với giải mã tùy chỉnh và phần còn lại với giải mã mặc định tiêu chuẩn
Michał Ziobro

@ MichałZiobro Nếu tôi hiểu bạn đúng thì không thể. Dù sao, câu hỏi của bạn không liên quan đến câu hỏi SO hiện tại và có giá trị riêng.
Alexey Kozhevnikov

0

giải mã bằng cách sử dụng bộ giải mã và khóa mã hóa

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

            }
        }

    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.