在 Swift 中解析 JSON 5

Parse JSON in Swift 5

我正在尝试为 JSON 文件编写一个类似于此的通用解析器。

{
    "BA": {
        "name": "Bert Andries",
        "age": 40,
        "likes": ["Food"]
    },
    ...
}

我想 return Dictionary<String, User> 其中 User 具有属性 nameagelikes。这就是我现在拥有的。

import Foundation

struct JsonHelper {
    static func parseJsonString<T: Decodable>(jsonString: String, entityType: T.Type) -> Dictionary<String, T> {
        let jsonData = jsonString.data(using: .utf8)!
        let items = try! JSONDecoder().decode([entityType].self, from: jsonData)

        return items
    }

    static func parseJsonFile<T: Decodable>(file: String, entityType: T.Type) -> Dictionary<String, T> {
        if let filepath = Bundle.main.path(forResource: file, ofType: "json") {
            if let contents = try? String(contentsOfFile: filepath) {
                return self.parseJsonString(jsonString: contents, entityType: entityType)
            }
        }
    }
}

解决方案

根据 bcal 的回答,我将 struct 重写为这个。

import Foundation

struct JsonHelper {
    static func parseJsonString<T: Decodable>(jsonString: String, entityType: T.Type) -> Dictionary<String, T>? {
        if let jsonData = jsonString.data(using: .utf8) {
            return try? JSONDecoder().decode(Dictionary<String, T>.self, from: jsonData)
        }

        return nil
    }

    static func parseJsonFile<T: Decodable>(file: String, entityType: T.Type) -> Dictionary<String, T>? {
        if let filepath = Bundle.main.path(forResource: file, ofType: "json") {
            if let contents = try? String(contentsOfFile: filepath) {
                return self.parseJsonString(jsonString: contents, entityType: entityType)
            }
        }

        return nil
    }
}

而且我可以这样使用它。

if let users = JsonHelper.parseJsonFile(file: "users", entityType: User.self) {
    if let user = dictionary["BA"] {
        print(user.name)
    }
}

User结构可以这样写。由于属性后面的 ?,即使 JSON 对象中缺少这些属性,解码也会成功。

struct User: Codable {
    let name: String?
    let age: Int?
    let likes: [String]?
}

Dictionary符合EncodableDecodable,所以JSONDecoder可以和Dictionary<String, User>类型一起使用。

解码您的 JSON 对象 ...

let jsonString = """
{
    "BA": {
        "name": "Bert Andries",
        "age": 40,
        "likes": ["FN"]
    },
    "FN": {
        "name": "Frans Niemen",
        "age": 44,
        "likes": ["BA"]
    },
    "SA": {
        "name": "Sandra Andries",
        "age": 35,
        "likes": ["BA", "FN"]
    }
}
"""

struct User: Codable {
    let name: String?
    let age: Int?
    let likes: [String]?
}

func decode(jsonString: String) throws -> Dictionary<String, User>? {
    guard let jsonData = jsonString.data(using: .utf8) else {
        return nil
    }
    return try JSONDecoder().decode(Dictionary<String, User>.self, from: jsonData)
}

let dict = try? decode(jsonString: jsonString)

print("\(String(describing: dict))")