JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift.

Related tags

JSON JSEN
Overview

JSEN

GitHub Action Build Status Swift 5.4 Supports iOS, macOS, tvOS, watchOS and Ubuntu Latest release

/ˈ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 ECMA-404 standard , can be:

  • A number
  • A boolean
  • A string
  • Null
  • An array of those things
  • A dictionary of those things

Thus, JSONs can be represented as a recursive enum (or indirect enum, in Swift), effectively creating a statically-typed JSON payload in Swift.

Installation

Using Swift Package Manager:

dependencies: [
    .package(name: "JSEN", url: "https://github.com/rogerluan/JSEN", .upToNextMajor(from: "1.0.0")),
]

Usage

I think it's essential for the understanding of how simple this is, for you to visualize the JSEN declaration:

/// A simple JSON value representation using enum cases.
public enum JSEN : Equatable {
    /// An integer value.
    case int(Int)
    /// A floating point value.
    case double(Double)
    /// A string value.
    case string(String)
    /// A boolean value.
    case bool(Bool)
    /// An array value in which all elements are also JSEN values.
    indirect case array([JSEN])
    /// An object value, also known as dictionary, hash, and map.
    /// All values of this object are also JSEN values.
    indirect case dictionary([String:JSEN])
    /// A null value.
    case null
}

That's it.

ExpressibleBy…Literal

Now that you're familiar with JSEN, it provides a few syntactic sugary utilities, such as conformance to most ExpressibleBy…Literal protocols:

  • ExpressibleByIntegerLiteral initializer returns an .int(…).
  • ExpressibleByFloatLiteral initializer returns a .double(…).
  • ExpressibleByStringLiteral initializer returns a .string(…).
  • ExpressibleByBooleanLiteral initializer returns a .bool(…).
  • ExpressibleByArrayLiteral initializer returns an .array(…) as long as its Elements are JSENs.
  • ExpressibleByDictionaryLiteral initializer returns an .dictionary(…) as long as its keys are Strings and Values JSENs.
  • ExpressibleByNilLiteral initializer returns a .null.

Conformance to ExpressibleBy…Literal protocols are great when you want to build a JSON structure like this:

let request: [String:JSEN] = [
    "key": "value",
    "another_key": 42,
]

But what if you're not working with literals?

let request: [String:JSEN] = [
    "amount": normalizedAmount // This won't compile
]

Enters the…

% Suffix Operator

let request: [String:JSEN] = [
    "amount": %normalizedAmount // This works!
]

The custom % suffix operator transforms any Int, Double, String, Bool, [JSEN] and [String:JSEN] values into its respective JSEN value.

By design, no support was added to transform Optional into a .null to prevent misuse.

Click here to expand the reason why it could lead to mistakes

To illustrate the possible problems around an %optionalValue operation, picture the following scenario:

let request: [String:JSEN] = [
    "middle_name": %optionalString
]
network.post(path: "user", parameters: request)
network.put(path: "user", parameters: request)
network.patch(path: "user", parameters: request)
network.mergePatch(path: "user", parameters: request)

In the scenarios above, what do you think should be the RESTful expected behavior?

If the % operator detected a nonnull String, great. But if it detected its underlying value to be .none (aka nil), it would convert the value to .null, which, when encoded, would be converted to NSNull() (more on this below in the Codable section). As you imagine, NSNull() and nil have very different behaviors when it comes to RESTful APIs - the former might delete the key information on the database, while the latter will simply be ignored by Swift Dictionary (as if the field wasn't even there).

Hence, if you want to use an optional value, make the call explicit by using either .null if you know the value must be encoded into a NSNull() instance, or unwrap its value and wrap it around one of the non-null JSEN cases.

Conformance to Codable

Of course! We couldn't miss this. JSEN has native support to Encodable & Decodable (aka Codable), so you can easily parse JSEN to/from JSON-like structures. Each case is mapped to its respective value type, and .null maps to a NSNull() instance (which, in a JSON, is represented by null).

One additional utility was added as well, which's the decode(as:) function. It receives a Decodable-conformant Type as parameter and will attempt to decode the JSEN value into the given type using a two-pass strategy:

  • First, it encodes the JSEN to Data, and attempts to decode that Data into the given type.
  • If that fails and the JSEN is a .string(…) case, it attempts to encode the JSEN's string using .utf8. If it is able to encode it, it attempts to decode the resulting Data into the given type.

Subscript Using KeyPath

Last, but not least, comes the KeyPath subscript.

Based on @olebegemann's article, KeyPath is a simple struct used to represent multiple segments of a string. It is initializable by a string literal such as "this.is.a.keypath" and, when initialized, the string gets separated by periods, which compounds the struct's segments.

The subscript to JSEN allows the following syntax:

let request: [String:JSEN] = [
    "1st": [
        "2nd": [
            "3rd": "Hello!"
        ]
    ]
]
print(request[keyPath: "1st.2nd.3rd"]) // "Hello!"

Without this syntax, you'd have to create multiple chains of awkward optionals and unwrap them in weird and verbosy ways to access a nested value in a dictionary. I'm not a fan of doing that :)

