和JSON处理相关的常见错误

2020-04-26  本文已影响0人  醉看红尘这场梦

一直以来,我们都忽略了在编码和解码JSON时可能发生的各种错误。在了解了各种正常情况的处理方法之后,这一节,我们集中来看错误处理的问题。

处理不合法的JSON格式

第一个要处理的情况,是要解码的JSON格式不合法,这可能是多种原因造成的,例如服务端的程序有bug,或者网络传输问题等。为了演示,我们人为创建一个非法的JSON:

let response = """
{
    "1":{
        "title": "Episode 1"
    },
    "2": {
        "title": "Episode 2"
    },
    "3": {
        "title": "Episode 3"

}
"""

还是上一节中的内容,只不过,我们在最后故意去掉了一个}。然后直接调用上一节的全局decode

try decode(response: response, of: Episodes.self)

就会在控制台看到一个DecodingError.dataCorrupted异常,为了处理它,我们得修改一下这个decode函数:

func decode<T>(response: String, of: T.Type) throws -> T where T: Codable {
    let data = response.data(using: .utf8)!
    let decoder = JSONDecoder()

    do {
        let model = try decoder.decode(T.self, from: data)

        return model
    }
    catch DecodingError.typeMismatch(let type, let context) {
        dump(type)
        dump(context)
        exit(-1)
    }
}

这次,我们捕获了DecodingError.dataCorrupted异常,它有一个关联值,类型是DecodingError.Context,我们把这个值dump出来,就能看到下面的结果了:

▿ Swift.DecodingError.Context
  - codingPath: 0 elements
  - debugDescription: "The given data was not valid JSON."
  ▿ underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Unexpected end of file while parsing object."

其中,debugDescription给出了错误信息的概述,而underlyingError则是一个Error?对象,包含了更具体的错误信息,这里,它提示我们Unexpected end of file while parsing object

处理不存在的key

第二种错误情况,是访问了不存在的key,例如,我们先定义一个CodingKey

enum DemoKey: String, CodingKey {
    case demo
}

接下来,在解码的时候,我们使用这个Demokey

init(from decoder: Decoder) throws {
    let container = try decoder.container(
        keyedBy: EpisodeInfo.self)

    var v = [Episode]()
    for key in container.allKeys {
        let innerContainer = try container.nestedContainer(
            keyedBy: DemoKey.self, forKey: key)

        /// !!! Error
        let title = try innerContainer.decode(
            String.self, forKey: .demo)
        /// !!! Error

        let episode = Episode(id: Int(key.stringValue)!, title: title)
        v.append(episode)
    }

    self.episodes = v
}

在上面这个例子中,当我们在子容器中尝试读取key "demo"对应的值时,就会发生运行时错误。我们可以在decode全局函数中通过捕获keyNotFound出来key不存在的异常:

do {
    _ = try decoder.decode(Episodes.self, from: data)
}
catch DecodingError.keyNotFound(let codingPath, let context) {
    dump(codingPath)
    dump(context)
}

执行一下,就会看到它们的值了:

- Codable.DemoKey.demo
▿ Swift.DecodingError.Context
  - codingPath: 0 elements
  - debugDescription: "No value associated with key demo (\"demo\")."
  - underlyingError: nil

处理JSON值和model类型不匹配

最后一类错误,是JSON中包含的值和model中对应属性的类型不同。为了看到这个错误,我们把init方法改成这样:

init(from decoder: Decoder) throws {
        let container =
            try decoder.container(keyedBy: EpisodeInfo.self)

        var v = [Episode]()
        for key in container.allKeys {
            let innerContainer = try container.nestedContainer(
                keyedBy: EpisodeInfo.self, forKey: key)

            let title = try innerContainer.decode(
                Int.self, forKey: .title)
        }

        self.episodes = v
    }

这次,我们在解码的时候,把title对应的类型,改成了Int,但JSON中包含的是String,因此重新执行就会发生异常。对此,我们可以通过捕获DecodingError.typeMismatch来处理:

do {
    _ = try decoder.decode(Episodes.self, from: data)
}
catch DecodingError.typeMismatch(let type, let context) {
    dump(type)
    dump(context)
}

这个异常也有两个关联值,分别表示了编码错误的类型,和我们熟悉的context对象,执行一下,就可以看到下面的结果了:

- Swift.Int #0
▿ Swift.DecodingError.Context
  ▿ codingPath: 1 element
    ▿ Codable.Episodes.EpisodeInfo
      - stringValue: "title"
  - debugDescription: "Expected to decode Int but found a string/data instead."
  - underlyingError: nil

以上,就是把JSON映射到Swift model时三种常见的错误处理。除了解码之外,当我们编码一个Swift model时,只有可能遇到一种错误,就是model属性的类型,无法编码到指定的容器中。大家可以自己了解下enum EncodingError,道理是一样的,我们就不重复了。

上一篇 下一篇

猜你喜欢

热点阅读