使用 Alamofire/Codable 解析 JSON 行

Posted

技术标签:

【中文标题】使用 Alamofire/Codable 解析 JSON 行【英文标题】:Parsing JSON Lines with Alamofire/Codable 【发布时间】:2018-03-07 18:57:57 【问题描述】:

是否可以用 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"

【问题讨论】:

【参考方案1】:

这是一个在 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)
            
        
    

【讨论】:

非常感谢,但这不适用于我们的用例。它根本不处理 JSON 行。它只是 Post 数组的普通序列化。那不是我要找的。这是我的问题中代码的副本,只是作为响应序列化程序编写的 你从哪里得到这个 json 行?是否有任何网址可以让我看到 json 并自己尝试一下? 当然可以!这是一组数据样本和 API 文档:docs.logdna.com/docs/v1-export-api JSON 行只是一个 json,其中每个 json 对象都用新行分隔,请参阅我上面的编辑,了解针对该特定场景的 json 行的自定义序列化程序。 即使我一直在尝试使用组件来做与此非常相似的事情,但我在每一行中都有 JSON 或在每一行中都有 JSON 的一部分,这令人困惑。我会试试你的解决方案【参考方案2】:

Alamofire 是可扩展的,因此我建议您编写自己的响应 ResponseSerializer,它可以逐行解析 JSON。似乎每一行都应该解析得很好,它们只是不能一起解析,因为整个文档不是有效的 JSON。

【讨论】:

我该怎么做。我从来没有写过ResponseSerializer 我建议你阅读the documentation。

以上是关于使用 Alamofire/Codable 解析 JSON 行的主要内容,如果未能解决你的问题,请参考以下文章

使用 neo4j-graphql-js 时如何访问自定义 graphQL 解析器中的请求标头?

JSON.parse不能解析j包含回车字符的son数据的问题

NestJS / TypeOrm / Neo4j :Nest 无法解析 NEO4J_DRIVER 的依赖关系

数组冒泡排序算法解析

字符串查找和函数操作题目解析

「斜率优化」解析及例题