[英]How can i create a file for JSONmodel programmatically and automatically create its Codable struct
How can i build a function that takes string as a parameter, that string will be my JSONObject and then this function will create a file in project that contains a struct which have keys of my model like this page does https://app.quicktype .io
我会将这种字符串传递给 function:
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}]
它将在项目中创建一个文件,例如:
import Foundation
// MARK: - UserInfoModelElement
struct UserInfoModelElement: Codable {
let id: Int
let name, username, email: String
let address: Address
let phone, website: String
let company: Company
}
// MARK: - Address
struct Address: Codable {
let street, suite, city, zipcode: String
let geo: Geo
}
// MARK: - Geo
struct Geo: Codable {
let lat, lng: String
}
// MARK: - Company
struct Company: Codable {
let name, catchPhrase, bs: String
}
typealias UserInfoModel = [UserInfoModelElement]
我在 GitHub juliofruta/CodableCode 上将其构建为一个开源项目。 随意提交拉取请求,因为这不支持评论中指定的所有情况。 我在这里复制并粘贴我当前的解决方案:
import Foundation
enum Error: Swift.Error {
case invalidData
}
let identation = " "
extension String {
var asType: String {
var string = self
let firstChar = string.removeFirst()
return firstChar.uppercased() + string
}
var asSymbol: String {
var string = self
let firstChar = string.removeFirst()
return firstChar.lowercased() + string
}
mutating func lineBreak() {
self = self + "\n"
}
func makeCodableTypeArray(anyArray: [Any], key: String, margin: String) throws -> String {
var types = Set<String>()
var existingTypes = Set<String>()
var structCodeSet = Set<String>()
try anyArray.forEach { jsonObject in
var type: String?
// check what type is each element of the array
switch jsonObject {
case _ as String:
type = "String"
case _ as Bool:
type = "Bool"
case _ as Decimal:
type = "Decimal"
case _ as Double:
type = "Double"
case _ as Int:
type = "Int"
case let dictionary as [String: Any]:
let objectData = try JSONSerialization.data(withJSONObject: dictionary, options: [])
let objectString = String(data: objectData, encoding: .utf8)!
let dummyTypeImplementation = try objectString.codableCode(name: "TYPE", margin: "")
// if the existing type does not contain the dummy type implementation
if !existingTypes.contains(dummyTypeImplementation) {
// insert it
existingTypes.insert(dummyTypeImplementation)
// keep a count
if existingTypes.count == 1 {
type = key.asType
} else {
type = key.asType + "\(existingTypes.count)"
}
// and get the actual implementation
let typeImplementation = try objectString.codableCode(name: type!, margin: margin + identation)
structCodeSet.insert(typeImplementation)
}
default:
type = ""
assertionFailure() // unhandled case
}
if let unwrappedType = type {
types.insert(unwrappedType)
}
}
// write type
var swiftCode = ""
if types.isEmpty {
swiftCode += "[Any]"
} else if types.count == 1 {
swiftCode += "[\(types.first!)]"
} else {
swiftCode += "\(key.asType)Options"
// TODO: Instead of enum refactor to use optionals where needed.
// TODO: Build Swift Build package plugin
// Use diffing algorithm to introduce optionals?
// Introduce strategies:
// create
// 1. enum withassociated types
// 2. optionals where needed
// 3. optionals everywhere
// add support to automatically fix when reserved keywords have reserved words for example:
// let return: Return // this does not compile and is part of the bitso api
// so add support for coding keys
//
// struct Landmark: Codable {
// var name: String
// var foundingYear: Int
// var location: Coordinate
// var vantagePoints: [Coordinate]
//
// enum CodingKeys: String, CodingKey {
// case name = "return"
// case foundingYear = "founding_date"
//
// case location
// case vantagePoints
// }
// }
// create enum
swiftCode.lineBreak()
swiftCode.lineBreak()
swiftCode += margin + identation + "enum \(key.asType)Options: Codable {"
types.forEach { type in
swiftCode.lineBreak()
// enum associatedTypes
swiftCode += margin + identation + identation + "case \(type.asSymbol)(\(type))"
}
swiftCode.lineBreak()
swiftCode += margin + identation + "}"
}
// write implementations
structCodeSet.forEach { implementation in
swiftCode.lineBreak()
swiftCode.lineBreak()
swiftCode += implementation
swiftCode.lineBreak()
}
return swiftCode
}
/// Compiles a valid JSON to a Codable Swift Type as in the following Grammar spec: https://www.json.org/json-en.html
/// - Parameter json: A valid JSON string
/// - Throws: Not sure if it should throw right now. We can check if the JSON is valid inside
/// - Returns: The string of the type produced by the JSON
public func codableCode(name: String, margin: String = "") throws -> String {
var swiftCode = ""
swiftCode += margin + "struct \(name.asType): Codable {"
guard let data = data(using: .utf8) else {
throw Error.invalidData
}
if let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
try dictionary
.sorted(by: { $0.0 < $1.0 })
.forEach { pair in
let (key, value) = pair
swiftCode.lineBreak()
swiftCode += margin + identation + "let \(key.asSymbol): "
switch value {
case _ as Bool:
swiftCode += "Bool"
case _ as String:
swiftCode += "String"
case _ as Decimal:
swiftCode += "Decimal"
case _ as Double:
swiftCode += "Double"
case _ as Int:
swiftCode += "Int"
case let jsonObject as [String: Any]:
let objectData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
let objectString = String(data: objectData, encoding: .utf8)!
swiftCode += "\(key.asType)"
swiftCode.lineBreak()
swiftCode.lineBreak()
swiftCode += try objectString.codableCode(name: key, margin: margin + identation)
swiftCode.lineBreak()
case let anyArray as [Any]:
swiftCode += try makeCodableTypeArray(anyArray: anyArray, key: key, margin: margin)
// TODO: Add more cases like dates
default:
swiftCode += "Any"
}
}
}
swiftCode.lineBreak()
swiftCode += margin + "}"
return swiftCode
}
public var codableCode: String? {
try? codableCode(name: "<#SomeType#>")
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.