首先,PHP 不需要这样做,因此如果可能,应该更正 PHP。 PHP 中表示“空对象”的方式是new \stdClass()。 Elastic has a nice explanation.
也就是说,如果您无法更正服务器,您可以在客户端进行修复。这里的许多答案都是基于尝试解码该值,如果失败则假设它是一个空数组。这行得通,但这意味着意外的 JSON 不会产生好的错误。相反,我会将这个问题提取到一个函数中:
/// Some PHP developers emit [] to indicate empty object rather than using stdClass().
/// This returns a default value in that case.
extension KeyedDecodingContainer {
func decodePHPObject<T>(_ type: T.Type, forKey key: Key, defaultValue: T) throws -> T
where T : Decodable
{
// Sadly neither Void nor Never conform to Decodable, so we have to pick a random type here, String.
// The nicest would be to decode EmptyCollection, but that also doesn't conform to Decodable.
if let emptyArray = try? decode([String].self, forKey: key), emptyArray.isEmpty {
return defaultValue
}
return try decode(T.self, forKey: key)
}
// Special case the common dictionary situation.
func decodePHPObject<K, V>(_ type: [K: V].Type, forKey key: Key) throws -> [K: V]
where K: Codable, V: Codable
{
return try decodePHPObject([K:V].self, forKey: key, defaultValue: [:])
}
}
这提供了一个.decodePHPObject(_:forKey:) 方法,您可以在自定义解码器中使用该方法。
public struct ErrorValue: Codable {
public let code: String
public let message: String
public let params: [String: String]
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.code = try container.decode(String.self, forKey: .code)
self.message = try container.decode(String.self, forKey: .message)
// And use our new decodePHPObject.
self.params = try container.decodePHPObject([String: String].self, forKey: .params)
}
}
(我已重命名此 ErrorValue 以消除与 stdlib 类型 Error 的冲突,并且我已将 params 设为非可选,因为您通常不应该有可选集合,除非“空”将与 nil 区别对待。)