KakaJSON

repository·master·Indexed 22 days ago

https://github.com/kakaopensource/kakajson

A fast Swift library for seamless conversion between JSON and Swift models, as well as archiving and unarchiving data to files. It utilizes the Convertible and ConvertibleEnum protocols to map JSON input types—including Dictionaries, Strings, and Data—into structs, classes, and enums. KakaJSON supports nested models, collections (Arrays, Sets, Dictionaries), and provides lifecycle hooks like kj_willConvertToModel and kj_didConvertToModel to intercept the conversion process.

Tokens
7.9K
Snippets
26
Records
34
Agent score
29%

What's inside KakaJSON

  1. Handle Nested Models and Collections

    master

    KakaJSON supports deeply nested JSON structures, including arrays, dictionaries, and sets, as long as the nested types also conform to Convertible.

    Nested Objects and Collections

    struct Book: Convertible {
        var name: String = ""
        var price: Double = 0.0
    }
    
    struct Person: Convertible {
        var name: String = ""
        var car: Car?            // Optional nested object
        var books: [Book]?       // Array of nested objects
        var dogs: [String: Dog]? // Dictionary of nested objects
    }

    Default Values in Nested Models

    If a property in your model is initialized with a default value, KakaJSON will preserve that value if the corresponding key is missing from the JSON, rather than overwriting it with a new instance.

    struct Person: Convertible {
        var name: String = ""
        // If 'car' is missing in JSON, this default instance is kept
        var car: Car = Car(name: "Bently", price: 106.5)
    }
  2. Convert Nested Models, Arrays, and Dictionaries

    master

    KakaJSON supports complex nested structures. If a property is another Convertible type, a collection of Convertible types (like [Book]), or a dictionary containing Convertible types (like [String: Dog]), the library will recursively convert them into the appropriate JSON structure.

    struct Book: Convertible {
        var name: String = ""
        var price: Double = 0.0
    }
     
    struct Dog: Convertible {
        var name: String = ""
        var age: Int = 0
    }
     
    struct Person: Convertible {
        var name: String = "Jack"
        var car: Car? = Car(name: "Bently", price: 106.666)
        var books: [Book]? = [Book(name: "Fast C++", price: 666.6)]
        var dogs: [String: Dog]? = ["dog0": Dog(name: "Wang", age: 5)]
    }
    
    let jsonString = Person().kj.JSONString()
  3. Handle key mapping in class inheritance

    master

    When using classes that conform to Convertible, you can override kj_modelKey(from:) to change how keys are resolved for a subclass.

    • To extend mapping: Call super.kj_modelKey(from: property) within your override to maintain the parent's mapping logic while adding new rules.
    • To completely replace mapping: Implement the method without calling super to ignore the parent's configuration for that specific subclass.
    class Person: Convertible {
        var name: String = ""
        required init() {}
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            return property.name == "name" ? "_name_" : property.name
        }
    }
     
    class Student: Person {
        var score: Int = 0
        
        override func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // `score` -> `_score_`, but `name` still uses Person's logic via super
            return property.name == "score" ? "_score_" : super.kj_modelKey(from: property)
        }
    }
  4. Define Convertible Models (Structs and Classes)

    master

    When defining models that conform to Convertible, follow these rules based on the type:

    Structs

    • Simple Structs: If all properties have default values, no explicit initializer is needed.
    • Manual Initializers: If properties do not have default values, you must implement an init() to satisfy the protocol requirements.

    Classes

    • Required Initializer: You must implement required init().
    • NSObject Inheritance: If your class inherits from NSObject, you must use required override init().

    Best Practices

    • Immutability: Avoid using let for properties you want KakaJSON to populate in release mode. Instead, use private(set) var to allow the library to set the value while keeping it read-only for consumers.
    // Struct with default values
    struct Dog: Convertible {
        var weight: Double = 0.0
        var name: String = ""
    }
    
    // Class with NSObject inheritance
    class Person: NSObject, Convertible {
        var name: String = ""
        var age: Int = 0
        required override init() {}
    }
    
    // Recommended way to handle 'let' properties
    struct Cat: Convertible {
        private(set) var weight: Double = 0.0
        let name: String = ""
    }
    struct Dog: Convertible {
        var weight: Double = 0.0
        var name: String = ""
    }
    
    class Person: NSObject, Convertible {
        var name: String = ""
        var age: Int = 0
        required override init() {}
    }
  5. Convert Models to and from Files

    master

    KakaJSON provides write(_:to:) and read(_:from:) functions to quickly archive and unarchive data (including Strings, Dates, Arrays, Sets, Dictionaries, and custom Models) to a file path (String or URL).

    To use custom models, your struct or class must conform to the Convertible protocol.

  6. Install KakaJSON via Swift Package Manager

    master

    To use Swift Package Manager (requires Xcode 11+):

    1. Open your project in Xcode.
    2. Go to the File tab.
    3. Select Swift Packages.
    4. Select Add Package Dependency.
    5. Enter the KakaJSON repository URL: https://github.com/kakaopensource/KakaJSON.git.

    Alternatively, you can search for KakaJSON directly in Xcode if you are logged in with your GitHub account.

  7. Convert JSON to Models using KakaJSON

    master

    To use KakaJSON, your data models (structs or classes) must conform to the Convertible protocol. You can then convert various JSON formats into model instances using the .kj.model() extension or the global model(from:type:) function.

    Supported JSON input types:

    • Dictionary ([String: Any], NSDictionary, NSMutableDictionary)
    • String (NSString, NSMutableString)
    • Data (NSData, NSMutableData)

    Basic Usage

    struct Cat: Convertible {
        var name: String = ""
        var weight: Double = 0.0
    }
    
    let json: [String: Any] = [
        "name": "Miaomiao",
        "weight": 6.66
    ]
    
    // Using the .kj extension
    let cat1 = json.kj.model(Cat.self)
    
    // Using the global function
    let cat2 = model(from: json, Cat.self)
    struct Cat: Convertible {
        var name: String = ""
        var weight: Double = 0.0
    }
    
    let json: [String: Any] = [
        "name": "Miaomiao",
        "weight": 6.66
    ]
    
    let cat1 = json.kj.model(Cat.self)
    let cat2 = model(from: json, Cat.self)
  8. Configure key mapping globally or locally with `ConvertibleConfig`

    master

    Instead of implementing kj_modelKey(from:) on every model, you can use ConvertibleConfig to define mapping rules centrally.

    Global Configuration

    Sets a rule for all types conforming to Convertible.

    ConvertibleConfig.setModelKey { property in
        property.name.kj.underlineCased()
    }

    Local (Type-Specific) Configuration

    Sets a rule for specific types. This also affects subclasses.

    ConvertibleConfig.setModelKey(for: [Person.self, Car.self]) { property in
        property.name.kj.underlineCased()
    }

    Resolution Order

    When KakaJSON looks for a key mapping, it follows this priority:

    1. The model's own kj_modelKey(from:) implementation.
    2. ConvertibleConfig for the specific type.
    3. ConvertibleConfig for the type's superclass.
    4. ... (continuing up the inheritance chain)
    5. The global ConvertibleConfig.
    // Global config
    ConvertibleConfig.setModelKey { property in
        property.name.kj.underlineCased()
    }
    
    // Config of Person
    ConvertibleConfig.setModelKey(for: Person.self) { property in
        property.name == "name" ? "_name_" : property.name
    }
  9. Convert JSON to Integer types

    master

    KakaJSON supports mapping various JSON values to Swift integer types (e.g., Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Int, UInt).

    Key behaviors:

    • String conversion: Strings containing numeric characters are converted to the target integer type.
    • Boolean conversion: true is treated as 1, and false is treated as 0.
    • String-based Booleans: Strings like "true", "TRUE", "YES", or "yes" convert to 1. Strings like "false", "FALSE", "NO", or "no" convert to 0.
    • Failure handling: If conversion fails, the model retains its default value defined in the struct.