2017-09-18 9 views
0

スウィフト3 JSONSerializationを使用すると、データモデルの一部が完全に動的だった場合は、逆シリアル化されたデータをそのままAnyに置き、消費者に対処させることができます。スウィフト4のCodableJSONと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を所有していますが、それにはDecoderのデコードをAnyにする必要があります。たとえば:

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) 
関連する問題