使用Swift 3 JSONSerialization
,如果您的数据模型的一部分是完全动态的,您可以随时将反序列化数据保留在Any
并让消费者处理它。
使用Swift 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
,那么仅此一点就不会成为世界末日,但这需要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 :(得分:11)
我最终必须实现自己的类来编码/解码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
}
}
答案 1 :(得分:3)
您只需使用Matt Thompson的酷库AnyCodable中的AnyCodable
类型。
例如:
import AnyCodable
struct Foo: Codable
{
var bar: AnyCodable
}
答案 2 :(得分:1)
您可以尝试BeyovaJSON
import BeyovaJSON
struct Foo: Codable {
let bar: JToken
}
let foo = try! JSONDecoder().decode(Foo.self, from: data)
答案 3 :(得分:0)
我解决了创建AnyValue结构以允许对JSON中的 Any 值进行编码和解码的问题:
要使用它非常简单:
class MyClass: Codable {
var data: [String: AnyValue?]?
init(data: [String: AnyValue?]) {
self.data = data
}
}
let data = ["a": AnyValue(3), "b": AnyValue(true), "c": AnyValue("Rodrigo"), "d": AnyValue(3.3)]
let myClass = MyClass(data: data)
if let json = JsonUtil<MyClass>.toJson(myClass) {
print(json) // {"data":{"d":3.3,"b":true,"c":"Rodrigo","a":3}}
if let data = JsonUtil<MyClass>.from(json: json)?.data {
print(data["a"]??.value() ?? "nil") // 3
print(data["b"]??.value() ?? "nil") // true
print(data["c"]??.value() ?? "nil") // Rodrigo
print(data["d"]??.value() ?? "nil") // 3.3
}
}
AnyValue结构:
struct AnyValue: Codable {
private var int: Int?
private var string: String?
private var bool: Bool?
private var double: Double?
init(_ int: Int) {
self.int = int
}
init(_ string: String) {
self.string = string
}
init(_ bool: Bool) {
self.bool = bool
}
init(_ double: Double) {
self.double = double
}
init(from decoder: Decoder) throws {
if let int = try? decoder.singleValueContainer().decode(Int.self) {
self.int = int
return
}
if let string = try? decoder.singleValueContainer().decode(String.self) {
self.string = string
return
}
if let bool = try? decoder.singleValueContainer().decode(Bool.self) {
self.bool = bool
return
}
if let double = try? decoder.singleValueContainer().decode(Double.self) {
self.double = double
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let anyValue = self.value() {
if let value = anyValue as? Int {
try container.encode(value)
return
}
if let value = anyValue as? String {
try container.encode(value)
return
}
if let value = anyValue as? Bool {
try container.encode(value)
return
}
if let value = anyValue as? Double {
try container.encode(value)
return
}
}
try container.encodeNil()
}
func value() -> Any? {
return self.int ?? self.string ?? self.bool ?? self.double
}
}
我也创建了一个 JsonUtil结构:
struct JsonUtil<T: Codable> {
public static func from(json: String) -> T? {
if let jsonData = json.data(using: .utf8) {
let jsonDecoder = JSONDecoder()
do {
return try jsonDecoder.decode(T.self, from: jsonData)
} catch {
print(error)
}
}
return nil
}
public static func toJson(_ obj: T) -> String? {
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(obj)
return String(data: jsonData, encoding: String.Encoding.utf8)
} catch {
print(error)
return nil
}
}
}
如果每个示例都需要像 [String] 这样的新类型,则只需将其添加到 AnyValue 结构上即可。
祝你好运:)