import Foundation public protocol JSONEncodable: Encodable { static var keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy { get } static var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy { get } static var dataEncodingStrategy: JSONEncoder.DataEncodingStrategy { get } static var nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy { get } func toJSON() throws -> Data } public extension JSONEncodable { static var keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy { .convertToSnakeCase } static var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy { .iso8601 } static var dataEncodingStrategy: JSONEncoder.DataEncodingStrategy { .base64 } static var nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy { .throw } func toJSON() throws -> Data { let encoder = JSONEncoder() encoder.keyEncodingStrategy = Self.keyEncodingStrategy encoder.dateEncodingStrategy = Self.dateEncodingStrategy encoder.dataEncodingStrategy = Self.dataEncodingStrategy encoder.nonConformingFloatEncodingStrategy = Self.nonConformingFloatEncodingStrategy return try encoder.encode(self) } } public protocol JSONDecodable: Decodable { static var keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy { get } static var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy { get } static var dataDecodingStrategy: JSONDecoder.DataDecodingStrategy { get } static var nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy { get } static func from(json data: Data) throws -> Self } public extension JSONDecodable { static var keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy { .convertFromSnakeCase } static var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy { .iso8601 } static var dataDecodingStrategy: JSONDecoder.DataDecodingStrategy { .base64 } static var nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy { .throw } static func from(json data: Data) throws -> Self { let decoder = JSONDecoder() decoder.keyDecodingStrategy = Self.keyDecodingStrategy decoder.dateDecodingStrategy = Self.dateDecodingStrategy decoder.dataDecodingStrategy = Self.dataDecodingStrategy decoder.nonConformingFloatDecodingStrategy = Self.nonConformingFloatDecodingStrategy return try decoder.decode(Self.self, from: data) } } public protocol JSONCodable: JSONEncodable & JSONDecodable {} // Usage struct Filter: JSONCodable { let id: String } let filter = Filter(id: "foo") let encodedFilter = try filter.toJSON() let jsonString = String(data: encodedFilter, encoding: .utf8) let decodedFilter = try Filter.from(json: encodedFilter)