Swift
4通过该Decodable
协议引入了对本机JSON编码和解码的支持。如何为此使用自定义键?
例如,说我有一个结构
struct Address:Codable {
var street:String
var zip:String
var city:String
var state:String
}
我可以将其编码为JSON。
let address = Address(street: "Apple Bay Street", zip: "94608", city: "Emeryville", state: "California")
if let encoded = try? encoder.encode(address) {
if let json = String(data: encoded, encoding: .utf8) {
// Print JSON String
print(json)
// JSON string is
{ "state":"California",
"street":"Apple Bay Street",
"zip":"94608",
"city":"Emeryville"
}
}
}
我可以将此编码回一个对象。
let newAddress: Address = try decoder.decode(Address.self, from: encoded)
但是如果我有一个json对象
{
"state":"California",
"street":"Apple Bay Street",
"zip_code":"94608",
"city":"Emeryville"
}
我怎么会告诉解码器上Address
是zip_code
映射到zip
?我相信您使用的是新CodingKey
协议,但是我不知道该如何使用。
在您的示例中,Codable
由于所有属性也都符合,因此您将获得自动生成的符合Codable
。这种一致性会自动创建一个仅与属性名称相对应的密钥类型,然后将其用于对单个密钥容器进行编码/解码。
但是,这种自动生成的一致性的一个 真正 巧妙的功能是,如果您enum
在称为“ CodingKeys
”
的类型中定义一个嵌套(或使用typealias
具有此名称的a)来符合CodingKey
协议,则Swift将自动将
其 用作键类型。因此,这使您可以轻松自定义用于对属性进行编码/解码的键。
因此,这意味着您可以说:
struct Address : Codable {
var street: String
var zip: String
var city: String
var state: String
private enum CodingKeys : String, CodingKey {
case street, zip = "zip_code", city, state
}
}
枚举案例名称需要与属性名称匹配,并且这些案例的原始值需要与您要编码/解码的键匹配(除非另有指定,否则String
枚举的原始值将与案例名称相同)。因此,zip
现在将使用key
对属性进行编码/解码"zip_code"
。
有关自动生成Encodable
/
Decodable
一致性的确切规则,请参见演进提案(重点是我的):
除了的自动
CodingKey
需求综合外enums
,Encodable
&Decodable
需求还可以针对某些类型自动综合:1.
符合Encodable
所有属性的类型将Encodable
获得自动生成的String
支持的CodingKey
枚举,将枚举属性映射到案例名称。对于Decodable
所有属性都相同的类型也是如此Decodable
属于(1)的 类型
CodingKey
enum``CodingKeys``typealias``Encodable``Decodable
- 以及手动提供类型
(名称为 ,直接或通过a )将其案例 按名称 一对一映射到 / 属性的类型 -
使用这些属性和键自动进行合并,init(from:)
并encode(to:)
在适当时进行合并落入类型既不(1)也没有(2)将具有如果需要的话提供一个自定义的密钥类型和提供它们自己的
init(from:)
和
encode(to:)
,如适当
编码示例:
import Foundation
let address = Address(street: "Apple Bay Street", zip: "94608",
city: "Emeryville", state: "California")
do {
let encoded = try JSONEncoder().encode(address)
print(String(decoding: encoded, as: UTF8.self))
} catch {
print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
解码示例:
// using the """ multi-line string literal here, as introduced in SE-0168,
// to avoid escaping the quotation marks
let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""
do {
let decoded = try JSONDecoder().decode(Address.self, from: Data(jsonString.utf8))
print(decoded)
} catch {
print(error)
}
// Address(street: "Apple Bay Street", zip: "94608",
// city: "Emeryville", state: "California")
snake_case
JSON键camelCase
在雨燕4.1,如果您重命名zip
属性zipCode
,你可以利用关键的编码/解码上的策略JSONEncoder
,并JSONDecoder
以自动转换之间的编码键camelCase
和snake_case
。
编码示例:
import Foundation
struct Address : Codable {
var street: String
var zipCode: String
var city: String
var state: String
}
let address = Address(street: "Apple Bay Street", zipCode: "94608",
city: "Emeryville", state: "California")
do {
let encoder = JSONEncoder()
**encoder.keyEncodingStrategy = .convertToSnakeCase**
let encoded = try encoder.encode(address)
print(String(decoding: encoded, as: UTF8.self))
} catch {
print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
解码示例:
let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""
do {
let decoder = JSONDecoder()
**decoder.keyDecodingStrategy = .convertFromSnakeCase**
let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
print(decoded)
} catch {
print(error)
}
// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")
但是,有关此策略的重要注意事项是,它无法使用首字母缩写词或首字母缩写来循环某些属性名称,根据Swift
API设计指南,这些属性名称应统一使用大写或小写(取决于位置) )。
例如,名为的属性someURL
将使用键进行编码some_url
,但是在解码时,它将转换为someUrl
。
要解决此问题,您必须手动将该属性的编码键指定为解码器期望的字符串,例如,someUrl
在这种情况下(some_url
编码器仍将其转换为):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(这并不能严格回答您的特定问题,但是鉴于此问答的典型性质,我认为值得考虑)
在雨燕4.1,您可以利用自定义按键编码/解码上的策略JSONEncoder
和JSONDecoder
,使您可以提供一个自定义函数映射的编码键。
您提供的函数采用[CodingKey]
,代表编码/解码中当前点的编码路径(在大多数情况下,您只需要考虑最后一个元素;即当前键)。该函数返回一个CodingKey
,它将替换此数组中的最后一个键。
例如,属性名称的UpperCamelCase
JSON键lowerCamelCase
:
import Foundation
// wrapper to allow us to substitute our mapped string keys.
struct AnyCodingKey : CodingKey {
var stringValue: String
var intValue: Int?
init(_ base: CodingKey) {
self.init(stringValue: base.stringValue, intValue: base.intValue)
}
init(stringValue: String) {
self.stringValue = stringValue
}
init(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
init(stringValue: String, intValue: Int?) {
self.stringValue = stringValue
self.intValue = intValue
}
}
extension JSONEncoder.KeyEncodingStrategy {
static var convertToUpperCamelCase: JSONEncoder.KeyEncodingStrategy {
return .custom { codingKeys in
var key = AnyCodingKey(codingKeys.last!)
// uppercase first letter
if let firstChar = key.stringValue.first {
let i = key.stringValue.startIndex
key.stringValue.replaceSubrange(
i ... i, with: String(firstChar).uppercased()
)
}
return key
}
}
}
extension JSONDecoder.KeyDecodingStrategy {
static var convertFromUpperCamelCase: JSONDecoder.KeyDecodingStrategy {
return .custom { codingKeys in
var key = AnyCodingKey(codingKeys.last!)
// lowercase first letter
if let firstChar = key.stringValue.first {
let i = key.stringValue.startIndex
key.stringValue.replaceSubrange(
i ... i, with: String(firstChar).lowercased()
)
}
return key
}
}
}
您现在可以使用以下.convertToUpperCamelCase
关键策略进行编码:
let address = Address(street: "Apple Bay Street", zipCode: "94608",
city: "Emeryville", state: "California")
do {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToUpperCamelCase
let encoded = try encoder.encode(address)
print(String(decoding: encoded, as: UTF8.self))
} catch {
print(error)
}
//{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}
并采用以下.convertFromUpperCamelCase
关键策略进行解码:
let jsonString = """
{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}
"""
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromUpperCamelCase
let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
print(decoded)
} catch {
print(error)
}
// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")
问题内容: 我正在尝试将我的爱好项目之一移植到linux。最好使用Mono,因为它是用C#编写的。但是我也在研究Python。 该应用程序的功能之一是它需要与自定义协议相关联,因此,当用户单击应用程序网站上的链接时,将调用该应用程序: 像这样,这个和这个的定制协议。 在linux / unix系统中怎么做?我可以像Windows中那样关联系统范围的处理程序吗?还是需要依赖于浏览器? 在Google
问题内容: 这是我的JSON 这是我希望将其保存到的结构(不完整) 我看过Apple的有关解码嵌套结构的文档,但是我仍然不明白如何正确执行JSON的不同级别。任何帮助都感激不尽。 问题答案: 另一种方法是创建一个与JSON紧密匹配的中间模型(借助于quicktype.io之类的工具),让Swift生成对它进行解码的方法,然后在最终数据模型中挑选所需的片段: 如果将来它包含多个值,这还使您可以轻松地
当然, 这个世界上没有绝对的事情. 所以万一现在所有的路由协议都不能满足你的需求, 那么你可以自己实现你自己的路由协议, 你要做的是, 申明你的路由协议实现了Yaf_Route_Interface接口即可.
元数据3.proto my_proto_file.proto 生成元数据3\u pb2。py并在protoc插件中使用它,如下所示, my\u proto\u file\u pb2。py公司 访问文件中定义的选项与生成代码中设置的选项相同 但从外部proto文件导入的选项似乎未设置,因此无法访问
问题内容: 我们正在使用注册自己的协议的软件。我们可以从浏览器运行应用程序,然后通过如下链接: 但是有没有办法检查用户系统是否支持这种自定义协议?如果不是,我们想请用户先安装软件。 例如: 编辑 我知道关于protocolLong属性,但是它仅在IE中有效。 问题答案: 不幸的是,没有简单的方法来实现这一目标。当然,没有预先确定是否安装协议处理程序的方法。 *正如您所提到的, *Internet
问题内容: 在Swift中有可能吗?如果没有,那么是否有解决方法? 问题答案: 1.使用默认实现(首选)。 优点 不涉及Objective-C运行时 (至少没有明确 涉及 )。这意味着您可以使其符合结构,枚举和非类。同样,这意味着您可以利用强大的泛型系统。 *当遇到符合此类协议的类型时, *您始终可以确保满足所有要求 。它始终是具体实现或默认实现。这就是“接口”或“合同”在其他语言中的行为方式。