A lightweight, type-safe JSON toolkit for Swift. JSONKit models any valid JSON
value as a Swift enum, provides ergonomic construction from native literals,
throwing typed accessors, Codable support, and convenient conversions to and
from Foundation and Core Data storable representations.
- Type-safe JSON model —
JSONValuemirrors the JSON data model (null, bool, number, string, array, object). - Ergonomic construction — build JSON directly from Swift literals and
collections via
JSONValueConvertible. - Throwing typed accessors — read values by key with clear, descriptive errors when a value is missing or has the wrong type.
Codableconformance — encode and decodeJSONValuewith the standardJSONEncoder/JSONDecoder.RawRepresentablebridging — convert to and from Foundation (Any/[String: Any]) representations, correctly distinguishing the numbers0/1from booleans.- Model mapping — map JSON objects onto your own types with the
JSONInitializable/JSONEncodable/JSONDecodableprotocols. - ISO 8601 dates — parse and format dates, including microsecond-precise fractional seconds.
- Core Data interop — convert values into Core Data storable types.
- Sendable —
JSONValueisHashableandSendable.
- Swift 6.3+
- Any Apple platform with Foundation.
Add JSONKit to your Package.swift dependencies:
dependencies:[.package(url:"https://github.com/thomasalbert1993/JSONKit.git", from:"1.0.0"),],Then add it to your target:
.target(
name:"YourTarget",
dependencies:["JSONKit"]),JSONValue is an indirect enum that represents any JSON value:
import JSONKit
letvalue:JSONValue=.object(["id":.number(1),"name":.string("Alice"),"active":.bool(true),"tags":.array([.string("swift"),.string("json")]),"deletedAt":.null,])JSONObject is a convenience alias for [String: JSONValue].
Thanks to JSONValueConvertible, native Swift types convert to JSONValue
automatically:
letvalue:JSONValue=["a","b","c"].jsonValue // .array([...])
letobject:JSONValue=["name":"Alice","age":30].jsonValueThe following types conform out of the box: JSONValue, NSNull, Bool,
Int (stored as .number), Double, String, arrays of convertibles, and
dictionaries of convertibles.
Note: You should not conform any additional types to
JSONValueConvertible.
The JSONObject initializer also lets you express null naturally with Swift
optionals — nil values become .null:
letobject=JSONObject(["a":1,"b":nil, // becomes .null
"c":"x",])Read values from a JSONObject with throwing accessors that validate types:
letid=try object.int(forKey:"id")letname=try object.string(forKey:"name")letactive=try object.bool(forKey:"active")lettags=try object.stringArray(forKey:"tags")Optional variants return nil when the key is missing or its value is null:
letnickname=try object.optionalString(forKey:"nickname") // String?
letscores=try object.optionalIntArray(forKey:"scores") // [Int]?Accessors exist for bool, int, double, string, object, arrays of
each, dates, and their optional counterparts. Reading a value with the wrong
type throws a descriptive JSONError.
You can also read typed values directly off a JSONValue:
letflag=tryJSONValue.bool(true).boolValue // true
letn=tryJSONValue.number(42).intValue // 42Booleans and the numbers 0/1 are interchangeable where sensible, and
intValue only accepts non-fractional numbers.
Decode enums backed by String, Int, or Double:
enumStatus:String{case active, inactive }letstatus:Status=try object.rawRepresentable(forKey:"status")letoptional:Status?=try object.optionalRawRepresentable(forKey:"status")Conform your types to JSONInitializable to build them from JSON:
structUser:JSONInitializable{letid:Intletname:Stringinit(from content:JSONObject)throws{
id =try content.int(forKey:"id")
name =try content.string(forKey:"name")}}letuser:User=try object.object(forKey:"user")letusers:[User]=try object.objectArray(forKey:"users")
// Or from a JSONObject directly:
letuser2=try object.to(User.self)letuser3:User=try object.instance()Use JSONEncodable for the reverse direction, and the JSONConvertible
alias (JSONEncodable & JSONDecodable) when a type does both.
JSONValue conforms to Codable, so it works with the standard encoders:
letdata=tryJSONEncoder().encode(value)letdecoded=tryJSONDecoder().decode(JSONValue.self, from: data)// Decode a JSONObject from raw Data:
letobject=tryJSONObject(data: data)
// Serialize back to a String (optionally pretty-printed):
letstring=try object.serialized(prettyPrinted:true)
// Convert an existing [String: Any] into a JSONObject:
letjson=try someDictionary.toJSON()Default equality is exact and order-sensitive. To compare arrays while ignoring
order and duplicates, use isEqual(to:handleArraysAsSets:):
leta:JSONValue=.array([.number(1),.number(2)])letb:JSONValue=.array([.number(2),.number(1)])
a == b // false
a.isEqual(to: b, handleArraysAsSets:true) // trueJSONValue.null.nullAsNil // nil
JSONValue.string("x").nullAsNil // .string("x")
JSONValue.optionalString(nil) // .null
someValue.when(condition) // someValue or .null
object.removingNullValues() // drops all .null entriesDate accessors parse ISO 8601 strings automatically, choosing the right format based on the string's contents (date only, date-time, or fractional seconds):
letdate=try object.date(forKey:"createdAt")letdates=try object.dateArray(forKey:"timestamps")Three shared formatters are available directly:
ISO8601DateFormatter.dateFormatter // full date, no time
ISO8601DateFormatter.dateTimeFormatter // date + time
ISO8601DateFormatter.dateTimeFractionalFormatter // microsecond precisionFor microsecond precision beyond the default millisecond support, use
MicroSecondISO8601DateFormatter.
Convert values into Core Data storable representations:
letstorable= value.coreDataStorable // Any
letobjectStorable= object.coreDataStorable // [String: Any]Failed lookups and conversions throw JSONError:
.invalidValue(_:expected:)— a value has an unexpected type..invalidValue(forKey:expected:in:)— a keyed value has an unexpected type..missingOrNullValue(forKey:in:)— a required key is missing ornull..unableToSerializeObject(_:)— serialization failed..unableToParseRawValue— aRawRepresentableor dictionary conversion failed.
Date parsing throws ISO8601DateFormatterError.invalidDate(_:).
The package includes a test suite built with the Swift Testing framework.
Run it with:
swift testJSONKit is available under the MIT license. See the LICENSE file for details.