Romain d'Alverny 73f5266596 [Swift3] Set more liberal Swift3 decoder, behind lenientTypeCast option (#5795)
* [Swift3] Add lenientTypeCast option

When set to true, this generates a client JSON decoder
that will accept and cast mistyped values.

Here:
 - String => Bool ("true" instead of true),
 - String => Int ("123" instead of 123),
 - NSNumber => String (123 instead of "123").

The point is to allow the same client code to handle several
server implementations that may (sadly) not be up to spec,
or still be "evolving".

The conversion is not guaranteed if the input

Not a perfect/complete solution, not sure if it should be
activated along other casts, so kept behind an option.

* Update Petstore client code
2017-06-08 09:10:21 +08:00

252 lines
10 KiB
Plaintext

// Models.swift
//
// Generated by swagger-codegen
// https://github.com/swagger-api/swagger-codegen
//
import Foundation
protocol JSONEncodable {
func encodeToJSON() -> Any
}
public enum ErrorResponse : Error {
case Error(Int, Data?, Error)
}
open class Response<T> {
open let statusCode: Int
open let header: [String: String]
open let body: T?
public init(statusCode: Int, header: [String: String], body: T?) {
self.statusCode = statusCode
self.header = header
self.body = body
}
public convenience init(response: HTTPURLResponse, body: T?) {
let rawHeader = response.allHeaderFields
var header = [String:String]()
for (key, value) in rawHeader {
header[key as! String] = value as? String
}
self.init(statusCode: response.statusCode, header: header, body: body)
}
}
private var once = Int()
class Decoders {
static fileprivate var decoders = Dictionary<String, ((AnyObject, AnyObject?) -> AnyObject)>()
static func addDecoder<T>(clazz: T.Type, decoder: @escaping ((AnyObject, AnyObject?) -> T)) {
let key = "\(T.self)"
decoders[key] = { decoder($0, $1) as AnyObject }
}
static func decode<T>(clazz: T.Type, discriminator: String, source: AnyObject) -> T {
let key = discriminator;
if let decoder = decoders[key] {
return decoder(source, nil) as! T
} else {
fatalError("Source \(source) is not convertible to type \(clazz): Maybe swagger file is insufficient")
}
}
static func decode<T>(clazz: [T].Type, source: AnyObject) -> [T] {
let array = source as! [AnyObject]
return array.map { Decoders.decode(clazz: T.self, source: $0, instance: nil) }
}
static func decode<T, Key: Hashable>(clazz: [Key:T].Type, source: AnyObject) -> [Key:T] {
let sourceDictionary = source as! [Key: AnyObject]
var dictionary = [Key:T]()
for (key, value) in sourceDictionary {
dictionary[key] = Decoders.decode(clazz: T.self, source: value, instance: nil)
}
return dictionary
}
static func decode<T>(clazz: T.Type, source: AnyObject, instance: AnyObject?) -> T {
initialize()
if T.self is Int32.Type && source is NSNumber {
return (source as! NSNumber).int32Value as! T
}
if T.self is Int64.Type && source is NSNumber {
return (source as! NSNumber).int64Value as! T
}
if T.self is UUID.Type && source is String {
return UUID(uuidString: source as! String) as! T
}
if source is T {
return source as! T
}
if T.self is Data.Type && source is String {
return Data(base64Encoded: source as! String) as! T
}
{{#lenientTypeCast}}
if T.self is Int32.Type && source is String {
return (source as! NSString).intValue as! T
}
if T.self is Int64.Type && source is String {
return (source as! NSString).intValue as! T
}
if T.self is Bool.Type && source is String {
return (source as! NSString).boolValue as! T
}
if T.self is String.Type && source is NSNumber {
return String(describing: source) as! T
}
{{/lenientTypeCast}}
let key = "\(T.self)"
if let decoder = decoders[key] {
return decoder(source, instance) as! T
} else {
fatalError("Source \(source) is not convertible to type \(clazz): Maybe swagger file is insufficient")
}
}
static func decodeOptional<T>(clazz: T.Type, source: AnyObject?) -> T? {
if source is NSNull {
return nil
}
return source.map { (source: AnyObject) -> T in
Decoders.decode(clazz: clazz, source: source, instance: nil)
}
}
static func decodeOptional<T>(clazz: [T].Type, source: AnyObject?) -> [T]? {
if source is NSNull {
return nil
}
return source.map { (someSource: AnyObject) -> [T] in
Decoders.decode(clazz: clazz, source: someSource)
}
}
static func decodeOptional<T, Key: Hashable>(clazz: [Key:T].Type, source: AnyObject?) -> [Key:T]? {
if source is NSNull {
return nil
}
return source.map { (someSource: AnyObject) -> [Key:T] in
Decoders.decode(clazz: clazz, source: someSource)
}
}
private static var __once: () = {
let formatters = [
"yyyy-MM-dd",
"yyyy-MM-dd'T'HH:mm:ssZZZZZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd HH:mm:ss"
].map { (format: String) -> DateFormatter in
let formatter = DateFormatter()
formatter.dateFormat = format
return formatter
}
// Decoder for Date
Decoders.addDecoder(clazz: Date.self) { (source: AnyObject, instance: AnyObject?) -> Date in
if let sourceString = source as? String {
for formatter in formatters {
if let date = formatter.date(from: sourceString) {
return date
}
}
}
if let sourceInt = source as? Int64 {
// treat as a java date
return Date(timeIntervalSince1970: Double(sourceInt / 1000) )
}
fatalError("formatter failed to parse \(source)")
} {{#models}}{{#model}}
// Decoder for [{{{classname}}}]
Decoders.addDecoder(clazz: [{{{classname}}}].self) { (source: AnyObject, instance: AnyObject?) -> [{{{classname}}}] in
return Decoders.decode(clazz: [{{{classname}}}].self, source: source)
}
// Decoder for {{{classname}}}
Decoders.addDecoder(clazz: {{{classname}}}.self) { (source: AnyObject, instance: AnyObject?) -> {{{classname}}} in
{{#isArrayModel}}
let sourceArray = source as! [AnyObject]
return sourceArray.map({ Decoders.decode(clazz: {{{arrayModelType}}}.self, source: $0, instance: nil) })
{{/isArrayModel}}
{{^isArrayModel}}
{{#isEnum}}
if let source = source as? {{dataType}} {
if let result = {{classname}}(rawValue: source) {
return result
}
}
fatalError("Source \(source) is not convertible to enum type {{classname}}: Maybe swagger file is insufficient")
{{/isEnum}}
{{^isEnum}}
{{#allVars.isEmpty}}
if let source = source as? {{dataType}} {
return source
}
fatalError("Source \(source) is not convertible to typealias {{classname}}: Maybe swagger file is insufficient")
{{/allVars.isEmpty}}
{{^allVars.isEmpty}}
let sourceDictionary = source as! [AnyHashable: Any]
{{#discriminator}}
// Check discriminator to support inheritance
if let discriminator = sourceDictionary["{{discriminator}}"] as? String, instance == nil && discriminator != "{{classname}}" {
return Decoders.decode(clazz: {{classname}}.self, discriminator: discriminator, source: source)
}
{{/discriminator}}
{{#additionalPropertiesType}}
var propsDictionary = sourceDictionary
let keys : [AnyHashable] = [{{#allVars}}{{^-last}}"{{baseName}}", {{/-last}}{{#-last}}"{{baseName}}"{{/-last}}{{/allVars}}]
{{/additionalPropertiesType}}
{{#unwrapRequired}}
let result = {{classname}}({{#requiredVars}}{{^-first}}, {{/-first}}{{#isEnum}}{{name}}: {{classname}}.{{datatypeWithEnum}}(rawValue: (sourceDictionary["{{baseName}}"] as! {{datatype}}))! {{/isEnum}}{{^isEnum}}{{name}}: Decoders.decode(clazz: {{{baseType}}}.self, source: sourceDictionary["{{baseName}}"]! as AnyObject, instance: nil){{/isEnum}}{{/requiredVars}})
{{#optionalVars}}{{#isEnum}}
if let {{name}} = sourceDictionary["{{baseName}}"] as? {{datatype}} { {{^isContainer}}
result.{{name}} = {{classname}}.{{datatypeWithEnum}}(rawValue: ({{name}})){{/isContainer}}{{#isListContainer}}
result.{{name}} = {{name}}.map ({ {{classname}}.{{enumName}}(rawValue: $0)! }){{/isListContainer}}
}{{/isEnum}}{{^isEnum}}
result.{{name}} = Decoders.decodeOptional(clazz: {{{baseType}}}.self, source: sourceDictionary["{{baseName}}"] as AnyObject?){{/isEnum}}
{{/optionalVars}}
{{/unwrapRequired}}
{{^unwrapRequired}}
let result = instance == nil ? {{classname}}() : instance as! {{classname}}
{{#parent}}
if decoders["\({{parent}}.self)"] != nil {
_ = Decoders.decode(clazz: {{parent}}.self, source: source, instance: result)
}
{{/parent}}
{{#allVars}}{{#isEnum}}
if let {{name}} = sourceDictionary["{{baseName}}"] as? {{datatype}} { {{^isContainer}}
result.{{name}} = {{classname}}.{{datatypeWithEnum}}(rawValue: ({{name}})){{/isContainer}}{{#isListContainer}}
result.{{name}} = {{name}}.map ({ {{classname}}.{{enumName}}(rawValue: $0)! }){{/isListContainer}}{{#isMapContainer}}//TODO: handle enum map scenario{{/isMapContainer}}
}{{/isEnum}}
{{^isEnum}}result.{{name}} = Decoders.decodeOptional(clazz: {{{baseType}}}.self, source: sourceDictionary["{{baseName}}"] as AnyObject?){{/isEnum}}{{/allVars}}
{{/unwrapRequired}}
{{#additionalPropertiesType}}
for key in keys {
propsDictionary.removeValue(forKey: key)
}
for key in propsDictionary.keys {
if let decodedValue = Decoders.decodeOptional(clazz: String.self, source: propsDictionary[key] as AnyObject?) {
result[key] = decodedValue
}
}
{{/additionalPropertiesType}}
return result
{{/allVars.isEmpty}}
{{/isEnum}}
{{/isArrayModel}}
}{{/model}}
{{/models}}
}()
static fileprivate func initialize() {
_ = Decoders.__once
}
}