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": "[email protected]"
  }
}
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.