HandyJSON Documentation

repository·master·Indexed 26 days ago

https://github.com/alibaba/handyjson

A Swift framework for simplifying the conversion of model objects (classes and structs) to and from JSON using runtime reflection instead of KVC. It supports custom key mapping via the mapping(mapper:) function, designated paths for nested deserialization, and built-in transformers for types like Date, URL, and Data. The library provides methods for serializing models to dictionaries or strings and supports RawRepresentable enums through the HandyJSONEnum protocol.

Tokens
7.9K
Snippets
28
Records
30
Agent score
38%

What's inside HandyJSON

  1. Install HandyJSON manually

    master

    To integrate HandyJSON manually into your project:

    1. Add HandyJSON as a git submodule in your project's top-level directory: git init && git submodule add https://github.com/alibaba/HandyJSON.git
    2. Drag the HandyJSON.xcodeproj from the new HandyJSON folder into your project's Project Navigator.
    3. In your application project's General panel, click the + button under Embedded Binaries.
    4. Select the HandyJSON.framework that matches your application's target platform.
    git init && git submodule add https://github.com/alibaba/HandyJSON.git
  2. Deserialize JSON to Enum with HandyJSONEnum

    master

    To support value-type enums, declare that the enum conforms to the HandyJSONEnum protocol. This allows HandyJSON to map JSON values directly to enum cases.

    enum AnimalType: String, HandyJSONEnum {
        case Cat = "cat"
        case Dog = "dog"
        case Bird = "bird"
    }
    
    struct Animal: HandyJSON {
        var name: String?
        var type: AnimalType?
    }
    
    let jsonString = "{\"type\":\"cat\",\"name\":\"Tom\"}"
    if let animal = Animal.deserialize(from: jsonString) {
        print(animal.type?.rawValue)
    }
  3. Install HandyJSON via Cocoapods

    master

    To install HandyJSON using Cocoapods, add the following line to your Podfile and run pod install in your terminal. Use version ~> 5.0.2 for Swift 5.0/5.1 (Xcode 10.2+/11.0+).

    pod 'HandyJSON', '~> 5.0.2'
    $ pod install
  4. Deserialize JSON to Model with HandyJSON

    master

    To enable deserialization (converting JSON to a Model), your Model must conform to the HandyJSON protocol. For class types, you must implement a required init() method. HandyJSON uses the property names as the keys for JSON parsing by default.

    class BasicTypes: HandyJSON {
        var int: Int = 2
        var doubleOptional: Double?
        var stringImplicitlyUnwrapped: String!
    
        required init() {}
    }
    
    let jsonString = "{\"doubleOptional\":1.1,\"stringImplicitlyUnwrapped\":\"hello\",\"int\":1}"
    if let object = BasicTypes.deserialize(from: jsonString) {
        print(object.int)
    }
  5. Deserialize JSON to Struct

    master

    For struct models, you do not need to declare an explicit init() because structs provide a default empty initializer. However, if you define a custom initializer, you must explicitly declare an empty init() to satisfy the HandyJSON protocol requirements.

    struct BasicTypes: HandyJSON {
        var int: Int = 2
        var doubleOptional: Double?
        var stringImplicitlyUnwrapped: String!
    }
    
    let jsonString = "{\"doubleOptional\":1.1,\"stringImplicitlyUnwrapped\":\"hello\",\"int\":1}"
    if let object = BasicTypes.deserialize(from: jsonString) {
        // ...
    }
  6. Trigger didSet/willSet observers during mapping

    master

    By default, HandyJSON assigns values directly in memory, bypassing Swift property observers like didSet and willSet.

    To trigger these observers, you must:

    1. Inherit from NSObject.
    2. Declare the properties as dynamic.

    Alternatively, use the didFinishMapping() function to execute logic after the mapping process is complete.

    // Option 1: Using dynamic and NSObject to trigger didSet/willSet
    class BasicTypes: NSObject, HandyJSON {
        dynamic var int: Int = 0 {
            didSet {
                print("oldValue: ", oldValue)
            }
            willSet {
                print("newValue: ", newValue)
            }
        }
    
        public override required init() {}
    }
    
    // Option 2: Using didFinishMapping for post-mapping logic
    class BasicTypes: HandyJSON {
        var int: Int?
    
        required init() {}
    
        func didFinishMapping() {
            print("you can fill some observing logic here")
        }
    }
  7. Handle mapping in inherited classes

    master
    Due to Swift type constraints, if you want to use the mapping function (or didFinishMapping) in a subclass, you must first define an empty mapping function in the top-level parent class and then override it in the subclass.
  8. Use built-in transformers for Date, URL, and more

    master

    HandyJSON provides built-in transformers for common non-basic types. Use these within your mapping function to handle complex types automatically.

    Supported transformers:

    • CustomDateFormatTransform(formatString:) for Date.
    • NSDecimalNumberTransform() for NSDecimalNumber.
    • URLTransform(shouldEncodeURLString:) for URL.
    • DataTransform() for Data.
    • HexColorTransform() for UIColor.
    class ExtendType: HandyJSON {
        var date: Date?
        var decimal: NSDecimalNumber?
        var url: URL?
        var data: Data?
        var color: UIColor?
    
        func mapping(mapper: HelpingMapper) {
            mapper <<<
                date <-- CustomDateFormatTransform(formatString: "yyyy-MM-dd")
    
            mapper <<<
                decimal <-- NSDecimalNumberTransform()
    
            mapper <<<
                url <-- URLTransform(shouldEncodeURLString: false)
    
            mapper <<<
                data <-- DataTransform()
    
            mapper <<<
                color <-- HexColorTransform()
        }
    
        public required init() {}
    }
  9. Exclude properties from deserialization and serialization

    master

    To prevent specific properties from being included in JSON parsing or generation, use the >>> operator within the mapping function. This is useful for properties that do not conform to HandyJSON or should remain private to the object.

    class Cat: HandyJSON {
        var id: Int64!
        var name: String!
        var notHandyJSONTypeProperty: NotHandyJSONType?
        var basicTypeButNotWantedProperty: String?
    
        required init() {}
    
        func mapping(mapper: HelpingMapper) {
            mapper >>> self.notHandyJSONTypeProperty
            mapper >>> self.basicTypeButNotWantedProperty
        }
    }
  10. Serialize Model to JSON

    master

    Models conforming to HandyJSON can be serialized back to various formats:

    • toJSON(): Returns a [String: Any] dictionary.
    • toJSONString(): Returns a JSON string.
    • toJSONString(prettyPrint: true): Returns a formatted (pretty-printed) JSON string.
    let object = BasicTypes()
    object.int = 1
    object.doubleOptional = 1.1
    object.stringImplicitlyUnwrapped = "hello"
    
    print(object.toJSON()!) // serialize to dictionary
    print(object.toJSONString()!) // serialize to JSON string
    print(object.toJSONString(prettyPrint: true)!) // serialize to pretty JSON string
  11. Deserialize JSON to a Struct

    master

    To deserialize JSON into a struct, conform the struct to the HandyJSON protocol. Structs use the compiler-provided default empty initializer automatically. However, if you have a custom designated initializer, you must explicitly declare an empty one.

    struct BasicTypes: HandyJSON {
        var int: Int = 2
        var doubleOptional: Double?
        var stringImplicitlyUnwrapped: String!
    }
    
    let jsonString = "{\"doubleOptional\":1.1,\"stringImplicitlyUnwrapped\":\"hello\",\"int\":1}"
    if let object = BasicTypes.deserialize(from: jsonString) {
        // ...
    }