在 Swift 中从响应数据中解码 json 字符串到结构

Decode json string to struct from response data in Swift

提问人:darencorp 提问时间:1/12/2022 更新时间:1/12/2022 访问量:1595

问:

我有一个用 python 编写的测试服务器,它在单个路径上生成 json 输出:“/”

import json

from fastapi import FastAPI
from uvicorn import run

app = FastAPI()


@app.get('/')
def main():
    return json.dumps([{
        'name': 'ihor',
        'age': 5
    }])


if __name__ == '__main__':
    run(app, host='0.0.0.0')

然后我在 SwiftUI 中拥有我的 iOS 客户端,并尝试从我的服务器获取一个 json 并将其解码为结构。

下面是 api 调用和解码的代码:

import SwiftUI

class Network: ObservableObject {
    @Published var users: [User] = []
    
    func getUsers() {
        let url = URL(string: "http://localhost:8000")

        let urlRequest = URLRequest(url: url!)

        let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            if let error = error {
                print("Request error: ", error)
                return
            }

            guard let response = response as? HTTPURLResponse else { return }

            if response.statusCode == 200 {
                guard let data = data else { return }
                DispatchQueue.main.async {
                    do {
                        print(String(decoding: data, as: UTF8.self))
                        let decodedUsers = try JSONDecoder().decode([User].self, from: data)
                        self.users = decodedUsers
                    } catch let error {
                        print("Error decoding: ", error)
                    }
                }
            }
        }

        dataTask.resume()
    }
}


struct User: Decodable {
    var name: String
    var age: Int
}

它是从简单的内容视图调用的

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var network: Network
    
    var body: some View {
        ScrollView {
                        Text("All users")
                        .font(.title).bold()
            if network.users.count > 0 {
            Text(network.users.first!.name)
                }
        }
                .onAppear {
                        network.getUsers()
                }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()    .environmentObject(Network())
    }
}

毕竟,它会产生下一个输出:

"[{\"name\": \"ihor\", \"age\": 5}]" Error decoding: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a string/data instead.", underlyingError: nil))

看起来 JSONDecoder 无法处理开头和结尾带有转义反斜杠和引号的输入。

一旦我删除了第一个和最后一个引号并转义的反斜杠,一切正常:

do {
    let rawJsonString = String(decoding: data, as: UTF8.self)
                        
    let lowerIndex = rawJsonString.index(rawJsonString.startIndex, offsetBy: 1)
    let upperIndex = rawJsonString.index(rawJsonString.endIndex, offsetBy: -1)
                        
    let jsonString = rawJsonString[lowerIndex..<upperIndex].replacingOccurrences(of: "\\", with: "")
                        
    print(jsonString)
                        
    let decodedUsers = try JSONDecoder().decode([User].self, from: jsonString.data(using:.utf8)!)
    self.users = decodedUsers
} catch let error {
    print("Error decoding: ", error)
}

但这是非常肮脏的解决方案,不适用于必须使用反斜杠转义的嵌套 json。

有没有一个简单的解决方案可以像这样解析JSON到结构?

json swift 转义 引号反 斜杠

评论

0赞 Larme 1/12/2022
你打印了什么作为“输出”?您是否正在发送 JSON Stringified?为什么?你能打印原始数据吗:在相同的水平上?否则,我会使用:print("Data: \(data.map { String(format: "%02hhx", $0) }.joined())print(String(decoding: data, as: UTF8.self))let decoder = JSONDecoder(); let stringified = try decoder.decode(String.self, from: data); let users = try decoder.decode([User].self, from: Data(stringified.utf8))
0赞 justintime 1/13/2022
@darencorp你的客户端代码对我来说似乎很好。在你的 python 代码中,将你的 json 转换为 ,这就是为什么解码它时遇到麻烦,因为它期望一个,但找到一个?您可以尝试在没有 json 的情况下直接返回 json 对象吗?json.dumpsStringSwiftArrayStringjson.dumps
0赞 darencorp 1/14/2022
您建议@Larme解决方案运行良好。谢谢你。请告诉我我是否理解正确:我从服务器得到了我的响应,第一步必须使用接口 String.self 将其从原始字符串解码为 JSON 字符串,然后才能使用专用接口将其解码为 Array?如果是这样,您能否在下面写一个答案,我想选择它作为适当的解决方案?
0赞 Larme 1/14/2022
如果可以修改php部分,请修改它,让它更容易,更常见,就像@justintime说的那样。我不了解 PHP,所以我无法帮助您完成这部分。
0赞 darencorp 1/14/2022
@justintime 实际上JSON格式是一个字符串(如YAML和XML),然后你可以将其解码为你的数据结构,如map、array、object等。正如 Larme 所建议的那样,它必须转换为正确的字符串格式,然后才能将其解码为数组。

答: 暂无答案