Skip to content

Instantly share code, notes, and snippets.

@rnapier
Created April 15, 2023 19:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rnapier/069e5a5505066fdc2df667c1239fd5e0 to your computer and use it in GitHub Desktop.
Save rnapier/069e5a5505066fdc2df667c1239fd5e0 to your computer and use it in GitHub Desktop.
__JSONDecoder made public
import Foundation
extension JSONDecoder {
struct _Options {
let dateDecodingStrategy: DateDecodingStrategy
let dataDecodingStrategy: DataDecodingStrategy
let nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy
let keyDecodingStrategy: KeyDecodingStrategy
let userInfo: [CodingUserInfoKey : Any]
}
}
private var _iso8601Formatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = .withInternetDateTime
return formatter
}()
internal protocol _JSONStringDictionaryDecodableMarker {
static var elementType: Decodable.Type { get }
}
extension DecodingError {
/// Returns a `.typeMismatch` error describing the expected type.
///
/// - parameter path: The path of `CodingKey`s taken to decode a value of this type.
/// - parameter expectation: The type expected to be encountered.
/// - parameter reality: The value that was encountered instead of the expected type.
/// - returns: A `DecodingError` with the appropriate path and debug description.
internal static func _typeMismatch(at path: [CodingKey], expectation: Any.Type, reality: Any) -> DecodingError {
let description = "Expected to decode \(expectation) but found \(_typeDescription(of: reality)) instead."
return .typeMismatch(expectation, Context(codingPath: path, debugDescription: description))
}
/// Returns a description of the type of `value` appropriate for an error message.
///
/// - parameter value: The value whose type to describe.
/// - returns: A string describing `value`.
/// - precondition: `value` is one of the types below.
private static func _typeDescription(of value: Any) -> String {
if value is NSNull {
return "a null value"
} else if value is NSNumber /* FIXME: If swift-corelibs-foundation isn't updated to use NSNumber, this check will be necessary: || value is Int || value is Double */ {
return "a number"
} else if value is String {
return "a string/data"
} else if value is [Any] {
return "an array"
} else if value is [String : Any] {
return "a dictionary"
} else {
return "\(type(of: value))"
}
}
}
extension JSONDecoder.KeyDecodingStrategy {
fileprivate static func _convertFromSnakeCase(_ stringKey: String) -> String {
guard !stringKey.isEmpty else { return stringKey }
// Find the first non-underscore character
guard let firstNonUnderscore = stringKey.firstIndex(where: { $0 != "_" }) else {
// Reached the end without finding an _
return stringKey
}
// Find the last non-underscore character
var lastNonUnderscore = stringKey.index(before: stringKey.endIndex)
while lastNonUnderscore > firstNonUnderscore && stringKey[lastNonUnderscore] == "_" {
stringKey.formIndex(before: &lastNonUnderscore)
}
let keyRange = firstNonUnderscore...lastNonUnderscore
let leadingUnderscoreRange = stringKey.startIndex..<firstNonUnderscore
let trailingUnderscoreRange = stringKey.index(after: lastNonUnderscore)..<stringKey.endIndex
let components = stringKey[keyRange].split(separator: "_")
let joinedString : String
if components.count == 1 {
// No underscores in key, leave the word as is - maybe already camel cased
joinedString = String(stringKey[keyRange])
} else {
joinedString = ([components[0].lowercased()] + components[1...].map { $0.capitalized }).joined()
}
// Do a cheap isEmpty check before creating and appending potentially empty strings
let result : String
if (leadingUnderscoreRange.isEmpty && trailingUnderscoreRange.isEmpty) {
result = joinedString
} else if (!leadingUnderscoreRange.isEmpty && !trailingUnderscoreRange.isEmpty) {
// Both leading and trailing underscores
result = String(stringKey[leadingUnderscoreRange]) + joinedString + String(stringKey[trailingUnderscoreRange])
} else if (!leadingUnderscoreRange.isEmpty) {
// Just leading
result = String(stringKey[leadingUnderscoreRange]) + joinedString
} else {
// Just trailing
result = joinedString + String(stringKey[trailingUnderscoreRange])
}
return result
}
}
extension JSONEncoder.KeyEncodingStrategy {
fileprivate static func _convertToSnakeCase(_ stringKey: String) -> String {
guard !stringKey.isEmpty else { return stringKey }
var words : [Range<String.Index>] = []
// The general idea of this algorithm is to split words on transition from lower to upper case, then on transition of >1 upper case characters to lowercase
//
// myProperty -> my_property
// myURLProperty -> my_url_property
//
// We assume, per Swift naming conventions, that the first character of the key is lowercase.
var wordStart = stringKey.startIndex
var searchRange = stringKey.index(after: wordStart)..<stringKey.endIndex
// Find next uppercase character
while let upperCaseRange = stringKey.rangeOfCharacter(from: CharacterSet.uppercaseLetters, options: [], range: searchRange) {
let untilUpperCase = wordStart..<upperCaseRange.lowerBound
words.append(untilUpperCase)
// Find next lowercase character
searchRange = upperCaseRange.lowerBound..<searchRange.upperBound
guard let lowerCaseRange = stringKey.rangeOfCharacter(from: CharacterSet.lowercaseLetters, options: [], range: searchRange) else {
// There are no more lower case letters. Just end here.
wordStart = searchRange.lowerBound
break
}
// Is the next lowercase letter more than 1 after the uppercase? If so, we encountered a group of uppercase letters that we should treat as its own word
let nextCharacterAfterCapital = stringKey.index(after: upperCaseRange.lowerBound)
if lowerCaseRange.lowerBound == nextCharacterAfterCapital {
// The next character after capital is a lower case character and therefore not a word boundary.
// Continue searching for the next upper case for the boundary.
wordStart = upperCaseRange.lowerBound
} else {
// There was a range of >1 capital letters. Turn those into a word, stopping at the capital before the lower case character.
let beforeLowerIndex = stringKey.index(before: lowerCaseRange.lowerBound)
words.append(upperCaseRange.lowerBound..<beforeLowerIndex)
// Next word starts at the capital before the lowercase we just found
wordStart = beforeLowerIndex
}
searchRange = lowerCaseRange.upperBound..<searchRange.upperBound
}
words.append(wordStart..<searchRange.upperBound)
let result = words.map({ (range) in
return stringKey[range].lowercased()
}).joined(separator: "_")
return result
}
}
class __JSONDecoder : Decoder {
// MARK: Properties
/// The decoder's storage.
var storage: _JSONDecodingStorage
/// Options set on the top-level decoder.
let options: JSONDecoder._Options
/// The path to the current point in encoding.
fileprivate(set) public var codingPath: [CodingKey]
/// Contextual user-provided information for use during encoding.
public var userInfo: [CodingUserInfoKey : Any] {
return self.options.userInfo
}
// MARK: - Initialization
/// Initializes `self` with the given top-level container and options.
init(referencing container: Any, at codingPath: [CodingKey] = [], options: JSONDecoder._Options) {
self.storage = _JSONDecodingStorage()
self.storage.push(container: container)
self.codingPath = codingPath
self.options = options
}
// MARK: - Decoder Methods
public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
guard !(self.storage.topContainer is NSNull) else {
throw DecodingError.valueNotFound(KeyedDecodingContainer<Key>.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get keyed decoding container -- found null value instead."))
}
guard let topContainer = self.storage.topContainer as? [String : Any] else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: self.storage.topContainer)
}
let container = _JSONKeyedDecodingContainer<Key>(referencing: self, wrapping: topContainer)
return KeyedDecodingContainer(container)
}
public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
guard !(self.storage.topContainer is NSNull) else {
throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get unkeyed decoding container -- found null value instead."))
}
guard let topContainer = self.storage.topContainer as? [Any] else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: self.storage.topContainer)
}
return _JSONUnkeyedDecodingContainer(referencing: self, wrapping: topContainer)
}
public func singleValueContainer() throws -> SingleValueDecodingContainer {
return self
}
}
// MARK: - Decoding Storage
struct _JSONDecodingStorage {
// MARK: Properties
/// The container stack.
/// Elements may be any one of the JSON types (NSNull, NSNumber, String, Array, [String : Any]).
private(set) var containers: [Any] = []
// MARK: - Initialization
/// Initializes `self` with no containers.
init() {}
// MARK: - Modifying the Stack
var count: Int {
return self.containers.count
}
var topContainer: Any {
precondition(!self.containers.isEmpty, "Empty container stack.")
return self.containers.last!
}
mutating func push(container: __owned Any) {
self.containers.append(container)
}
mutating func popContainer() {
precondition(!self.containers.isEmpty, "Empty container stack.")
self.containers.removeLast()
}
}
// MARK: Decoding Containers
private struct _JSONKeyedDecodingContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
typealias Key = K
// MARK: Properties
/// A reference to the decoder we're reading from.
private let decoder: __JSONDecoder
/// A reference to the container we're reading from.
private let container: [String : Any]
/// The path of coding keys taken to get to this point in decoding.
private(set) public var codingPath: [CodingKey]
// MARK: - Initialization
/// Initializes `self` by referencing the given decoder and container.
init(referencing decoder: __JSONDecoder, wrapping container: [String : Any]) {
self.decoder = decoder
switch decoder.options.keyDecodingStrategy {
case .useDefaultKeys:
self.container = container
case .convertFromSnakeCase:
// Convert the snake case keys in the container to camel case.
// If we hit a duplicate key after conversion, then we'll use the first one we saw. Effectively an undefined behavior with JSON dictionaries.
self.container = Dictionary(container.map {
key, value in (JSONDecoder.KeyDecodingStrategy._convertFromSnakeCase(key), value)
}, uniquingKeysWith: { (first, _) in first })
case .custom(let converter):
self.container = Dictionary(container.map {
key, value in (converter(decoder.codingPath + [_JSONKey(stringValue: key, intValue: nil)]).stringValue, value)
}, uniquingKeysWith: { (first, _) in first })
}
self.codingPath = decoder.codingPath
}
// MARK: - KeyedDecodingContainerProtocol Methods
public var allKeys: [Key] {
return self.container.keys.compactMap { Key(stringValue: $0) }
}
public func contains(_ key: Key) -> Bool {
return self.container[key.stringValue] != nil
}
private func _errorDescription(of key: CodingKey) -> String {
switch decoder.options.keyDecodingStrategy {
case .convertFromSnakeCase:
// In this case we can attempt to recover the original value by reversing the transform
let original = key.stringValue
let converted = JSONEncoder.KeyEncodingStrategy._convertToSnakeCase(original)
let roundtrip = JSONDecoder.KeyDecodingStrategy._convertFromSnakeCase(converted)
if converted == original {
return "\(key) (\"\(original)\")"
} else if roundtrip == original {
return "\(key) (\"\(original)\"), converted to \(converted)"
} else {
return "\(key) (\"\(original)\"), with divergent representation \(roundtrip), converted to \(converted)"
}
default:
// Otherwise, just report the converted string
return "\(key) (\"\(key.stringValue)\")"
}
}
public func decodeNil(forKey key: Key) throws -> Bool {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
return entry is NSNull
}
public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Bool.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Int.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Int8.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Int16.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Int32.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Int64.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: UInt.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: UInt8.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: UInt16.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: UInt32.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: UInt64.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Float.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: Double.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode(_ type: String.Type, forKey key: Key) throws -> String {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: String.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func decode<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
guard let entry = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))."))
}
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = try self.decoder.unbox(entry, as: type) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "Expected \(type) value but found null instead."))
}
return value
}
public func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get \(KeyedDecodingContainer<NestedKey>.self) -- no value found for key \(_errorDescription(of: key))"))
}
guard let dictionary = value as? [String : Any] else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value)
}
let container = _JSONKeyedDecodingContainer<NestedKey>(referencing: self.decoder, wrapping: dictionary)
return KeyedDecodingContainer(container)
}
public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
guard let value = self.container[key.stringValue] else {
throw DecodingError.keyNotFound(key,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get UnkeyedDecodingContainer -- no value found for key \(_errorDescription(of: key))"))
}
guard let array = value as? [Any] else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value)
}
return _JSONUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array)
}
private func _superDecoder(forKey key: __owned CodingKey) throws -> Decoder {
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
let value: Any = self.container[key.stringValue] ?? NSNull()
return __JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options)
}
public func superDecoder() throws -> Decoder {
return try _superDecoder(forKey: _JSONKey.super)
}
public func superDecoder(forKey key: Key) throws -> Decoder {
return try _superDecoder(forKey: key)
}
}
private struct _JSONUnkeyedDecodingContainer : UnkeyedDecodingContainer {
// MARK: Properties
/// A reference to the decoder we're reading from.
private let decoder: __JSONDecoder
/// A reference to the container we're reading from.
private let container: [Any]
/// The path of coding keys taken to get to this point in decoding.
private(set) public var codingPath: [CodingKey]
/// The index of the element we're about to decode.
private(set) public var currentIndex: Int
// MARK: - Initialization
/// Initializes `self` by referencing the given decoder and container.
init(referencing decoder: __JSONDecoder, wrapping container: [Any]) {
self.decoder = decoder
self.container = container
self.codingPath = decoder.codingPath
self.currentIndex = 0
}
// MARK: - UnkeyedDecodingContainer Methods
public var count: Int? {
return self.container.count
}
public var isAtEnd: Bool {
return self.currentIndex >= self.count!
}
public mutating func decodeNil() throws -> Bool {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(Any?.self, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
if self.container[self.currentIndex] is NSNull {
self.currentIndex += 1
return true
} else {
return false
}
}
public mutating func decode(_ type: Bool.Type) throws -> Bool {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Bool.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: Int.Type) throws -> Int {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: Int8.Type) throws -> Int8 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int8.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: Int16.Type) throws -> Int16 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int16.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: Int32.Type) throws -> Int32 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int32.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: Int64.Type) throws -> Int64 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Int64.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: UInt.Type) throws -> UInt {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: UInt8.Type) throws -> UInt8 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt8.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: UInt16.Type) throws -> UInt16 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt16.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: UInt32.Type) throws -> UInt32 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt32.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: UInt64.Type) throws -> UInt64 {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: UInt64.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: Float.Type) throws -> Float {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Float.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: Double.Type) throws -> Double {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: Double.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode(_ type: String.Type) throws -> String {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: String.self) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func decode<T : Decodable>(_ type: T.Type) throws -> T {
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Unkeyed container is at end."))
}
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: type) else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath + [_JSONKey(index: self.currentIndex)], debugDescription: "Expected \(type) but found null instead."))
}
self.currentIndex += 1
return decoded
}
public mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> {
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(KeyedDecodingContainer<NestedKey>.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get nested keyed container -- unkeyed container is at end."))
}
let value = self.container[self.currentIndex]
guard !(value is NSNull) else {
throw DecodingError.valueNotFound(KeyedDecodingContainer<NestedKey>.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get keyed decoding container -- found null value instead."))
}
guard let dictionary = value as? [String : Any] else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [String : Any].self, reality: value)
}
self.currentIndex += 1
let container = _JSONKeyedDecodingContainer<NestedKey>(referencing: self.decoder, wrapping: dictionary)
return KeyedDecodingContainer(container)
}
public mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get nested keyed container -- unkeyed container is at end."))
}
let value = self.container[self.currentIndex]
guard !(value is NSNull) else {
throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get keyed decoding container -- found null value instead."))
}
guard let array = value as? [Any] else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: [Any].self, reality: value)
}
self.currentIndex += 1
return _JSONUnkeyedDecodingContainer(referencing: self.decoder, wrapping: array)
}
public mutating func superDecoder() throws -> Decoder {
self.decoder.codingPath.append(_JSONKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }
guard !self.isAtEnd else {
throw DecodingError.valueNotFound(Decoder.self,
DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Cannot get superDecoder() -- unkeyed container is at end."))
}
let value = self.container[self.currentIndex]
self.currentIndex += 1
return __JSONDecoder(referencing: value, at: self.decoder.codingPath, options: self.decoder.options)
}
}
extension __JSONDecoder : SingleValueDecodingContainer {
// MARK: SingleValueDecodingContainer Methods
private func expectNonNull<T>(_ type: T.Type) throws {
guard !self.decodeNil() else {
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected \(type) but found null value instead."))
}
}
public func decodeNil() -> Bool {
return self.storage.topContainer is NSNull
}
public func decode(_ type: Bool.Type) throws -> Bool {
try expectNonNull(Bool.self)
return try self.unbox(self.storage.topContainer, as: Bool.self)!
}
public func decode(_ type: Int.Type) throws -> Int {
try expectNonNull(Int.self)
return try self.unbox(self.storage.topContainer, as: Int.self)!
}
public func decode(_ type: Int8.Type) throws -> Int8 {
try expectNonNull(Int8.self)
return try self.unbox(self.storage.topContainer, as: Int8.self)!
}
public func decode(_ type: Int16.Type) throws -> Int16 {
try expectNonNull(Int16.self)
return try self.unbox(self.storage.topContainer, as: Int16.self)!
}
public func decode(_ type: Int32.Type) throws -> Int32 {
try expectNonNull(Int32.self)
return try self.unbox(self.storage.topContainer, as: Int32.self)!
}
public func decode(_ type: Int64.Type) throws -> Int64 {
try expectNonNull(Int64.self)
return try self.unbox(self.storage.topContainer, as: Int64.self)!
}
public func decode(_ type: UInt.Type) throws -> UInt {
try expectNonNull(UInt.self)
return try self.unbox(self.storage.topContainer, as: UInt.self)!
}
public func decode(_ type: UInt8.Type) throws -> UInt8 {
try expectNonNull(UInt8.self)
return try self.unbox(self.storage.topContainer, as: UInt8.self)!
}
public func decode(_ type: UInt16.Type) throws -> UInt16 {
try expectNonNull(UInt16.self)
return try self.unbox(self.storage.topContainer, as: UInt16.self)!
}
public func decode(_ type: UInt32.Type) throws -> UInt32 {
try expectNonNull(UInt32.self)
return try self.unbox(self.storage.topContainer, as: UInt32.self)!
}
public func decode(_ type: UInt64.Type) throws -> UInt64 {
try expectNonNull(UInt64.self)
return try self.unbox(self.storage.topContainer, as: UInt64.self)!
}
public func decode(_ type: Float.Type) throws -> Float {
try expectNonNull(Float.self)
return try self.unbox(self.storage.topContainer, as: Float.self)!
}
public func decode(_ type: Double.Type) throws -> Double {
try expectNonNull(Double.self)
return try self.unbox(self.storage.topContainer, as: Double.self)!
}
public func decode(_ type: String.Type) throws -> String {
try expectNonNull(String.self)
return try self.unbox(self.storage.topContainer, as: String.self)!
}
public func decode<T : Decodable>(_ type: T.Type) throws -> T {
try expectNonNull(type)
return try self.unbox(self.storage.topContainer, as: type)!
}
}
// MARK: - Concrete Value Representations
private extension __JSONDecoder {
/// Returns the given value unboxed from a container.
func unbox(_ value: Any, as type: Bool.Type) throws -> Bool? {
guard !(value is NSNull) else { return nil }
if let number = value as? NSNumber {
// TODO: Add a flag to coerce non-boolean numbers into Bools?
if number === kCFBooleanTrue as NSNumber {
return true
} else if number === kCFBooleanFalse as NSNumber {
return false
}
/* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested:
} else if let bool = value as? Bool {
return bool
*/
}
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
func unbox(_ value: Any, as type: Int.Type) throws -> Int? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let int = number.intValue
guard NSNumber(value: int) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return int
}
func unbox(_ value: Any, as type: Int8.Type) throws -> Int8? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let int8 = number.int8Value
guard NSNumber(value: int8) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return int8
}
func unbox(_ value: Any, as type: Int16.Type) throws -> Int16? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let int16 = number.int16Value
guard NSNumber(value: int16) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return int16
}
func unbox(_ value: Any, as type: Int32.Type) throws -> Int32? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let int32 = number.int32Value
guard NSNumber(value: int32) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return int32
}
func unbox(_ value: Any, as type: Int64.Type) throws -> Int64? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let int64 = number.int64Value
guard NSNumber(value: int64) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return int64
}
func unbox(_ value: Any, as type: UInt.Type) throws -> UInt? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let uint = number.uintValue
guard NSNumber(value: uint) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return uint
}
func unbox(_ value: Any, as type: UInt8.Type) throws -> UInt8? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let uint8 = number.uint8Value
guard NSNumber(value: uint8) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return uint8
}
func unbox(_ value: Any, as type: UInt16.Type) throws -> UInt16? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let uint16 = number.uint16Value
guard NSNumber(value: uint16) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return uint16
}
func unbox(_ value: Any, as type: UInt32.Type) throws -> UInt32? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let uint32 = number.uint32Value
guard NSNumber(value: uint32) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return uint32
}
func unbox(_ value: Any, as type: UInt64.Type) throws -> UInt64? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let uint64 = number.uint64Value
guard NSNumber(value: uint64) == number else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number <\(number)> does not fit in \(type)."))
}
return uint64
}
func unbox(_ value: Any, as type: Float.Type) throws -> Float? {
guard !(value is NSNull) else { return nil }
if let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse {
// We are willing to return a Float by losing precision:
// * If the original value was integral,
// * and the integral value was > Float.greatestFiniteMagnitude, we will fail
// * and the integral value was <= Float.greatestFiniteMagnitude, we are willing to lose precision past 2^24
// * If it was a Float, you will get back the precise value
// * If it was a Double or Decimal, you will get back the nearest approximation if it will fit
let double = number.doubleValue
guard abs(double) <= Double(Float.greatestFiniteMagnitude) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Parsed JSON number \(number) does not fit in \(type)."))
}
return Float(double)
/* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested:
} else if let double = value as? Double {
if abs(double) <= Double(Float.max) {
return Float(double)
}
overflow = true
} else if let int = value as? Int {
if let float = Float(exactly: int) {
return float
}
overflow = true
*/
} else if let string = value as? String,
case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy {
if string == posInfString {
return Float.infinity
} else if string == negInfString {
return -Float.infinity
} else if string == nanString {
return Float.nan
}
}
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
func unbox(_ value: Any, as type: Double.Type) throws -> Double? {
guard !(value is NSNull) else { return nil }
if let number = value as? NSNumber, number !== kCFBooleanTrue, number !== kCFBooleanFalse {
// We are always willing to return the number as a Double:
// * If the original value was integral, it is guaranteed to fit in a Double; we are willing to lose precision past 2^53 if you encoded a UInt64 but requested a Double
// * If it was a Float or Double, you will get back the precise value
// * If it was Decimal, you will get back the nearest approximation
return number.doubleValue
/* FIXME: If swift-corelibs-foundation doesn't change to use NSNumber, this code path will need to be included and tested:
} else if let double = value as? Double {
return double
} else if let int = value as? Int {
if let double = Double(exactly: int) {
return double
}
overflow = true
*/
} else if let string = value as? String,
case .convertFromString(let posInfString, let negInfString, let nanString) = self.options.nonConformingFloatDecodingStrategy {
if string == posInfString {
return Double.infinity
} else if string == negInfString {
return -Double.infinity
} else if string == nanString {
return Double.nan
}
}
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
func unbox(_ value: Any, as type: String.Type) throws -> String? {
guard !(value is NSNull) else { return nil }
guard let string = value as? String else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
return string
}
func unbox(_ value: Any, as type: Date.Type) throws -> Date? {
guard !(value is NSNull) else { return nil }
switch self.options.dateDecodingStrategy {
case .deferredToDate:
self.storage.push(container: value)
defer { self.storage.popContainer() }
return try Date(from: self)
case .secondsSince1970:
let double = try self.unbox(value, as: Double.self)!
return Date(timeIntervalSince1970: double)
case .millisecondsSince1970:
let double = try self.unbox(value, as: Double.self)!
return Date(timeIntervalSince1970: double / 1000.0)
case .iso8601:
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
let string = try self.unbox(value, as: String.self)!
guard let date = _iso8601Formatter.date(from: string) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected date string to be ISO8601-formatted."))
}
return date
} else {
fatalError("ISO8601DateFormatter is unavailable on this platform.")
}
case .formatted(let formatter):
let string = try self.unbox(value, as: String.self)!
guard let date = formatter.date(from: string) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Date string does not match format expected by formatter."))
}
return date
case .custom(let closure):
self.storage.push(container: value)
defer { self.storage.popContainer() }
return try closure(self)
}
}
func unbox(_ value: Any, as type: Data.Type) throws -> Data? {
guard !(value is NSNull) else { return nil }
switch self.options.dataDecodingStrategy {
case .deferredToData:
self.storage.push(container: value)
defer { self.storage.popContainer() }
return try Data(from: self)
case .base64:
guard let string = value as? String else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
guard let data = Data(base64Encoded: string) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Encountered Data is not valid Base64."))
}
return data
case .custom(let closure):
self.storage.push(container: value)
defer { self.storage.popContainer() }
return try closure(self)
}
}
func unbox(_ value: Any, as type: Decimal.Type) throws -> Decimal? {
guard !(value is NSNull) else { return nil }
// Attempt to bridge from NSDecimalNumber.
if let decimal = value as? Decimal {
return decimal
} else {
let doubleValue = try self.unbox(value, as: Double.self)!
return Decimal(doubleValue)
}
}
func unbox<T>(_ value: Any, as type: _JSONStringDictionaryDecodableMarker.Type) throws -> T? {
guard !(value is NSNull) else { return nil }
var result = [String : Any]()
guard let dict = value as? NSDictionary else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
let elementType = type.elementType
for (key, value) in dict {
let key = key as! String
self.codingPath.append(_JSONKey(stringValue: key, intValue: nil))
defer { self.codingPath.removeLast() }
result[key] = try unbox_(value, as: elementType)
}
return result as? T
}
func unbox<T : Decodable>(_ value: Any, as type: T.Type) throws -> T? {
return try unbox_(value, as: type) as? T
}
func unbox_(_ value: Any, as type: Decodable.Type) throws -> Any? {
if type == Date.self || type == NSDate.self {
return try self.unbox(value, as: Date.self)
} else if type == Data.self || type == NSData.self {
return try self.unbox(value, as: Data.self)
} else if type == URL.self || type == NSURL.self {
guard let urlString = try self.unbox(value, as: String.self) else {
return nil
}
guard let url = URL(string: urlString) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
debugDescription: "Invalid URL string."))
}
return url
} else if type == Decimal.self || type == NSDecimalNumber.self {
return try self.unbox(value, as: Decimal.self)
} else if let stringKeyedDictType = type as? _JSONStringDictionaryDecodableMarker.Type {
return try self.unbox(value, as: stringKeyedDictType)
} else {
self.storage.push(container: value)
defer { self.storage.popContainer() }
return try type.init(from: self)
}
}
}
//===----------------------------------------------------------------------===//
// Shared Key Types
//===----------------------------------------------------------------------===//
private struct _JSONKey : CodingKey {
public var stringValue: String
public var intValue: Int?
public init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
public init?(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
public init(stringValue: String, intValue: Int?) {
self.stringValue = stringValue
self.intValue = intValue
}
init(index: Int) {
self.stringValue = "Index \(index)"
self.intValue = index
}
static let `super` = _JSONKey(stringValue: "super")!
}
struct Point: Codable {
let x: Int
let y: Int
}
let pointDictionary = [ "x": 0, "y": 1]
// Inital example was too easy so also we might have
struct Score: Codable {
let points: Int
let name: String
}
let scoreDictionary: [String: Any] = [
"points": 10,
"name": "iOSDevZone"
]
let options = JSONDecoder._Options(dateDecodingStrategy: .deferredToDate,
dataDecodingStrategy: .base64,
nonConformingFloatDecodingStrategy: .throw,
keyDecodingStrategy: .useDefaultKeys,
userInfo: [:])
guard let score = try __JSONDecoder(referencing: scoreDictionary, at: [], options: options)
.unbox(scoreDictionary, as: Score.self) else {
throw DecodingError.valueNotFound(Score.self, DecodingError.Context(codingPath: [], debugDescription: "The given data did not contain a top-level value."))
}
score.points
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment