Nson provides a more convenient way to handle Decodable and Encodable through Property Wrappers.
- Swift 5.0
- iOS 11.0+
- macOS 10.13+
- tvOS 11.0+
- watchOS 4.0+
You can use The Swift Package Manager to install Nson by adding the proper description to your Package.swift file:
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
targets: [],
dependencies: [
.package(url: "https://github.com/devhplusn/Nson.git", from: "1.0.0")
]
)
Nson is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'Nson', :tag => '1.0.0', :git => 'https://github.com/devhplusn/Nson'
@Key allows you to process JSON decoding or encoding without CodingKeys and using custom keys. @Key uses the <- operator or KeyPaths to define how it maps to and from JSON. The nested key accepts additional keys as arguments. The object needs to implement the NSON protocol.
{
"name": "tanaka",
"info": {
"age": 20,
"gender": "M",
"email_address": "tanaka@gmail.com"
}
}
struct Model {
var name: String
var age: Int
var gender: String
var email: String
enum CodingKeys: String, CodingKey {
case name
case info
}
enum InfoKeys: String, CodingKey {
case age
case gender
case email = "email_address"
}
}
extension Model: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
let nestedContainer = try container.nestedContainer(keyedBy: InfoKeys.self, forKey: .info)
age = try nestedContainer.decode(Int.self, forKey: .age)
gender = try nestedContainer.decode(String.self, forKey: .gender)
email = try nestedContainer.decode(String.self, forKey: .email)
}
}
extension Model: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
var nestedContainer = container.nestedContainer(keyedBy: InfoKeys.self, forKey: .info)
try nestedContainer.encode(age, forKey: .age)
try nestedContainer.encode(gender, forKey: .gender)
try nestedContainer.encode(email, forKey: .email)
}
}
struct Model: Codable, NSON {
var name: String
@Key("age" <- ["info", "age"])
var age: Int
@Key(to: \.gender, "info", "gender")
var gender: String
@Key("email" <- ["info", "email_address"])
var email: String
}
// Decode
let model = try NSONDecoder().decode(Model.self, from: jsonData)
// Encode
let data = try NSONEncoder().encode(model)
When you need to decode flat JSON values into nested structures or encode nested structures into flat JSON, you can handle it through the use of the @Flat property wrapper.
{
"name": "tanaka",
"info_age": 20,
"info_gender": "M"
}
struct Model: Codable {
var name: String
@Flat
var info: ModelInfo
}
struct ModelInfo: Codable, NSON {
@Key("age" <- "info_age")
var age: Int
@Key(to: \.gender, "info_gender")
var gender: String
}
To add custom transformations, please implement DecodeTransformable, EncodeTransformable, or CodeTransformable for bidirectional transformations.
{
"count": "10"
"date": "2023-11-01"
}
struct Object: Decodable {
@Transform<ToInt>
var count: Int
@Transform<ToDate>
var date: Date?
}
enum ToInt: CodeTransformable {
typealias Value = Int
static func transform(decoded: String) throws -> Value {
Int(decoded) ?? 0
}
static func transform(value: Int) throws -> String {
String(value)
}
}
enum ToDate: DecodeTransformable {
public typealias Value = Date?
public static func transform(decoded: String) throws -> Value {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.date(from: decoded)
}
}
Can combine @Key and @Transform to handle JSON keys and transform decoded values when needed.
{
"count": "10"
}
struct Object: Decodable {
@Key("m_count" <- "count")
@Transform<ToInt>
var m_count: Int
}
enum ToInt: DecodeTransformable {
typealias Value = Int
static func transform(decoded: String) throws -> Value {
Int(decoded) ?? 0
}
}
These works are available under the MIT license. See the LICENSE file for more info.