Contributions

If you spot something wrong, missing, or if you'd like to propose improvements to this project, please open an Issue or a Pull Request with your ideas and I promise to get back to you within 24 hours! 😇

References

JSEN was heavily based on Statically-typed JSON payload in Swift and other various implementations of this same utility spread throughout Stack Overflow and Swift Forums. I brought everything I needed together in this project because I couldn't find something similar as a Swift Package that had everything I needed.

License

This project is open source and covered by a standard 2-clause BSD license. That means you can use (publicly, commercially and privately), modify and distribute this project's content, as long as you mention Roger Oba as the original author of this code and reproduce the LICENSE text inside your app, repository, project or research paper.

Contact

Twitter: @rogerluan_

You might also like...
Simple JSON Object mapping written in Swift

ObjectMapper ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

An iOS framework for creating JSON-based models. Written in Swift.
An iOS framework for creating JSON-based models. Written in Swift.

An iOS framework for creating JSON-based models. Written in Swift (because it totally rules!) Requirements iOS 8.0+ Xcode 7.3 Swift 2.2 Installation E

A JSON parser with concise API written in Swift.
A JSON parser with concise API written in Swift.

A JSON parser with concise API written in Swift Maps JSON attributes to different Swift types with just two methods: map and mapArrayOfObjects. The li

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.

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

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

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

Freddy - A reusable framework for parsing JSON in Swift.
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

[Deprecated] A shiny JSON parsing library in Swift :sparkles: Loved by many from 2015-2021
[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

Comments
  • allow JSEN to be initialized from a type-erased JSEN instance

    allow JSEN to be initialized from a type-erased JSEN instance

    Currently, JSEN(from: jsen) -> nil. This allows for JSEN(from: jsen) -> jsen. This is analogous to Int(42) -> int, not -> nil.

    This allows the initializer to accept a JSEN instance that's been type-erased. This'll come up if the JSEN value has been passed through some function that erases it to Any. In my case, this came up when I passed my "JSON object" [String: JSEN] into an adaptor function that took [String: Any].

    Thanks for considering!

    opened by loganmoseley 2
Releases(1.2.1)
  • 1.2.1(Jun 2, 2022)

    What's Changed

    • Fix Issue When Writing to Empty Dictionary by @rogerluan in https://github.com/rogerluan/JSEN/pull/4

    Full Changelog: https://github.com/rogerluan/JSEN/compare/1.2.0...1.2.1

    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(May 28, 2022)

    What's Changed

    • Add appending function to KeyPath using the "+" operator by @rogerluan in https://github.com/rogerluan/JSEN/pull/3

    Full Changelog: https://github.com/rogerluan/JSEN/compare/1.1.3...1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.1.3(May 28, 2022)

  • 1.1.2(Feb 9, 2022)

    What's Changed

    • allow JSEN to be initialized from a type-erased JSEN instance by @loganmoseley in https://github.com/rogerluan/JSEN/pull/2

    New Contributors

    • @loganmoseley made their first contribution in https://github.com/rogerluan/JSEN/pull/2

    Full Changelog: https://github.com/rogerluan/JSEN/compare/1.1.1...1.1.2

    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(Jun 13, 2021)

    The init method release in 1.1.0 was accidentally released as internal (default) instead of public, thus not being accessible from outside. This release changes that.

    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Jun 13, 2021)

  • 1.0.0(Jun 12, 2021)

Owner
Roger Oba
Lead Mobile Engineer @zillyinc 🏡 @apple Developer Academy scholarship student  Core contributor @fastlane 🚀 7+ years of mobile app development 👨‍💻
Roger Oba
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
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
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
JASON is a faster JSON deserializer written in Swift.

JASON is a faster JSON deserializer written in Swift. JASON is the best framework we found to manage JSON at Swapcard. This is by far the fastest and

Damien 1k Oct 15, 2022
ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects to and from JSON.

ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

Tristan Himmelman 9k Jan 2, 2023
A type-safe JSON-RPC 2.0 library purely written in Swift

JSONRPCKit JSONRPCKit is a type-safe JSON-RPC 2.0 library purely written in Swift. // Generating request JSON let batchFactory = BatchFactory(version:

Shinichiro Oba 178 Mar 18, 2022
An extremely simple JSON helper written in Swift.

Alexander Alexander is an extremely simple JSON helper written in Swift. It brings type safety and Foundation helpers to the cumbersome task of JSON u

HODINKEE 36 Sep 15, 2022