簡體   English   中英

使用 Alamofire/Codable 解析 JSON 行

[英]Parsing JSON Lines with Alamofire/Codable

是否可以使用 Alamofire 和 codable 解析 JSON 行?

這是我現在的代碼。

        Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseString {(response) in
            switch response.result {
            case .success(let value):
                print ("response is \(value)")
            case .failure(let error):
                print ("error is \(error)")
            }
        }

這會將所有 JSON 行打印為字符串,但我想將響應序列化為 JSON 數組。 我該怎么做? JSON 行的問題在於它在單獨的行上返回每組 json,因此 alamofire 不熟悉它。

這是我嘗試過的,就好像這是傳統的 JSON 一樣,但顯然不是,所以這不起作用:

Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON {(response) in
    switch response.result {
    case .success(let value):
        print ("response is \(value)")             
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .secondsSince1970          
        let data = try! JSONSerialization.data(withJSONObject: value)

        do {
            let logs = try decoder.decode([Logs].self, from: data)
            completion(logs)
        } catch let error {
            print ("error parsing get logs: \(error)")
        }
    case .failure(let error):
        print ("failed get logs: \(error) ** \(response.result.value ?? "")")
    }
}

對於任何不熟悉 json 行的人,這里是官方格式信息: http : //jsonlines.org

{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:39.644199    6124 conversion.go:134] failed to handle multiple devices for container. Skipping Filesystem stats","_ts":1491869920198,"timestamp":"2017-04-11T00:18:39.000Z","_id":"804760774821019649"}
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:39.644167    6124 conversion.go:134] failed to handle multiple devices for container. Skipping Filesystem stats","_ts":1491869920198,"timestamp":"2017-04-11T00:18:39.000Z","_id":"804760774821019648"}
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:37.053730    6124 operation_executor.go:917] MountVolume.SetUp succeeded for volume \"kubernetes.io/secret/6f322c04-e1d2-11e6-bca0-000d3a111245-default-token-swb07\" (spec.Name: \"default-token-swb07\") pod \"6f322c04-e1d2-11e6-bca0-000d3a111245\" (UID: \"6f322c04-e1d2-11e6-bca0-000d3a111245\").","_ts":1491869917193,"timestamp":"2017-04-11T00:18:37.000Z","_id":"804760762212941824"}

這是在 Alamofire 中編寫自定義 DataSerializer 的示例,您可以使用它來解碼您的 Decodable 對象。

我正在使用隨機帖子 json url https://jsonplaceholder.typicode.com/posts 中的一個例子

這是Post類和自定義序列化器類PostDataSerializer 的示例

struct Post: Decodable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}


struct PostDataSerializer: DataResponseSerializerProtocol {

    enum PostDataSerializerError: Error {
        case InvalidData
    }

    var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<[Post]> {
        return { request, response, data, error in

            if let error = error {
                return .failure(error)

            }

            guard let data = data else {
                return .failure(PostDataSerializerError.InvalidData)
            }

            do {
                let jsonDecoder = JSONDecoder()
                let posts = try jsonDecoder.decode([Post].self, from: data)
                return .success(posts)
            } catch {
                return .failure(error)
            }
        }
    }
}

你可以簡單地將它連接到你的 Alamofire 客戶端,它像這樣向遠程 url 發送請求,

let request = Alamofire.request("https://jsonplaceholder.typicode.com/posts")

let postDataSerializer = PostDataSerializer()

request.response(responseSerializer: postDataSerializer) { response in
    print(response)
}

您還可以對自定義序列化程序的 serializeResponse getter 中的錯誤和 http 響應代碼進行額外的錯誤檢查。

對於您的 json 行,似乎每個 json 對象都以所謂的 json 行格式用換行符分隔。 您可以簡單地使用換行符拆分該行並將每一行解碼為 Log。

這是我創建的 Log 類。

struct Log: Decodable {
    let logType: String
    let ingester: String
    let ip: String
    let pid: Int
    let host: String
    let logsource: String
    let app: String
    let file: String
    let line: String
    let ts: Float64
    let timestamp: String
    let id: String


    enum CodingKeys: String, CodingKey {
        case logType = "_logtype"
        case ingester = "_ingester"
        case ip = "_ip"
        case pid
        case host = "_host"
        case logsource
        case app = "_app"
        case file = "_file"
        case line = "_line"
        case ts = "_ts"
        case timestamp
        case id = "_id"

    }
}

以及可以與 Alamofire 一起使用的自定義日志序列化程序。 以下序列化程序中的錯誤我沒有處理過,希望你能做到。

struct LogDataSerializer: DataResponseSerializerProtocol {

    enum LogDataSerializerError: Error {
        case InvalidData
    }

    var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<[Post]> {
        return { request, response, data, error in

            if let error = error {
                return .failure(error)

            }

            guard let data = data else {
                return .failure(LogDataSerializerError.InvalidData)
            }

            do {
                let jsonDecoder = JSONDecoder()
                let string = String(data: data, encoding: .utf8)!

                let allLogs = string.components(separatedBy: .newlines)
                    .filter { $0 != "" }
                    .map { jsonLine -> Log? in
                        guard let data = jsonLine.data(using: .utf8) else {
                            return nil
                        }
                        return try? jsonDecoder.decode(Log.self, from: data)
                    }.flatMap { $0 }
                return .success(allLogs)
            } catch {
                return .failure(error)
            }
        }
    }
}

Alamofire 是可擴展的,因此我建議您編寫自己的響應ResponseSerializer ,它可以逐行解析 JSON。 似乎每一行都應該解析得很好,它們只是不能一起解析,因為整個文檔不是有效的 JSON。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM