Swift Json实例详细解析

2020-01-09 00:10:36丽君

这让人有点懊恼,端上的应用,我们希望它能够尽可能稳定,而不是某些情况下遇到若干个基本类型不一致整个解析就停止,甚至是 Crash。

如下面表格所示,我们希望类型不匹配时,能够这么处理:左列代表前端的类型,右列代表服务端类型,每一行代表前端类型为X时,能从服务端下发的哪些类型中转化,比如String 可以从 IntorFloat转化。这几个类型基本能覆盖日常服务端下发的数据,其它类型的转化可根据自己的需求扩充。

 


前端
服务端
String Int,Float
Float String
Double String
Bool String, Int
 

 

JSONDecoder没有给我们便利的这种异常处理的API。如何解决呢?最直接的想法,在具体的model内实现init(decoder: Decoder)手动解析可以实现,但每个都这么处理太麻烦。

解决方案:KeyedDecodingContainer方法覆盖

研究JSONDecoder的源码,在解析自定义Model过程中,会发现这样一个调用关系。


// 入口方法
JSONDecoder decoder(type:Type data:Data) 
 // 内部类,真实用来解析的
 _JSONDecoder unbox(value:Any type:Type) 
 // Model调用init方法
 Decodable init(decoder: Decoder) 
 // 自动生成的init方法调用container
 Decoder container(keyedBy:CodingKeys) 
 // 解析的容器
 KeyedDecodingContainer decoderIfPresent(type:Type) or decode(type:Type)
  // 内部类,循环调用unbox
  _JSONDecoder unbox(value:Any type:Type)
  ...循环,直到基本类型

最终的解析落到,_JSONDecoder的unbox 及 KeyedDecodingContainer的decoderIfPresent decode方法。但_JSONDecoder是内部类,我们处理不了。最终决定对KeyedDecodingContainer下手,其中部分代码如下:


extension KeyedDecodingContainer {
 .......
 /// Decode (Int, String) -> Int if possiable
 public func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
  if let value = try? decode(type, forKey: key) {
   return value
  }
  if let value = try? decode(String.self, forKey: key) {
   return Int(value)
  }
  return nil
 }
 
 .......
 
 /// Avoid the failure just when decoding type of Dictionary, Array, SubModel failed
 public func decodeIfPresent<T>(_ type: T.Type, forKey key: K) throws -> T? where T : Decodable {
  return try? decode(type, forKey: key)
 }
}

上述代码中,第一个函数decodeIfPresent(_ type: Int.Type, forKey key: K)是以key的信息解析出Int?值。这里覆盖了KeyedDecodingContainer中的该函数的实现,现在已try?的形式以Int类型解析,解析成功则直接返回,失败则以String类型解析出一个StringValue,如果解析成功,再把String转换成Int?值。