JSONJoy - Convert JSON to Swift objects

Related tags

JSON JSONJoy-Swift
Overview

JSONJoy

Convert JSON to Swift objects. The Objective-C counterpart can be found here: JSONJoy.

Parsing JSON in Swift has be likened to a trip through Mordor, then JSONJoy would be using eagles for that trip.

Deprecation

The release of Swift 4 brought support for the new Codable protocol. This renders most of JSONJoy unneccessary and thus will be deprecated. Version 3.0.2 has been update to support Swift 4 as a means of backward compatibility, but I would encourage the adoption of the new Codable protocol. I wrote a post about it here

First thing is to import the framework. See the Installation instructions on how to add the framework to your project.

import JSONJoy

Example

First here is some example JSON we have to parse.

{
    "id" : 1,
    "first_name": "John",
    "last_name": "Smith",
    "age": 25,
    "address": {
        "id": 1,
        "street_address": "2nd Street",
        "city": "Bakersfield",
        "state": "CA",
        "postal_code": 93309
     }

}

We want to translate that JSON to these Swift objects:

struct Address {
    let objID: Int?
    let streetAddress: String?
    let city: String?
    let state: String?
    let postalCode: String?
    init() {

    }
}

struct User {
    let objID: Int?
    let firstName: String?
    let lastName: String?
    let age: Int?
    let address = Address()
    init() {

    }
}

Normally this would put us in a validation nightmare:

var user = User()
var error: NSError?
var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(), error: &error)
if let userDict = response as? NSDictionary {
    if let addressDict = userDict["address"] as? NSDictionary {
        user.address.city = addressDict["city"] as? String
        user.address.streetAddress = addressDict["street_address"] as? String
        //etc, etc
    }
    user.firstName = userDict["first_name"] as? String
    user.lastName = userDict["last_name"] as? String
    //etc, etc
}

JSONJoy makes this much simpler. We have our Swift objects implement the JSONJoy protocol:

struct Address : JSONJoy {
    let objID: Int
    let streetAddress: String
    let city: String
    let state: String
    let postalCode: String
    let streetTwo: String?

    init(_ decoder: JSONLoader) throws {
        objID = try decoder["id"].get()
        streetAddress = try decoder["street_address"].get()
        city = try decoder["city"].get()
        state = try decoder["state"].get()
        postalCode = try decoder["postal_code"].get()
        streetTwo = decoder["street_two"].getOptional()
        
        //just an example of "checking" for a property. 
        if let meta: String = decoder["meta"].getOptional() {
            print("found some meta info: \(meta)")
        }
    }
}

struct User : JSONJoy {
    let objID: Int
    let firstName: String
    let lastName: String
    let age: Int
    let address: Address
    let addresses: [Address]

    init(_ decoder: JSONLoader) throws {
        objID = try decoder["id"].get()
        firstName = try decoder["first_name"].get()
        lastName = try decoder["last_name"].get()
        age = try decoder["age"].get()
        address = try Address(decoder["address"])
        addresses = try decoder["addresses"].get() //infers the type and returns a valid array
    }
}

Then when we get the JSON back:

do {
	var user = try User(JSONLoader(data))
	println("city is: \(user.address.city)")
	//That's it! The object has all the appropriate properties mapped.
} catch {
	print("unable to parse the JSON")
}

This also has automatic optional validation like most Swift JSON libraries.

//some randomly incorrect key. This will work fine and the property will just be nil.
firstName = decoder[5]["wrongKey"]["MoreWrong"].getOptional()
//firstName is nil, but no crashing!

Custom Types

