2017-09-18 5 views
0

Swift 3 JSONSerialization을 사용하면 데이터 모델의 일부가 완전히 동적 인 경우 deserialize 된 데이터를 항상 Any에두고 소비자가 처리하도록 할 수 있습니다.JSON을 코드 가능으로 디코딩 할 때 어떤 것이 있습니까?

스위프트 4의 Codable

, 나는이 일을하고 싶습니다 :

struct Foo : Codable { 
    let bar: Any; 
} 

하지만 내가 구현할 수 있다면 혼자 세상의 종말이되지 않을 것

JSONPlayground.playground:4:9: note: cannot automatically synthesize 'Decodable' because 'Any' does not conform to 'Decodable' 
    let bar: Any; 
     ^

그건 얻을 내 자신의 Decodable,하지만 그 DecoderAny로 디코딩을 지원해야 할 것입니다, 지금까지 내가 말할 수 없다. 예를 들어 :

extension Foo { 
    init(from decoder: Decoder) throws { 
     let container = try decoder.singleValueContainer() 
     let result = try container.decode(AnyClass.self) 
    } 
} 

error: JSONPlayground.playground:4:36: error: cannot invoke 'decode' with an argument list of type '(AnyClass.Protocol)' 
     let result = try container.decode(AnyClass.self) 
           ^

는 모든 솔루션이 여기에 있는가 준다?

+0

BeyovaJSON 당신이'Any'를 해독하려고 노력으로 운이있을 것 같아요 시도해 볼 수도 있지만, 어쩌면 당신은 단지에 줄을 변환 시도 할 수 있습니다 JSON 문자열을 저장 한 다음 저장하십시오. – TNguyen

+0

'Codable'은 숫자를 문자열로 디코딩하지 않습니다. –

+0

왜 'Any'가 필요한가요? –

답변

3

Any 값을 인코딩/디코딩하기 위해 자체 클래스를 구현해야했습니다. 그것은 꽤 아니지만, 작동하는 것 같다 :

class JSONAny: Codable { 
    public 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: MyCodingKey.self) { 
      return try decodeDictionary(from: &container) 
     } 
     throw decodingError(forCodingPath: container.codingPath) 
    } 

    static func decode(from container: inout KeyedDecodingContainer, forKey key: MyCodingKey) 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: MyCodingKey.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) 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: MyCodingKey.self) 
       try encode(to: &container, dictionary: value) 
      } else { 
       throw encodingError(forValue: value, codingPath: container.codingPath) 
      } 
     } 
    } 

    static func encode(to container: inout KeyedEncodingContainer, dictionary: [String: Any]) throws { 
     for (key, value) in dictionary { 
      let key = MyCodingKey(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: MyCodingKey.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: MyCodingKey.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: MyCodingKey.self) 
      try JSONAny.encode(to: &container, dictionary: dict) 
     } else { 
      var container = encoder.singleValueContainer() 
      try JSONAny.encode(to: &container, value: self.value) 
     } 
    } 
} 

class JSONNull: Codable { 
    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 MyCodingKey : 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 
    } 
}
+0

을 참조하십시오. 좋습니다. 고맙습니다. KeyedEncodingContainer에 을 추가하기 만하면 빌드 오류가 발생했습니다. – Yaroslav

1

당신은

import BeyovaJSON 

struct Foo: Codable { 
    let bar: JToken 
} 

let foo = try! JSONDecoder().decode(Foo.self, from: data)