首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Swift JSON解码器

Swift JSON解码器
EN

Stack Overflow用户
提问于 2022-04-17 22:52:47
回答 2查看 348关注 0票数 0

复活节快乐!

我正在构建一个应用程序来接收3个不同的JSON文件。它们中的每一个都由相同的数字组成,作为赢得一场比赛的选项(飞镖出局)。

示例:

代码语言:javascript
复制
{
    "checkoutOptions1": [
        {
            "170": {
                "out": "T20 T20 DB"
            },
            
            "167": {
                "out": "T20 T19 DB"
            },
            
            "164": {
                "out": "T20 T18 DB"
            },
            
            "161": {
                "out": "T20 T17 DB"
            },
            
            "160": {
                "out": "T20 T20 D20"
            }
        }
    ]
}

其他两个文件是相同的数字,但给出的字符串完全不同。

我有一个名为"Out“的结构,它允许从有关的json文件中获取"out”。

代码语言:javascript
复制
struct Out: Codable {
    let out: String
}

我的捆绑解码器的定义如下:

代码语言:javascript
复制
extension Bundle {
    func decode(_ file: String) -> [String: Out] {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }
        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }
        let decoder = JSONDecoder()
        
        guard let loaded = try? decoder.decode([String: Out].self, from: data ) else {
            fatalError("Failed to decode \(file) from bundle.")
        }
        
        return loaded
    }
}

这不管用。这是我第一次处理JSON解码器。寻找一些洞察力看看我是否有正确的想法。提亚

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-04-17 23:32:34

您的模型(Out)与您显示的json数据不匹配。试着做这样的事情:

代码语言:javascript
复制
struct Checkout: Codable {
    var checkoutOptions1: [[String : Out]]
}

struct Out: Codable {
    var out: String
}

代码语言:javascript
复制
 guard let loaded = try? decoder.decode(Checkout.self, from: data) else {
        fatalError("Failed to decode \(file) from bundle.")
    }

编辑-1:下面是如何使用模型的一个简单示例。

代码语言:javascript
复制
struct Checkout: Codable {
    var checkoutOptions1: [[String : Out]]
}

struct Out: Codable {
    var out: String
}

struct ContentView: View {
    
    var body: some View {
        Text("testing")
            .onAppear {
                let json = """
{
    "checkoutOptions1": [
        {
            "170": {
                "out": "T20 T20 DB"
            },
            
            "167": {
                "out": "T20 T19 DB"
            },
            
            "164": {
                "out": "T20 T18 DB"
            },
            
            "161": {
                "out": "T20 T17 DB"
            },
            
            "160": {
                "out": "T20 T20 D20"
            }
        }
    ]
}

"""
                
                let data = json.data(using: .utf8)!
                let loaded = try? JSONDecoder().decode(Checkout.self, from: data)
                print("\n----> loaded: \(loaded?.checkoutOptions1) \n")
                
            }
    }
}

另一种选择是:

代码语言:javascript
复制
extension Bundle {
    
    func decode<T: Codable>(_ file: String) -> T {
        guard let url = Bundle.main.url(forResource: file, withExtension: nil) else {
            fatalError("Could not find \(file) in the project")
        }
        
        guard let data = try? Data(contentsOf: url) else {
            fatalError("Could not load \(file) in the project")
        }
        
        do {
            let loadedData = try JSONDecoder().decode(T.self, from: data)
            return loadedData
        } catch {
            print(error)
            fatalError("Could not decode \(file) in the project")
        }
    }
}

struct ContentView: View {
    var body: some View {
        Text("testing")
            .onAppear {
                let result: Checkout = Bundle().decode("Dart.json")
                print("\n----> result: \(result) ")
                print("\n----> checkoutOptions1: \(result.checkoutOptions1) ")
                print("\n----> first: \(result.checkoutOptions1.first) ")
                print("\n----> 170: \(result.checkoutOptions1.first?["170"]) ")
                print("\n----> 170 out: \(result.checkoutOptions1.first?["170"]?.out) ")
            }
    }
}

编辑-2:

我不知道如何打分飞镖游戏,但使用@JoakimDanielson的建议,您可以将json数据重组为:

代码语言:javascript
复制
{
    "options": [
                {
                    "key": "170",
                    "out": "T20 T20 DB"
                },
                {
                    "key": "167",
                    "out": "T20 T19 DB"
                },
                {
                    "key": "164",
                    "out": "T20 T18 DB"
                },
                {
                    "key": "161",
                    "out": "T20 T17 DB"
                },
                {
                    "key": "160",
                    "out": "T20 T20 D20"
                }
            ]
}

并使用此示例代码将其解码为更易于管理的结构。

代码语言:javascript
复制
extension Bundle {
    
    func decode<T: Codable>(_ file: String) -> T? {   // <-- here optional
        guard let url = Bundle.main.url(forResource: file, withExtension: nil) else {
            print("Could not find \(file) in the project")
            return nil
        }
        guard let data = try? Data(contentsOf: url) else {
            print("Could not load \(file) in the project")
            return nil
        }
        do {
            return try JSONDecoder().decode(T.self, from: data)
        } catch {
            print("Could not decode \(file) in the project")
            return nil
        }
    }
}

struct ContentView: View {
    @State var options: [Option] = []
    
    var body: some View {
        Button("load data", action: {
            if let checkout: CheckOut = Bundle().decode("Dart.json") {
                options = checkout.options
            }
        }).buttonStyle(.bordered)
        List {
            ForEach(options) { option in
                Text("key: \(option.key)  out: \(option.out) ")
            }
        }
    }
}

struct CheckOut: Codable {
    let options: [Option]
}

struct Option: Identifiable, Codable {
    let id = UUID()
    let key, out: String
}
票数 1
EN

Stack Overflow用户

发布于 2022-04-17 23:41:18

decode包装到try catch并打印错误总是有帮助的。错误会告诉你问题出在哪里。如果你这样做的话

代码语言:javascript
复制
do {
  let loaded = try decoder.decode([String: Out].self, from: data )
  return loaded
} catch {
  print(error)
  fatalError("Failed to decode \(file) from bundle.")
}

然后,它将打印如下:

代码语言:javascript
复制
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "checkoutOptions1", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

实际上,如果仔细观察,那么JSON的结构实际上是这样的:[String: [[String: Out]]]checkoutOptions1的值是一个数组,包含一个[String: Out]类型的字典。

因此,如果您将代码更改为:

代码语言:javascript
复制
let loaded = try decoder.decode([String: [[String: Out]]].self, from: data )

它将取得成功,尽管结果并不容易处理,因为您的Out结构将被隐藏在字典中的几个层次中。

改善这种情况的一种方法是定义以下模型:

代码语言:javascript
复制
struct Options: Codable {
    let checkoutOptions1: [[String: Out]]
}

然后将解码行更改为:

代码语言:javascript
复制
let loaded = try decoder.decode(Options.self, from: data)

loaded.checkoutOptions1[0]将包含[String: Out]类型的字典。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71906055

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档