If you to extend a standard Foundation type (you probably won't need to though)

extension UInt64:   JSONBasicType {}

SwiftHTTP

This can be combined with SwiftHTTP to make API interaction really clean and easy.

https://github.com/daltoniam/SwiftHTTP#clientserver-example

Requirements

JSONJoy requires at least iOS 7/OSX 10.10 or above.

Installation

Swift Package Manager

Add the project as a dependency to your Package.swift:

import PackageDescription

let package = Package(
    name: "YourProject",
    dependencies: [
        .Package(url: "https://github.com/daltoniam/JSONJoy-Swift", majorVersion: 3)
    ]
)

CocoaPods

Check out Get Started tab on cocoapods.org.

To use JSONJoy-Swift in your project add the following 'Podfile' to your project

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'JSONJoy-Swift', '~> 3.0.2'

Then run:

pod install

Carthage

Check out the Carthage docs on how to add a install. The JSONJoy framework is already setup with shared schemes.

Carthage Install

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate JSONJoy into your Xcode project using Carthage, specify it in your Cartfile:

= 3.0.2">
github "daltoniam/JSONJoy-Swift" >= 3.0.2

Rogue

First see the installation docs for how to install Rogue.

To install JSONJoy run the command below in the directory you created the rogue file.

rogue add https://github.com/daltoniam/JSONJoy-Swift

Next open the libs folder and add the JSONJoy.xcodeproj to your Xcode project. Once that is complete, in your "Build Phases" add the JSONJoy.framework to your "Link Binary with Libraries" phase. Make sure to add the libs folder to your .gitignore file.

Other

Simply grab the framework (either via git submodule or another package manager).

Add the JSONJoy.xcodeproj to your Xcode project. Once that is complete, in your "Build Phases" add the JSONJoy.framework to your "Link Binary with Libraries" phase.

Add Copy Frameworks Phase

If you are running this in an OSX app or on a physical iOS device you will need to make sure you add the JSONJoy.framework included in your app bundle. To do this, in Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar. In the tab bar at the top of that window, open the "Build Phases" panel. Expand the "Link Binary with Libraries" group, and add JSONJoy.framework. Click on the + button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add JSONJoy.framework.

TODOs

  • Add Unit Tests

License

JSONJoy is licensed under the Apache v2 License.

Contact

Dalton Cherry

Comments
  • Nested arrays .append

    Nested arrays .append

    Flowing the instructions I am getting an error about missing the .append function

    image

    import Cocoa

    struct Status_response :JSONJoy { var status: String? var issu: Issues? init(){} init(_ decoder: JSONDecoder){ status = decoder["status"].string issu = Issues(decoder["issues"]) } }

    struct Issues:JSONJoy {

    var issues: Array<Issue>?
    init(){}
    init(_ decoder: JSONDecoder){
        //we check if the array is valid then alloc our array and loop through it, creating the new address objects.
        if let issu = decoder["issues"].array {
            issues = Array<Issue>()
            for issuDecoder in issu {
                issues += Issue(issuDecoder)
            }
        }
    }
    

    }

    struct Issue:JSONJoy { var id: Int? var skill_name : String? var problem : String? var priority : Int? init(){} init(_ decoder: JSONDecoder){ id = decoder["id"].integer skill_name = decoder["skill_name"].string problem = decoder["problem"].string priority = decoder["priority"].integer } }

    opened by jasonpjohnson 14
  • Handling optional array

    Handling optional array

    Hi Dalton,

    This is a great tool. Thanks.

    I encountered a scenario.

    String? can use getOptional() to obtain string [String]? will result in nil

    How to resolve? Or did I miss something.

    opened by chriskan0305 5
  • Handling dynamic and meaningful keys

    Handling dynamic and meaningful keys

    Hi Dalton, library is a life-saver, thank you thank you.

    Question: I have JSON that looks like this:

    {
        "userdata": {
            "192.168.88.253": {
                "applications": {
                    "54.230.87.156": {
                        "upbytes": 1055296,
                        "downbytes": 297264
                    },
                    "63.203.87.82": {
                        "upbytes": 18816,
                        "downbytes": 18816
                    },
                    "63.203.87.92": {
                        "upbytes": 18816,
                        "downbytes": 18816
                    }
                }
            }
        }
    }
    

    Before I go and refactor this to be an array.. Is there a way JSONJoy can decode dynamic and meaningful keys like this?

    In the above example '192.168.88.253' is a key I can't get to decode since it can be any arbitrary IP address (and there may be 0-infinity passed to JSONJoy).

    My code needs access to both the IP, its applications and then for each application another dynamic and meaningful key and then its bytes data.

    I could convert these down to just arrays of arrays without meaningful keys but that will create a domino effect of refactoring I'd like to avoid..

    Thanks!

    opened by siberian1967 5
  • swift 2.0 : toInt() method is given a error. Because,In Swift 2.x, the .toInt() function was removed from String

    swift 2.0 : toInt() method is given a error. Because,In Swift 2.x, the .toInt() function was removed from String

            if lower == "true" || lower.toInt() > 0 {
    

    swift 2.0 : toInt() method is given a error. Because,In Swift 2.x, the .toInt() function was removed from String

    opened by cczufish 5
  • Swift 4 issue with

    Swift 4 issue with

    In below two functions

    /** get typed Array of JSONJoy and have it throw if it doesn't work */ public func get<T: JSONJoy>() throws -> [T] { guard let a = getOptionalArray() else { throw JSONError.wrongType } return try a.reduce(T) { $0.0 + [try T($0.1)] } }

    /**
     get typed `Array` and have it throw if it doesn't work
     */
    open func get<T: JSONBasicType>() throws -> [T] {
        guard let a = getOptionalArray() else { throw JSONError.wrongType }
        return try a.reduce([T]()) { $0.0 + [try $0.1.get()] }
    }
    

    In below two return values

    return try a.reduce(T) { $0.0 + [try T($0.1)] } return try a.reduce(T) { $0.0 + [try $0.1.get()] }

    Error = "Contextual type for closure argument list expects 1 argument, but 2 were specified".

    Update your code respectively.

    opened by LalitKY 4
  • Swift4 compile error

    Swift4 compile error

    First of all, thank for your development.

    My swift project made in swift 4 version, and JSONJoy 3.0.2 version.

    I'm stuck in the error msg when complie. That msg was "Contextual closure type '(_, _) -> _' expects 2 arguments, but 1 was used in closure body"..

    so i can't modify ..

    the code is

    public func getOptional<T: JSONJoy>() -> [T]? { guard let a = getOptionalArray() else { return nil } do { return try a.reduce(T) { $0.0 + [try T($0.1)] } } catch { return nil } }

    opened by freddyKang 3
  • refactored module and added some tests for the refactored methods

    refactored module and added some tests for the refactored methods

    Hi,

    I fixed the issue that I opened earlier today and did some refactoring to the whole module. Furthermore I added some unit tests that can be executed when selecting the JSONJoyTestApplication scheme.

    Best regards

    opened by kyc3 3
  • Ambiguous use of 'getString()'

    Ambiguous use of 'getString()'

    Hi,

    with version 2.1.0 I am unable to use the framework as for example

    jsonDecoder["MyKey"].getString()

    fails, as there is one getString() that throws and one that returns an optional.

    EDIT: This is also true for getInt() etc.

    Best regards

    opened by kyc3 3
  • How to parse a string using JSONJoy?

    How to parse a string using JSONJoy?

    For testing purpose I need to translate a string like "{\"ip\": \"128.74.141.82\"}" to an object, imitating server's response.

    How can it be done?

    opened by basharov 3
  • Question?

    Question?

    Hi, I have the following JSONDecoder: Optional({ AppUserId = "JSONJoy.JSONDecoder"; ContentToDisplay = "JSONJoy.JSONDecoder"; CreationDate = "JSONJoy.JSONDecoder"; ExiprationDate = "JSONJoy.JSONDecoder"; IsSeenByUser = "JSONJoy.JSONDecoder"; NotificationContent = "JSONJoy.JSONDecoder"; NotificationID = "JSONJoy.JSONDecoder"; NotificationType = "JSONJoy.JSONDecoder"; SISUserID = "JSONJoy.JSONDecoder"; SISUserNumber = "JSONJoy.JSONDecoder"; })

    All keys return a value except for "NotificationContent" it returns another JSON. I was able to get all values by using: decoder["AppUserID"] for example, but for NotificationContent I had to read it as String, convert it to NSData then decode it using JSONDecoder, using decoder["NotificationContent"] just didn't work. Below is my full code, it would be great if you let me know what I'm doing wrong

     let resultDecoder = JSONDecoder(response)[resultKey];
            let serverResponse = ServerResponseModel(resultDecoder);
            if serverResponse.Result == 0 {
                var resultArray: Array<JSONDecoder>? = resultDecoder["OperationData"].array;
                if let arr = resultArray {
                    for i in arr {
                        let jsonNotificationType = i["NotificationType"].integer!;
                        let jsonMessage: JSONDecoder = i["NotificationContent"]; // This did not work, it was treated like i["NotificationContent"] value was sent as a String and not NSData (if you recall my last issue I provided it's link below the code) 
                        // For some reason we must decode NotificationContenet again like shown below
                        let x = (i["NotificationContent"].string! as NSString).dataUsingEncoding(NSUTF8StringEncoding);
                        switch jsonNotificationType{
                        case 0:
                            let messageModel = MessageModel(JSONDecoder(x!));
                        break;
                     }
                   }
    

    Link to my previous issue in-case it can help

    opened by OmarBizreh 3
  • Array with no Key in JSON

    Array with no Key in JSON

    What if you're getting a response of just an array, no keys. for example...

    [
        {
            "SecureString" : "this is a string"
        },
        {
            "SecureString" : "this is another string"
        }
    ]
    

    How can JSONJoy handle that?

    opened by fwhenin 3
  • Array of Object Not working

    Array of Object Not working

    I have JSON array like this.

    {
        "id": "7397632",
        "studentid": "554",
        "daily_attendance": [
            {
                "period_id": "29",
                "attendance_type_id": "1"
            },
            {
                "period_id": "30",
                "attendance_type_id": "1"
            },
            {
                "period_id": "31",
                "attendance_type_id": "1"
            },
            {
                "period_id": "32",
                "attendance_type_id": "1"
            }
        ]
    },
    {
        "id": "5397643",
        "studentid": "626",
        "daily_attendance": [
          
          {
                "period_id": "30",
                "attendance_type_id": "1"
            },
            {
                "period_id": "31",
                "attendance_type_id": "1"
            },
            {
                "period_id": "32",
                "attendance_type_id": "1"
            }
        ]
    }
    

    This is my class

    import JSONJoy struct StudentTimeTable : JSONJoy {

    let id : String?
    let studentid : String?
    let daily_attendance : [Daily_attendance]?
    
    
    init(_ decoder: JSONLoader) throws {
        id = try decoder["id"].get()
        studentid = try decoder["studentid"].get()
        //daily_attendance = try [Daily_attendance(decoder["daily_attendance"])]
        daily_attendance = try decoder["daily_attendance"].get()
    }
    

    Problem is it does not create the 'daily_attendance' array Object.

    opened by chathuralakmal 0
  • Issue with Swift 4

    Issue with Swift 4 "JSONLoader' is not a member type of 'JSONJoy"

    Hi, I am using JSONJoy in my project which supports Swift 4 but, getting the following issue while parsing:

    'JSONLoader' is not a member type of 'JSONJoy'

    How can I resolve this issue?

    opened by Neena-Mishra 0
Owner
Dalton
Developer. Disciple. Computer science aficionado. Mobile enthusiast. Drum machine. Rock climbing extraordinaire. The next American Ninja Warrior.
Dalton
Codable code is a Swift Package that allows you to convert JSON Strings into Swift structs

Codable code is a Swift Package that allows you to convert JSON Strings into Swift structs.

Julio Cesar Guzman Villanueva 2 Oct 6, 2022
AlamofireObjectMappe - An Alamofire extension which converts JSON response data into swift objects using ObjectMapper

AlamofireObjectMapper An extension to Alamofire which automatically converts JSON response data into swift objects using ObjectMapper. Usage Given a U

Tristan Himmelman 2.6k Dec 29, 2022
HandyJSON is a framework written in Swift which to make converting model objects to and from JSON easy on iOS.

HandyJSON To deal with crash on iOS 14 beta4 please try version 5.0.3-beta HandyJSON is a framework written in Swift which to make converting model ob

Alibaba 4.1k Dec 29, 2022
JSONExport is a desktop application for Mac OS X which enables you to export JSON objects as model classes with their associated constructors, utility methods, setters and getters in your favorite language.

JSONExport JSONExport is a desktop application for Mac OS X written in Swift. Using JSONExport you will be able to: Convert any valid JSON object to a

Ahmed Ali 4.7k Dec 30, 2022
An extension for Alamofire that converts JSON data into Decodable objects.

Swift 4 introduces a new Codable protocol that lets you serialize and deserialize custom data types without writing any special code and without havin

Nikita Ermolenko 749 Dec 5, 2022
JSONHelper - ✌ Convert anything into anything in one operation; JSON data into class instances, hex strings into UIColor/NSColor, y/n strings to booleans, arrays and dictionaries of these; anything you can make sense of!

JSONHelper Convert anything into anything in one operation; hex strings into UIColor/NSColor, JSON strings into class instances, y/n strings to boolea

Baris Sencan 788 Jul 19, 2022
CLT to convert JSON5 to JSON

JSON5toJSON A small command line tool to convert JSON5 to JSON. Runs on macOS 12 (Monterey). Installation Install using Mint. $ mint install auramagi/

Mike Apurin 1 Nov 4, 2021
JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift.

JSEN /ˈdʒeɪsən/ JAY-sən JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift. A JSON, as defined in the EC

Roger Oba 8 Nov 22, 2022
Swift-json - High-performance json parsing in swift

json 0.1.4 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
JSON-Practice - JSON Practice With Swift

JSON Practice Vista creada con: Programmatic + AutoLayout Breve explicación de l

Vanesa Giselle Korbenfeld 0 Oct 29, 2021
Ss-json - High-performance json parsing in swift

json 0.1.1 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
Swift parser for JSON Feed — a new format similar to RSS and Atom but in JSON.

JSONFeed Swift parser for JSON Feed — a new format similar to RSS and Atom but in JSON. For more information about this new feed format visit: https:/

Toto Tvalavadze 31 Nov 22, 2021
JSONNeverDie - Auto reflection tool from JSON to Model, user friendly JSON encoder / decoder, aims to never die

JSONNeverDie is an auto reflection tool from JSON to Model, a user friendly JSON encoder / decoder, aims to never die. Also JSONNeverDie is a very important part of Pitaya.

John Lui 454 Oct 30, 2022
Encode and decode deeply-nested data into flat Swift objects

DeepCodable: Encode and decode deeply-nested data into flat Swift objects Have you ever gotten a response from an API that looked like this and wanted

Mike Lewis 91 Dec 26, 2022
Implement dynamic JSON decoding within the constraints of Swift's sound type system by working on top of Swift's Codable implementations.

DynamicCodableKit DynamicCodableKit helps you to implement dynamic JSON decoding within the constraints of Swift's sound type system by working on top

SwiftyLab 15 Oct 16, 2022
Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable

Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable. Elevate should no longer be used for

Nike Inc. 611 Oct 23, 2022
Freddy - A reusable framework for parsing JSON in Swift.

Why Freddy? Parsing JSON elegantly and safely can be hard, but Freddy is here to help. Freddy is a reusable framework for parsing JSON in Swift. It ha

Big Nerd Ranch 1.1k Jan 1, 2023
[Deprecated] A shiny JSON parsing library in Swift :sparkles: Loved by many from 2015-2021

?? Deprecation Notice ?? Gloss has been deprecated in favor of Swift's Codable framework. The existing Gloss source is not going away, however updates

Harlan Kellaway 1.6k Nov 24, 2022
Himotoki (紐解き) is a type-safe JSON decoding library written purely in Swift.

Himotoki Himotoki (紐解き) is a type-safe JSON decoding library written purely in Swift. This library is highly inspired by the popular Swift JSON parsin

IKEDA Sho 799 Dec 6, 2022