【问题标题】:JSON "Invalid value around character 0" though the url works flawless and the JSON is validJSON“字符 0 周围的值无效”,尽管 url 工作正常且 JSON 有效
【发布时间】:2018-11-14 11:19:58
【问题描述】:

我有这个似乎只是随机弹出的 json 错误。

我从域加载 json 并将其解析为字典。如果错误没有发生,它就完美无缺。代码如下:

func retrieveCoinPairData() {

    guard !checkIfBaseAndTargetAreEqual() else { return }

    if let base = self.currentBase, let target = self.currentTarget {
        if let url = URL(string: "https://api.cryptonator.com/api/full/\(base.code)-\(target.code)") {

            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

                if let error = error {
                    print(error.localizedDescription)
                }
                if let response = response {
                    print("reponse /api/full", response)
                }
                do {
                    if let data = data {
                        let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
                        self.createCoinPairData(with: jsonData)
                    } else {
                        self.delegate?.updateInfoLabelContent(content: .noInternetConnection)
                    }
                } catch {
                    self.delegate?.updateInfoLabelContent(content: .error)
                    print("catch error", error, data?.description as Any)
                    self.retrieveCoinPairData()
                }
            }).resume()
        } else {
            self.delegate?.updateInfoLabelContent(content: .error)
        }
    }
}

服务器响应如下抛出 403 错误:

