我有一个JSON字符串,我想解析它像ObjectMapper使用Codable协议。
struct Health: Mappable {
var size: [String : Any] = [:]?
var name: Double?
init?(map: Map) {
}
mutating func mapping(map: Map) {
size <- map["health.size"]
name <- map["health.name"]
}
}
我希望消除直接访问的健康结构模型,因为为不同的属性创建每个模型结构。
let jsonString = """
{
"health": {
"size":{
"width":150,
"height":150
},
"name":"Apple"
}
}
"""
我想使用(。)Dot运算符访问属性,例如health.size,而不创建健康的结构模型。
struct HealthType: Codable {
var health: Health
}
struct Health: Codable {
var title: String
var size: Size
enum CodingKeys: String, CodingKey
{
case title = "name"
}
}
struct Size: Codable {
var width: Double
var height: Double
}
答案 0 :(得分:1)
为此,您需要自己实施Codable
协议。这并不太难:
在Playground上尝试以下操作。
import Foundation
struct HealthType: Codable {
let title: String
let width: Double
let height: Double
enum CodingKeys: String, CodingKey
{
case health = "health"
case title = "name"
case width = "width"
case height = "height"
case size = "size"
}
}
extension HealthType {
init(from decoder: Decoder) throws {
let healthTypeContainer = try decoder.container(keyedBy: CodingKeys.self)
let health = try healthTypeContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .health)
let size = try health.nestedContainer(keyedBy: CodingKeys.self, forKey: .size)
let title = try health.decode(String.self, forKey: .title)
let width = try size.decode(Double.self, forKey: .width)
let height = try size.decode(Double.self, forKey: .height)
self.init(title: title, width: width, height: height)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var health = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .health)
var size = health.nestedContainer(keyedBy: CodingKeys.self, forKey: .size)
try health.encode(title, forKey: .title)
try size.encode(width, forKey: .width)
try size.encode(height, forKey: .height)
}
}
let jsonData = """
{
"health": {
"size":{
"width":150,
"height":150
},
"name":"Apple"
}
}
""".data(using: .utf8)!
do {
print(jsonData)
let healthType = try JSONDecoder().decode(HealthType.self, from: jsonData)
print(healthType.title) // Apple
print(healthType.width) // 150.0
print(healthType.width) // 150.0
} catch {
print(error)
}
答案 1 :(得分:0)
你几乎可以完成这项工作。但是,JSONDecoder
在Data
而不是String
上运行,默认情况下使用UTF-8对Playground进行编码,以便运行以下Playground:
import Cocoa
let jsonData = """
{
"health": {
"size":{
"width":150,
"height":150
},
"name":"Apple"
}
}
""".data(using: .utf8)!
struct HealthType: Codable {
var health: Health
}
struct Health: Codable {
var title: String
var size: Size
enum CodingKeys: String, CodingKey
{
case title = "name"
case size
}
}
struct Size: Codable {
var width: Double
var height: Double
}
do {
let health = try JSONDecoder().decode(HealthType.self, from:jsonData)
print(health)
let h = health.health
print(h.title)
} catch {
print(error)
}
虽然这解析并运行良好但我无法理解你的陈述并且没有创建健康的结构模型&#34;。使用Codable
的部分权衡是您必须提供JSON相关部分的结构定义。您也可以将输入解析为[String:Any].self
,但使用它是一种阻力。你将不得不经常评估演员表和期权。使用Codable
协议解析您的错误时,所有内容都会集中在decode
可能throw
的内容中。您获得的相关信息非常适合描述JSON(或struct
中的错误,具体取决于您的观点)。
简而言之:只要你的JSON包含你的"health"
密钥,你就必须告诉它如何处理它。
答案 2 :(得分:0)
如果您的意图是 不公开我不需要的属性 ,那么一种方法是私下解码整个结构,然后只使用那些属性将在外部世界曝光。
struct Health {
let size: Size
let title: String
struct Size: Decodable {
let width: Int
let height: Int
}
private struct RawResponse: Decodable {
let health: PrivateHealth
struct PrivateHealth: Decodable {
let size: Size
let name: String
}
}
}
// Decodable requirement is moved to extension so that default initializer is accessible
extension Health: Decodable {
init(from decoder: Decoder) throws {
let response = try RawResponse(from: decoder)
size = response.health.size
title = response.health.name
}
}
<强>用法:强>
let jsonData = """
{
"health": {
"size":{
"width":150,
"height":150
},
"name":"Apple"
}
}
""".data(using: .utf8)!
do {
let health = try JSONDecoder().decode(Health.self, from: jsonData)
print(health.size) // Size(width: 150, height: 150)
print(health.title) // Apple
} catch {
print(error.localizedDescription)
}
如果您还需要实施Encodable
协议,以便可以像实际响应那样对数据进行编码,那么您可以使用以下方法进行编码:
extension Health: Encodable {
func encode(to encoder: Encoder) throws {
let health = Health.RawResponse.PrivateHealth(size: size, name: title)
let response = RawResponse(health: health)
var container = encoder.singleValueContainer()
try container.encode(response)
}
}
为了实现这一目标,您需要使
RawResponse
,PrivateHealth
和Size
结构也可以编码
private struct RawResponse: Encodable, Decodable {
struct PrivateHealth: Encodable, Decodable {
struct Size: Encodable, Decodable {
编码示例:
let health = Health(size: Health.Size(width: 150, height: 150), title: "Apple")
do {
let encodedHealth = try JSONEncoder().encode(health) // Encoded data
// For checking purpose, you convert the data to string and print
let jsonString = String(data: encodedHealth, encoding: .utf8)!
print(jsonString) // This will ensure data is encoded as your desired format
} catch {
print(error.localizedDescription)
}
答案 3 :(得分:0)
嘿,我创建了KeyedCodable,我认为这正是您所寻找的。您的实现将如下所示。
struct Health: Codable, Keyedable {
var size: [String: Int]!
var name: String?
mutating func map(map: KeyMap) throws {
try size <-> map["health.size"]
try name <-> map["health.name"]
}
init(from decoder: Decoder) throws {
try KeyedDecoder(with: decoder).decode(to: &self)
}
}