使用 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数据的问题