reponse /api/full <NSHTTPURLResponse: 0x608000232400> { 
URL: https://api.cryptonator.com/api/full/BTC-ETH } { 
status code: 403, headers {
    Connection = "keep-alive";
    "Content-Encoding" = gzip;
    "Content-Type" = "text/html";
    Date = "Tue, 05 Jun 2018 04:23:37 GMT";
    "Keep-Alive" = "timeout=15";
    Server = nginx;
    "Transfer-Encoding" = Identit

并且 URLSession 捕获错误如下:

catch error Error Domain=NSCocoaErrorDomain Code=3840 
"Invalid value around character 0." 
UserInfo={NSDebugDescription=Invalid value around character 0.}
Optional("162 bytes")

错误出现在try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]这一行。

现在,无论何时发生此错误(而且是完全随机的),当我在浏览器中检查 url 时,它可以完美运行。

我用 jsonlint.com 检查了 json,它是有效的,没有顶级对象使 json 需要碎片,尽管该选项似乎减少了过去的错误。

我知道代码 403 错误告诉我网站阻止访问,代码 3840 错误告诉我没有要解析的内容。我仍然想知道错误发生的位置和原因。

这是本示例中使用的网站:https://api.cryptonator.com/api/full/btc-eth

【问题讨论】:

  • 我怀疑在出现403 错误的情况下,您没有从服务器返回有效的 json(相反,您收到的是纯文本错误响应)。您可以在尝试使用print("\(String(data: data, encoding: String.Encoding.utf8))") 对其进行反序列化之前打印出数据
  • 谢谢。它打印 "\r\n 403 Forbidden\r\n\r\n

    403 Forbidden

    \r\n
    nginx
    \r\n\r\n\r\n" 所以是的正确的。知道什么可能导致这种情况吗?这甚至在我的权力范围内吗?让我烦恼的是,当我在浏览器中手动打开它时,该 url 随时可用
  • 不相关但一如既往,如果预期类型是集合类型,选项.allowFragments 毫无意义。省略options 参数。要解决问题,请处理响应代码。
  • 是的,你是对的
  • 所以我联系了他们,他们说如果超过 100 个呼叫/分钟,该网站将自动禁止该 IP(一段时间)。这是有道理的,尽管我不会说我的应用程序通过测试甚至接近这一点。等待他们的另一个答复。

标签: json swift macos api urlsession


【解决方案1】:

更改您的代码行以执行此操作:

if let error = error {
    print(error.localizedDescription)
    return
 }
 if let httpResponse = response as HTTPURLResponse {
    if httpResponse.statusCode == 200 {
         // do your json deserialization after you verify you 
         // got a 200 / OK from the server
         do {
            if let data = data {
                let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
                self.createCoinPairData(with: jsonData)
            } else {
                self.delegate?.updateInfoLabelContent(content: .noInternetConnection)
            }
        } catch {
            self.delegate?.updateInfoLabelContent(content: .error)
            print("catch error", error, data?.description as Any)
            self.retrieveCoinPairData()
        }
    } else {
        print("reponse /api/full", response)
    }
 }

您看到的是,如果您知道服务器向您发送了 200/OK 响应,您应该只尝试 JSON 反序列化。

【讨论】:

  • 谢谢。仍然抛出:响应/api/full Optional( { URL: api.cryptonator.com/api/full/BTC-ETH } { 状态代码: 403, headers { Connection = "keep-alive"; "Content-Encoding" = gzip; "Content-Type " = "text/html"; Date = "Tue, 05 Jun 2018 05:00:02 GMT"; "Keep-Alive" = "timeout=15"; Server = nginx; "Transfer-Encoding" = Identity; Vary = "Accept-Encoding"; } }) 仍然,我的问题是在我的代码和 working url 之间的某个地方出了点问题,我不知道它是什么
  • 您能指出您找到https://api.cryptonator.com/api/full 端点的位置吗?我正在查看 cryptonator.com 的 API 文档,但在任何地方的 API 中都没有看到 full
  • cryptonator.com/api 就在“完整代码”下方。希望能回答你的问题?
【解决方案2】:

有时会在 url 上发生重定向。您可以在请求 url 时看到重定向 ohio.something 之类的内容。 当重定向发生时,urlsession 得到错误代码 403。 当 json 序列化获取 html 或其他代码而不是 vald json 对象时,也会出现 0 左右的无效值。

尝试从网络服务中删除重定向。

【讨论】:

  • 好的,谢谢。我了解 html 如何创建 3840 错误。 “删除重定向”是什么意思?我该怎么做?
  • 根据this Apple developer forum thread,“您无需自己处理重定向。NSURLSession 及其任务会适当地完成,包括cookie 处理”。这听起来像是远程服务器没有提供有效的站点来重定向。
  • 我刚刚给他们发了电子邮件,也许他们有答案。我认为重定向可能真的是问题所在。现在调用 url 我不会被重定向并同时运行应用程序不会引发错误。现在我在等待他们重定向我(在浏览器中),如果应用程序崩溃至少应该识别错误。
  • 您无法删除或处理重定向。这假设在服务器端完成。
  • 所以我联系了他们,他们说如果超过 100 个呼叫/分钟,该网站将自动禁止该 IP(一段时间)。这是有道理的,尽管我不会说我的应用程序通过测试甚至接近这一点。等待他们的另一个答复。
【解决方案3】:

您的要求应该是这样的,这只是演示版本,您必须根据您的要求进行相应的修改,

if let url = URL(string: "https://api.cryptonator.com/api/full/BTC-ETH") {
    var urlRequest = URLRequest(url: url)
    urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
    urlRequest.setValue("application/json", forHTTPHeaderField: "accept")
    urlRequest.httpMethod = "get"

    URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
        guard let data = data, error == nil else {
            print(error ?? "")
            return
        }
        do {
            let response = try JSONDecoder().decode(ResultResponse.self, from: data)
            print(response)
        } catch {
            print("catch error", error.localizedDescription)
        }
    }.resume()
} else {
    //Your else code
}

下面是两个 Codable 类,

struct Ticker: Codable {
    let base, target, price, volume: String
    let change: String
    let markets: [String]
}

struct ResultResponse: Codable {
    let ticker: Ticker
    let timestamp: Int
    let success: Bool
    let error: String
}

一旦你调用这个,你会得到以下输出,

ResultResponse(ticker: Demo.Ticker(base: "BTC", target: "ETH", price: "12.67485919", volume: "", change: "-0.04568160", markets: []), timestamp: 1528180081, success: true, error: "")

【讨论】:

    猜你喜欢
    • 2014-09-08
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 2016-09-19
    相关资源
    最近更新 更多