blob: ecafeedd8bd2b55558adf59b442a4aecc613a951 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import CoreFoundation
//===----------------------------------------------------------------------===//
// JSON Encoder
//===----------------------------------------------------------------------===//
/// `JSONEncoder` facilitates the encoding of `Encodable` values into JSON.
open class JSONEncoder {
// MARK: Options
/// The formatting of the output JSON data.
public struct OutputFormatting : OptionSet {
/// The format's default value.
public let rawValue: UInt
/// Creates an OutputFormatting value with the given raw value.
public init(rawValue: UInt) {
self.rawValue = rawValue
}
/// Produce human-readable JSON with indented output.
public static let prettyPrinted = OutputFormatting(rawValue: 1 << 0)
/// Produce JSON with dictionary keys sorted in lexicographic order.
@available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *)
public static let sortedKeys = OutputFormatting(rawValue: 1 << 1)
}
/// The strategy to use for encoding `Date` values.
public enum DateEncodingStrategy {
/// Defer to `Date` for choosing an encoding. This is the default strategy.
case deferredToDate
/// Encode the `Date` as a UNIX timestamp (as a JSON number).
case secondsSince1970
/// Encode the `Date` as UNIX millisecond timestamp (as a JSON number).
case millisecondsSince1970
/// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
case iso8601
/// Encode the `Date` as a string formatted by the given formatter.
case formatted(DateFormatter)
/// Encode the `Date` as a custom value encoded by the given closure.
///
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
case custom((Date, Encoder) throws -> Void)
}
/// The strategy to use for encoding `Data` values.
public enum DataEncodingStrategy {
/// Encoded the `Data` as a Base64-encoded string. This is the default strategy.
case base64
/// Encode the `Data` as a custom value encoded by the given closure.
///
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
case custom((Data, Encoder) throws -> Void)
}
/// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN).
public enum NonConformingFloatEncodingStrategy {
/// Throw upon encountering non-conforming values. This is the default strategy.
case `throw`
/// Encode the values using the given representation strings.
case convertToString(positiveInfinity: String, negativeInfinity: String, nan: String)
}
/// The output format to produce. Defaults to `[]`.
open var outputFormatting: OutputFormatting = []
/// The strategy to use in encoding dates. Defaults to `.deferredToDate`.
open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate
/// The strategy to use in encoding binary data. Defaults to `.base64`.
open var dataEncodingStrategy: DataEncodingStrategy = .base64
/// The strategy to use in encoding non-conforming numbers. Defaults to `.throw`.
open var nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy = .throw
/// Contextual user-provided information for use during encoding.
open var userInfo: [CodingUserInfoKey : Any] = [:]
/// Options set on the top-level encoder to pass down the encoding hierarchy.
fileprivate struct _Options {
let dateEncodingStrategy: DateEncodingStrategy
let dataEncodingStrategy: DataEncodingStrategy
let nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy
let userInfo: [CodingUserInfoKey : Any]
}
/// The options set on the top-level encoder.
fileprivate var options: _Options {
return _Options(dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy,
nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy,
userInfo: userInfo)
}
// MARK: - Constructing a JSON Encoder
/// Initializes `self` with default strategies.
public init() {}
// MARK: - Encoding Values
/// Encodes the given top-level value and returns its JSON representation.
///
/// - parameter value: The value to encode.
/// - returns: A new `Data` value containing the encoded JSON data.
/// - throws: `EncodingError.invalidValue` if a non-conforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`.
/// - throws: An error if any value throws an error during encoding.
open func encode<T : Encodable>(_ value: T) throws -> Data {
let encoder = _JSONEncoder(options: self.options)
try value.encode(to: encoder)
guard encoder.storage.count > 0 else {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) did not encode any values."))
}
let topLevel = encoder.storage.popContainer()
if topLevel is NSNull {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) encoded as null JSON fragment."))
} else if topLevel is NSNumber {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) encoded as number JSON fragment."))
} else if topLevel is NSString {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) encoded as string JSON fragment."))
}
let writingOptions = JSONSerialization.WritingOptions(rawValue: self.outputFormatting.rawValue)
return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions)
}
}
// MARK: - _JSONEncoder
fileprivate class _JSONEncoder : Encoder {
// MARK: Properties
/// The encoder's storage.
fileprivate var storage: _JSONEncodingStorage
/// Options set on the top-level encoder.
fileprivate let options: JSONEncoder._Options
/// The path to the current point in encoding.
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 encoder options.
fileprivate init(options: JSONEncoder._Options, codingPath: [CodingKey] = []) {
self.options = options
self.storage = _JSONEncodingStorage()
self.codingPath = codingPath
}
// MARK: - Coding Path Operations
/// Performs the given closure with the given key pushed onto the end of the current coding path.
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
fileprivate func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
return ret
}
/// Returns whether a new element can be encoded at this coding path.
///
/// `true` if an element has not yet been encoded at this coding path; `false` otherwise.
fileprivate var canEncodeNewValue: Bool {
// Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container).
// At the same time, every time a container is requested, a new value gets pushed onto the storage stack.
// If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition.
//
// This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path.
// Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here).
return self.storage.count == self.codingPath.count
}
// MARK: - Encoder Methods
public func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
// If an existing keyed container was already requested, return that one.
let topContainer: NSMutableDictionary
if self.canEncodeNewValue {
// We haven't yet pushed a container at this level; do so here.
topContainer = self.storage.pushKeyedContainer()
} else {
guard let container = self.storage.containers.last as? NSMutableDictionary else {
preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.")
}
topContainer = container
}
let container = _JSONKeyedEncodingContainer<Key>(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
return KeyedEncodingContainer(container)
}
public func unkeyedContainer() -> UnkeyedEncodingContainer {
// If an existing unkeyed container was already requested, return that one.
let topContainer: NSMutableArray
if self.canEncodeNewValue {
// We haven't yet pushed a container at this level; do so here.
topContainer = self.storage.pushUnkeyedContainer()
} else {
guard let container = self.storage.containers.last as? NSMutableArray else {
preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.")
}
topContainer = container
}
return _JSONUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
}
public func singleValueContainer() -> SingleValueEncodingContainer {
return self
}
}
// MARK: - Encoding Storage and Containers
fileprivate struct _JSONEncodingStorage {
// MARK: Properties
/// The container stack.
/// Elements may be any one of the JSON types (NSNull, NSNumber, NSString, NSArray, NSDictionary).
private(set) fileprivate var containers: [NSObject] = []
// MARK: - Initialization
/// Initializes `self` with no containers.
fileprivate init() {}
// MARK: - Modifying the Stack
fileprivate var count: Int {
return self.containers.count
}
fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary {
let dictionary = NSMutableDictionary()
self.containers.append(dictionary)
return dictionary
}
fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray {
let array = NSMutableArray()
self.containers.append(array)
return array
}
fileprivate mutating func push(container: NSObject) {
self.containers.append(container)
}
fileprivate mutating func popContainer() -> NSObject {
precondition(self.containers.count > 0, "Empty container stack.")
return self.containers.popLast()!
}
}
// MARK: - Encoding Containers
fileprivate struct _JSONKeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol {
typealias Key = K
// MARK: Properties
/// A reference to the encoder we're writing to.
private let encoder: _JSONEncoder
/// A reference to the container we're writing to.
private let container: NSMutableDictionary
/// The path of coding keys taken to get to this point in encoding.
private(set) public var codingPath: [CodingKey]
// MARK: - Initialization
/// Initializes `self` with the given references.
fileprivate init(referencing encoder: _JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) {
self.encoder = encoder
self.codingPath = codingPath
self.container = container
}
// MARK: - Coding Path Operations
/// Performs the given closure with the given key pushed onto the end of the current coding path.
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
fileprivate mutating func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
return ret
}
// MARK: - KeyedEncodingContainerProtocol Methods
public mutating func encodeNil(forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = NSNull() }
public mutating func encode(_ value: Bool, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: Int, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: Int8, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: Int16, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: Int32, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: Int64, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: UInt, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: UInt8, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: UInt16, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: UInt32, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: UInt64, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: String, forKey key: Key) throws { self.container[key.stringValue._bridgeToObjectiveC()] = self.encoder.box(value) }
public mutating func encode(_ value: Float, forKey key: Key) throws {
// Since the float may be invalid and throw, the coding path needs to contain this key.
try self.encoder.with(pushedKey: key) {
self.container[key.stringValue._bridgeToObjectiveC()] = try self.encoder.box(value)
}
}
public mutating func encode(_ value: Double, forKey key: Key) throws {
// Since the double may be invalid and throw, the coding path needs to contain this key.
try self.encoder.with(pushedKey: key) {
self.container[key.stringValue._bridgeToObjectiveC()] = try self.encoder.box(value)
}
}
public mutating func encode<T : Encodable>(_ value: T, forKey key: Key) throws {
try self.encoder.with(pushedKey: key) {
self.container[key.stringValue._bridgeToObjectiveC()] = try self.encoder.box(value)
}
}
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
let dictionary = NSMutableDictionary()
self.container[key.stringValue._bridgeToObjectiveC()] = dictionary
return self.with(pushedKey: key) {
let container = _JSONKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
return KeyedEncodingContainer(container)
}
}
public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
let array = NSMutableArray()
self.container[key.stringValue._bridgeToObjectiveC()] = array
return self.with(pushedKey: key) {
return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
}
}
public mutating func superEncoder() -> Encoder {
return _JSONReferencingEncoder(referencing: self.encoder, at: _JSONKey.super, wrapping: self.container)
}
public mutating func superEncoder(forKey key: Key) -> Encoder {
return _JSONReferencingEncoder(referencing: self.encoder, at: key, wrapping: self.container)
}
}
fileprivate struct _JSONUnkeyedEncodingContainer : UnkeyedEncodingContainer {
// MARK: Properties
/// A reference to the encoder we're writing to.
private let encoder: _JSONEncoder
/// A reference to the container we're writing to.
private let container: NSMutableArray
/// The path of coding keys taken to get to this point in encoding.
private(set) public var codingPath: [CodingKey]
/// The number of elements encoded into the container.
public var count: Int {
return self.container.count
}
// MARK: - Initialization
/// Initializes `self` with the given references.
fileprivate init(referencing encoder: _JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) {
self.encoder = encoder
self.codingPath = codingPath
self.container = container
}
// MARK: - Coding Path Operations
/// Performs the given closure with the given key pushed onto the end of the current coding path.
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
fileprivate mutating func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
return ret
}
// MARK: - UnkeyedEncodingContainer Methods
public mutating func encodeNil() throws { self.container.add(NSNull()) }
public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) }
public mutating func encode(_ value: Float) throws {
// Since the float may be invalid and throw, the coding path needs to contain this key.
try self.encoder.with(pushedKey: _JSONKey(index: self.count)) {
self.container.add(try self.encoder.box(value))
}
}
public mutating func encode(_ value: Double) throws {
// Since the double may be invalid and throw, the coding path needs to contain this key.
try self.encoder.with(pushedKey: _JSONKey(index: self.count)) {
self.container.add(try self.encoder.box(value))
}
}
public mutating func encode<T : Encodable>(_ value: T) throws {
try self.encoder.with(pushedKey: _JSONKey(index: self.count)) {
self.container.add(try self.encoder.box(value))
}
}
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
let dictionary = NSMutableDictionary()
self.container.add(dictionary)
// self.count - 1 to accommodate the fact that we just pushed a container.
return self.with(pushedKey: _JSONKey(index: self.count - 1)) {
let container = _JSONKeyedEncodingContainer<NestedKey>(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary)
return KeyedEncodingContainer(container)
}
}
public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
let array = NSMutableArray()
self.container.add(array)
// self.count - 1 to accommodate the fact that we just pushed a container.
return self.with(pushedKey: _JSONKey(index: self.count - 1)) {
return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array)
}
}
public mutating func superEncoder() -> Encoder {
return _JSONReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container)
}
}
extension _JSONEncoder : SingleValueEncodingContainer {
// MARK: - SingleValueEncodingContainer Methods
fileprivate func assertCanEncodeNewValue() {
precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.")
}
public func encodeNil() throws {
assertCanEncodeNewValue()
self.storage.push(container: NSNull())
}
public func encode(_ value: Bool) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: Int) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: Int8) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: Int16) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: Int32) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: Int64) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: UInt) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: UInt8) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: UInt16) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: UInt32) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: UInt64) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: String) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
public func encode(_ value: Float) throws {
assertCanEncodeNewValue()
try self.storage.push(container: box(value))
}
public func encode(_ value: Double) throws {
assertCanEncodeNewValue()
try self.storage.push(container: box(value))
}
public func encode<T : Encodable>(_ value: T) throws {
assertCanEncodeNewValue()
try self.storage.push(container: box(value))
}
}
// MARK: - Concrete Value Representations
extension _JSONEncoder {
/// Returns the given value boxed in a container appropriate for pushing onto the container stack.
fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) }
fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) }
fileprivate func box(_ float: Float) throws -> NSObject {
guard !float.isInfinite && !float.isNaN else {
guard case let .convertToString(positiveInfinity: posInfString,
negativeInfinity: negInfString,
nan: nanString) = self.options.nonConformingFloatEncodingStrategy else {
throw EncodingError._invalidFloatingPointValue(float, at: codingPath)
}
if float == Float.infinity {
return NSString(string: posInfString)
} else if float == -Float.infinity {
return NSString(string: negInfString)
} else {
return NSString(string: nanString)
}
}
return NSNumber(value: float)
}
fileprivate func box(_ double: Double) throws -> NSObject {
guard !double.isInfinite && !double.isNaN else {
guard case let .convertToString(positiveInfinity: posInfString,
negativeInfinity: negInfString,
nan: nanString) = self.options.nonConformingFloatEncodingStrategy else {
throw EncodingError._invalidFloatingPointValue(double, at: codingPath)
}
if double == Double.infinity {
return NSString(string: posInfString)
} else if double == -Double.infinity {
return NSString(string: negInfString)
} else {
return NSString(string: nanString)
}
}
return NSNumber(value: double)
}
fileprivate func box(_ date: Date) throws -> NSObject {
switch self.options.dateEncodingStrategy {
case .deferredToDate:
// Must be called with a surrounding with(pushedKey:) call.
try date.encode(to: self)
return self.storage.popContainer()
case .secondsSince1970:
return NSNumber(value: date.timeIntervalSince1970)
case .millisecondsSince1970:
return NSNumber(value: 1000.0 * date.timeIntervalSince1970)
case .iso8601:
if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
return NSString(string: _iso8601Formatter.string(from: date))
} else {
fatalError("ISO8601DateFormatter is unavailable on this platform.")
}
case .formatted(let formatter):
return NSString(string: formatter.string(from: date))
case .custom(let closure):
let depth = self.storage.count
try closure(date, self)
guard self.storage.count > depth else {
// The closure didn't encode anything. Return the default keyed container.
return NSDictionary()
}
// We can pop because the closure encoded something.
return self.storage.popContainer()
}
}
fileprivate func box(_ data: Data) throws -> NSObject {
switch self.options.dataEncodingStrategy {
case .base64:
return NSString(string: data.base64EncodedString())
case .custom(let closure):
let depth = self.storage.count
try closure(data, self)
guard self.storage.count > depth else {
// The closure didn't encode anything. Return the default keyed container.
return NSDictionary()
}
// We can pop because the closure encoded something.
return self.storage.popContainer()
}
}
fileprivate func box<T : Encodable>(_ value: T) throws -> NSObject {
if T.self == Date.self {
// Respect Date encoding strategy
return try self.box((value as! Date))
} else if T.self == Data.self {
// Respect Data encoding strategy
return try self.box((value as! Data))
} else if T.self == URL.self {
// Encode URLs as single strings.
return self.box((value as! URL).absoluteString)
} else if T.self == Decimal.self {
// On Darwin we get ((value as! Decimal) as NSDecimalNumber) since JSONSerialization can consume NSDecimalNumber values.
// FIXME: Attempt to create a Decimal value if JSONSerialization on Linux consume one.
let doubleValue = (value as! Decimal).doubleValue
return try self.box(doubleValue)
}
// The value should request a container from the _JSONEncoder.
let topContainer = self.storage.containers.last
try value.encode(to: self)
// The top container should be a new container.
guard self.storage.containers.last! !== topContainer else {
// If the value didn't request a container at all, encode the default container instead.
return NSDictionary()
}
return self.storage.popContainer()
}
}
// MARK: - _JSONReferencingEncoder
/// _JSONReferencingEncoder is a special subclass of _JSONEncoder which has its own storage, but references the contents of a different encoder.
/// It's used in superEncoder(), which returns a new encoder for encoding a superclass -- the lifetime of the encoder should not escape the scope it's created in, but it doesn't necessarily know when it's done being used (to write to the original container).
fileprivate class _JSONReferencingEncoder : _JSONEncoder {
// MARK: Reference types.
/// The type of container we're referencing.
private enum Reference {
/// Referencing a specific index in an array container.
case array(NSMutableArray, Int)
/// Referencing a specific key in a dictionary container.
case dictionary(NSMutableDictionary, String)
}
// MARK: - Properties
/// The encoder we're referencing.
fileprivate let encoder: _JSONEncoder
/// The container reference itself.
private let reference: Reference
// MARK: - Initialization
/// Initializes `self` by referencing the given array container in the given encoder.
fileprivate init(referencing encoder: _JSONEncoder, at index: Int, wrapping array: NSMutableArray) {
self.encoder = encoder
self.reference = .array(array, index)
super.init(options: encoder.options, codingPath: encoder.codingPath)
self.codingPath.append(_JSONKey(index: index))
}
/// Initializes `self` by referencing the given dictionary container in the given encoder.
fileprivate init(referencing encoder: _JSONEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) {
self.encoder = encoder
self.reference = .dictionary(dictionary, key.stringValue)
super.init(options: encoder.options, codingPath: encoder.codingPath)
self.codingPath.append(key)
}
// MARK: - Coding Path Operations
fileprivate override var canEncodeNewValue: Bool {
// With a regular encoder, the storage and coding path grow together.
// A referencing encoder, however, inherits its parents coding path, as well as the key it was created for.
// We have to take this into account.
return self.storage.count == self.codingPath.count - self.encoder.codingPath.count - 1
}
// MARK: - Deinitialization
// Finalizes `self` by writing the contents of our storage to the referenced encoder's storage.
deinit {
let value: Any
switch self.storage.count {
case 0: value = NSDictionary()
case 1: value = self.storage.popContainer()
default: fatalError("Referencing encoder deallocated with multiple containers on stack.")
}
switch self.reference {
case .array(let array, let index):
array.insert(value, at: index)
case .dictionary(let dictionary, let key):
dictionary[NSString(string: key)] = value
}
}
}
//===----------------------------------------------------------------------===//
// JSON Decoder
//===----------------------------------------------------------------------===//
/// `JSONDecoder` facilitates the decoding of JSON into semantic `Decodable` types.
open class JSONDecoder {
// MARK: Options
/// The strategy to use for decoding `Date` values.
public enum DateDecodingStrategy {
/// Defer to `Date` for decoding. This is the default strategy.
case deferredToDate
/// Decode the `Date` as a UNIX timestamp from a JSON number.
case secondsSince1970
/// Decode the `Date` as UNIX millisecond timestamp from a JSON number.
case millisecondsSince1970
/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
case iso8601
/// Decode the `Date` as a string parsed by the given formatter.
case formatted(DateFormatter)
/// Decode the `Date` as a custom value decoded by the given closure.
case custom((_ decoder: Decoder) throws -> Date)
}
/// The strategy to use for decoding `Data` values.
public enum DataDecodingStrategy {
/// Decode the `Data` from a Base64-encoded string. This is the default strategy.
case base64
/// Decode the `Data` as a custom value decoded by the given closure.
case custom((_ decoder: Decoder) throws -> Data)
}
/// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN).
public enum NonConformingFloatDecodingStrategy {
/// Throw upon encountering non-conforming values. This is the default strategy.
case `throw`
/// Decode the values from the given representation strings.
case convertFromString(positiveInfinity: String, negativeInfinity: String, nan: String)
}
/// The strategy to use in decoding dates. Defaults to `.deferredToDate`.
open var dateDecodingStrategy: DateDecodingStrategy = .deferredToDate
/// The strategy to use in decoding binary data. Defaults to `.base64`.
open var dataDecodingStrategy: DataDecodingStrategy = .base64
/// The strategy to use in decoding non-conforming numbers. Defaults to `.throw`.
open var nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy = .throw
/// Contextual user-provided information for use during decoding.
open var userInfo: [CodingUserInfoKey : Any] = [:]
/// Options set on the top-level encoder to pass down the decoding hierarchy.
fileprivate struct _Options {
let dateDecodingStrategy: DateDecodingStrategy
let dataDecodingStrategy: DataDecodingStrategy
let nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy
let userInfo: [CodingUserInfoKey : Any]
}
/// The options set on the top-level decoder.
fileprivate var options: _Options {
return _Options(dateDecodingStrategy: dateDecodingStrategy,
dataDecodingStrategy: dataDecodingStrategy,
nonConformingFloatDecodingStrategy: nonConformingFloatDecodingStrategy,
userInfo: userInfo)
}
// MARK: - Constructing a JSON Decoder
/// Initializes `self` with default strategies.
public init() {}
// MARK: - Decoding Values
/// Decodes a top-level value of the given type from the given JSON representation.
///
/// - parameter type: The type of the value to decode.
/// - parameter data: The data to decode from.
/// - returns: A value of the requested type.
/// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not valid JSON.
/// - throws: An error if any value throws an error during decoding.
open func decode<T : Decodable>(_ type: T.Type, from data: Data) throws -> T {
let topLevel = try JSONSerialization.jsonObject(with: data, options: [.useReferenceNumericTypes])
let decoder = _JSONDecoder(referencing: topLevel, options: self.options)
return try T(from: decoder)
}
}
// MARK: - _JSONDecoder
fileprivate class _JSONDecoder : Decoder {
// MARK: Properties
/// The decoder's storage.
fileprivate var storage: _JSONDecodingStorage
/// Options set on the top-level decoder.
fileprivate let options: JSONDecoder._Options
/// The path to the current point in encoding.
private(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.
fileprivate 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: - Coding Path Operations
/// Performs the given closure with the given key pushed onto the end of the current coding path.
///
/// - parameter key: The key to push. May be nil for unkeyed containers.
/// - parameter work: The work to perform with the key in the path.
fileprivate func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {
self.codingPath.append(key)
let ret: T = try work()
self.codingPath.removeLast()
return ret
}
// 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
fileprivate struct _JSONDecodingStorage {
// MARK: Properties
/// The container stack.
/// Elements may be any one of the JSON types (NSNull, NSNumber, String, Array, [String : Any]).
private(set) fileprivate var containers: [Any] = []
// MARK: - Initialization
/// Initializes `self` with no containers.
fileprivate init() {}
// MARK: - Modifying the Stack
fileprivate var count: Int {
return self.containers.count
}
fileprivate var topContainer: Any {
precondition(self.containers.count > 0, "Empty container stack.")
return self.containers.last!
}
fileprivate mutating func push(container: Any) {
self.containers.append(container)
}
fileprivate mutating func popContainer() {
precondition(self.containers.count > 0, "Empty container stack.")
self.containers.removeLast()
}
}
// MARK: Decoding Containers
fileprivate 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.
fileprivate init(referencing decoder: _JSONDecoder, wrapping container: [String : Any]) {
self.decoder = decoder
self.container = container
self.codingPath = decoder.codingPath
}
// MARK: - KeyedDecodingContainerProtocol Methods
public var allKeys: [Key] {
return self.container.keys.flatMap { Key(stringValue: $0) }
}
public func contains(_ key: Key) -> Bool {
return self.container[key.stringValue] != nil
}
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 \(key) (\"\(key.stringValue)\")."))
}
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
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 \(key) (\"\(key.stringValue)\")."))
}
return try self.decoder.with(pushedKey: key) {
guard let value = try self.decoder.unbox(entry, as: T.self) 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> {
return try self.decoder.with(pushedKey: key) {
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 \"\(key.stringValue)\""))
}
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 {
return try self.decoder.with(pushedKey: key) {
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 \"\(key.stringValue)\""))
}
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: CodingKey) throws -> Decoder {
return self.decoder.with(pushedKey: key) {
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)
}
}
fileprivate 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.
fileprivate 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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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."))
}
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
guard let decoded = try self.decoder.unbox(self.container[self.currentIndex], as: T.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 nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> {
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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 {
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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 {
return try self.decoder.with(pushedKey: _JSONKey(index: self.currentIndex)) {
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(T.self)
return try self.unbox(self.storage.topContainer, as: T.self)!
}
}
// MARK: - Concrete Value Representations
extension _JSONDecoder {
/// Returns the given value unboxed from a container.
fileprivate func unbox(_ value: Any, as type: Bool.Type) throws -> Bool? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
// TODO: Add a flag to coerce non-boolean numbers into Bools?
guard number._cfTypeID == CFBooleanGetTypeID() else {
throw DecodingError._typeMismatch(at: self.codingPath, expectation: type, reality: value)
}
return number.boolValue
}
fileprivate func unbox(_ value: Any, as type: Int.Type) throws -> Int? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: Int8.Type) throws -> Int8? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: Int16.Type) throws -> Int16? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: Int32.Type) throws -> Int32? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: Int64.Type) throws -> Int64? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: UInt.Type) throws -> UInt? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: UInt8.Type) throws -> UInt8? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: UInt16.Type) throws -> UInt16? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: UInt32.Type) throws -> UInt32? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: UInt64.Type) throws -> UInt64? {
guard !(value is NSNull) else { return nil }
guard let number = value as? NSNumber 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
}
fileprivate func unbox(_ value: Any, as type: Float.Type) throws -> Float? {
guard !(value is NSNull) else { return nil }
if let number = value as? NSNumber {
// 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)
}
fileprivate func unbox(_ value: Any, as type: Double.Type) throws -> Double? {
guard !(value is NSNull) else { return nil }
if let number = value as? NSNumber {
// 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)
}
fileprivate 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
}
fileprivate 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)
let date = try Date(from: self)
self.storage.popContainer()
return date
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(OSX 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)
let date = try closure(self)
self.storage.popContainer()
return date
}
}
fileprivate func unbox(_ value: Any, as type: Data.Type) throws -> Data? {
guard !(value is NSNull) else { return nil }
switch self.options.dataDecodingStrategy {
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)
let data = try closure(self)
self.storage.popContainer()
return data
}
}
fileprivate func unbox(_ value: Any, as type: Decimal.Type) throws -> Decimal? {
guard !(value is NSNull) else { return nil }
// On Darwin we get (value as? Decimal) since JSONSerialization can produce NSDecimalNumber values.
// FIXME: Attempt to grab a Decimal value if JSONSerialization on Linux produces one.
let doubleValue = try self.unbox(value, as: Double.self)!
return Decimal(doubleValue)
}
fileprivate func unbox<T : Decodable>(_ value: Any, as type: T.Type) throws -> T? {
let decoded: T
if T.self == Date.self {
guard let date = try self.unbox(value, as: Date.self) else { return nil }
decoded = date as! T
} else if T.self == Data.self {
guard let data = try self.unbox(value, as: Data.self) else { return nil }
decoded = data as! T
} else if T.self == URL.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."))
}
decoded = (url as! T)
} else if T.self == Decimal.self {
guard let decimal = try self.unbox(value, as: Decimal.self) else { return nil }
decoded = decimal as! T
} else {
self.storage.push(container: value)
decoded = try T(from: self)
self.storage.popContainer()
}
return decoded
}
}
//===----------------------------------------------------------------------===//
// Shared Key Types
//===----------------------------------------------------------------------===//
fileprivate 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
}
fileprivate init(index: Int) {
self.stringValue = "Index \(index)"
self.intValue = index
}
fileprivate static let `super` = _JSONKey(stringValue: "super")!
}
//===----------------------------------------------------------------------===//
// Shared ISO8601 Date Formatter
//===----------------------------------------------------------------------===//
// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled against the latest SDK (w/ ISO8601DateFormatter), but linked against whichever Foundation the user has. ISO8601DateFormatter might not exist, so we better not hit this code path on an older OS.
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
fileprivate var _iso8601Formatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = .withInternetDateTime
return formatter
}()
//===----------------------------------------------------------------------===//
// Error Utilities
//===----------------------------------------------------------------------===//
fileprivate extension EncodingError {
/// Returns a `.invalidValue` error describing the given invalid floating-point value.
///
///
/// - parameter value: The value that was invalid to encode.
/// - parameter path: The path of `CodingKey`s taken to encode this value.
/// - returns: An `EncodingError` with the appropriate path and debug description.
fileprivate static func _invalidFloatingPointValue<T : FloatingPoint>(_ value: T, at codingPath: [CodingKey]) -> EncodingError {
let valueDescription: String
if value == T.infinity {
valueDescription = "\(T.self).infinity"
} else if value == -T.infinity {
valueDescription = "-\(T.self).infinity"
} else {
valueDescription = "\(T.self).nan"
}
let debugDescription = "Unable to encode \(valueDescription) directly in JSON. Use JSONEncoder.NonConformingFloatEncodingStrategy.convertToString to specify how the value should be encoded."
return .invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: debugDescription))
}
}