Coder Social home page Coder Social logo

parson's Introduction

ParSON

A lightweight JSON to swift Parser

Apple provides a way to get JSON from a URL but after that generally parsing is left up to you, and it's generally not the cleanest of affairs. This is my personal take on providing a solution.

How to

Given the JSON of

let jsonData = 
[
	"first":
	[
		"name": "Alphabet",
		"upperCase": ["A", "B", "C"],
		"lowerCase": ["a","b","c"]
	],
	"second":
	[
		"name": "Numerics",
		"data": ["1","2","3"]
	]
]

Dictionaries

let parsonObject = ParSON(collection: jsonData)

let firstDictionary: [String: AnyObject] = try! parsonObject.value(forKeyPath: "first")
let secondDictionary: [String: AnyObject] = try! parsonObject.value(forKeyPath: "second")

and you can then access the values normally as one would traverse a swift collection.

If however you wish to iterate through the json and get a value directly, you can.

let nameOfFirstStuff: String = try! parsonObject.value(forKeyPath: "first.name")
let nameOfSecondStuff: String = try! parsonObject.value(forKeyPath: "second.name")

So dictionaries should be pretty straight forward each successive key inside it's parent dicitonary is prepended with a "."

e.g "this.that.thisOneInsideThat.other"

Arrays

let upperCaseAlphabetFirstLetter: [String] = try! parsonObject.value(forKeyPath: "first.upperCase[0]") // 'A'
let thirdNumericalItem: [String] = try! parsonObject.value(forKeyPath: "second.data[2]") // '3'

just place the index you wish to access inside square brackets, this also works for nested array

let nestedJSONArray =
[
	"response":
	[
		[
			["hello", "there"],
			["cup", "of", "tea?"]
		],
		["good", "bye"]
	],
	"addOn": ["and", "good", "luck"]
]

let parsonObject = ParSON(collection: nestedJSONArray)

var greeting: String = try! parsonObject.value(forKeyPath: "response[0][0]") // 'hello' 
greeting.append(" \(try! parsonObject.value(forKeyPath: "response[0][1]") as String).") // 'hello there. '
greeting.append(" \(try! parsonObject.value(forKeyPath: "response[0][1]") as String)") // 'hello there. cup'
greeting.append(" \(try! parsonObject.value(forKeyPath: "response[0][1]") as String)") // 'hello there. cup of'
greeting.append(" \(try! parsonObject.value(forKeyPath: "response[0][1]") as String)") // 'hello there. cup of tea?'

var leaving = parsonObject.value(forKeyPath: "response[1][0]") // 'good'
leaving.append( " \(try! parsonObject.value(forKeyPath: "response[1][0]") as String).") // 'good bye.'

leaving.append( " \(try! parsonObject.value(forKeyPath: "addOn[0]") as String)") // 'good bye. and'
leaving.append( " \(try! parsonObject.value(forKeyPath: "addOn[1]") as String)") // 'good bye. and good'
leaving.append( " \(try! parsonObject.value(forKeyPath: "addOn[2]") as String).") // 'good bye. and good luck.'

Collection Enumeration

If however instead of manually accessing the collection for each key and or index and you just wish to iterate over a known dictionary or array you can do so like so.

let jsonData = 
[
	"employees":
	[
		"jim",
		"barb",
		"sam"
	],
	"managers":
	[
		"racheal": ["dept": "HR", "salary": 123],
		"gavin": ["dept": "RND", "salary": 122]
	]
]

let parsonObject = ParSON(collection: jsonData)

parsonObject.enumerateObject(atKeyPath: "employees") { (keyIndex, element) in
	print("element: \(element) at index \(keyIndex)")
}

// element: jim at index 0
// element: barb at index 1
// element: sam at index 2

jsonObject.enumerateObject(atKeyPath: "managers") { (keyIndex, element) in
	print("element: \(element) for key \(keyIndex)")
}

// element: {"dept": "HR", "salary": 123} for key racheal
// element: {"dept": "RND", "salary": 122} for key gavin

Object Enumeration

Generally you will pull values from the JSON and then assign the value to an instance variable of a class, the larger the class the more tedious this can become, ParSON however offers a way for you to dictate how each class will map the values and when enumerating you will get the object with the mapped values assigned.

First, your class should conform to the protocol ParSONDeserializable

protocol ParSONDeserializable 
{
	static func create(inContext context: NSManagedObjectContext) -> Self
	func deserialize(_ parsonObject: ParSON, context: NSManagedObjectContext, keyPath: String) throws
}

Example

fileprivate class JSONableTestable: ParSONDeserializable
{
    private(set) var text: String?
    private(set) var id: String?

    static func create(inContext context: NSManagedObjectContext) -> Self {
	return .init()
    }

    func deserialize(_ parsonObject: ParSON, context: NSManagedObjectContext, keyPath: String) throws {
	self.id = try parsonObject.value(forKeyPath: "\(keyPath).id")
	self.text = try parsonObject.value(forKeyPath: "\(keyPath).text")
    }
}

let jsonData =
[
	"testables":
	[
		["id": "first"
		"text": "Welcome"],
		["id": "second"
		"text": "to my"],
		["id": "third"
		"text": "example"]
	]
]
	
	let jsonObject = ParSON(collection: jsonData)
	
	jsonObject.enumerateObjects(ofType: JSONableTestable.self, forKeyPath: "testables") { (jsonableObject) in
            
	    //The closure returns a JSONAble so, if you want to access the variables you need to cast it.
            guard let jsonTestable = jsonableObject as? JSONableTestable else { return }
            
		print("\(jsonTestable.id)")
		print("\(jsonTestable.text)")
        }
	
	// 'first'
	// 'Welcome'
	// 'second'
	// 'to my'
	// 'third'
	// 'example'

Core Data

You may notice that the ParSONDeserializable protocol and a few of the other methods have a parameter for an NSManagedObjectContext, this is in case you are using core data objects, you can pass the context through to create the objects inside the context.

Error Handling

There are 4 errors that can be raised while attempt to Parse

case NoValueForKey(String)
case IndexOutOfRange
case TypeMismatch
case InvalidString

NoValueForKey case

Will be thrown when you attempt to access a value with a non-existent key. Either simply the key does not exist or you havent got the correct path to get to that key

IndexOutOfRange case

Will be thrown when you attempt to access a value outside of the array range.

TypeMismatch case

Will occur when you dont infer the correct type when using value(forKeyPath: "example"). For example if you tell it to infer a String with as String but the variable is a collection i.e array or dictionary.

InvalidString case

This occurs when the string you attempt to parse with is the incorrect syntax, especially with arrays. e.g "[0" or "2]" or [] and any other that is not recognized

parson's People

Contributors

genhain avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.