Flexible JSON traversal for rapid prototyping

Overview

RBBJSON

Swift Version: 5.2 Swift Package Manager Swift Package Manager Twitter: @DLX

RBBJSON enables flexible JSON traversal at runtime and JSONPath-like querying for rapid prototyping.

Use JSONDecoder to create an RBBJSON struct, then traverse it using dynamic member lookup:

let json = try JSONDecoder().decode(RBBJSON.self, from: data)

json.firstName         // RBBJSON.string("John")
json.lastName          // RBBJSON.string("Appleseed")
json.age               // RBBJSON.number(26)
json.invalidKey        // RBBJSON.null
json.phoneNumbers[0]   // RBBJSON.string("+14086065775")

If you want to access a value that coincides with a Swift-defined property, use a String subscript instead:

json.office.map     // Error: Maps to Sequence.map
json.office["map"]  // RBBJSON.string("https://maps.apple.com/?q=IL1")

To unbox a JSON value, use one of the failable initializers:

String(json.firstName) // "John"
String(json.lastName)  // "Appleseed"
String(json.age)       // nil

Int(json.age)          // 26
Double(json.age)       // 26.0

You can also make use of a JSONPath-inspired Query syntax to find nested data inside a JSON structure.

For example, given:

{ 
  "store": {
    "book": [ 
      { 
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { 
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { 
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { 
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}
JSONPath RBBJSON Result
$.store.book[*].author json.store.book[any: .child].author The authors of all books in the store.
$..author json[any: .descendantOrSelf].author All authors.
$.store.* json.store[any: .child] All things in the store, a list of books an a red bycicle.
$.store..price json.store[any: .descendantOrSelf].price All prices in the store.
$..book[2] json[any: .descendantOrSelf].book[2] The second book.
$..book[-2] json[any: .descendantOrSelf].book[-2] The second-to-last book.
$..book[0,1], $..book[:2] json[any: .descendantOrSelf].book[0, 1]), json[any: .descendantOrSelf].book[0...1]), json[any: .descendantOrSelf].book[0..<2]) The first two books.
$..book[?(@.isbn)] json[any: .descendantOrSelf].book[has: \.isbn] All books with an ISBN number.
$..book[?(@.price<10)] json.store.book[matches: { $0.price <= 10 }] All books cheaper than 10.
$.store["book", "bicycle"]..["price", "author"] json.store["book", "bicycle"][any: .descendantOrSelf]["price", "author"] The author (where available) and price of every book or bicycle.

Once you query a JSON value using one of the higher order selectors, the resulting type of the expression will be a lazy RBBJSONQuery:

json.store.book[0]["title"]     // RBBJSON.string("Sayings of the Century")
json.store.book[0, 1]["title"]  // some RBBJSONQuery

Because RBBJSONQuery conforms to Sequence, you can initialize an Array with it to obtain the results or use e.g. compactMap:

String(json.store.book[0].title)                    // "Sayings of the Century"
json.store.book[0, 1].title.compactMap(String.init) // ["Sayings of the Century", "Sword of Honour"]

String(json.store.book[0]["invalid Property"])                    // nil
json.store.book[0, 1]["invalid Property"].compactMap(String.init) // []

You might also like...
Argo is a library that lets you extract models from JSON or similar structures in a way that's concise, type-safe, and easy to extend
Argo is a library that lets you extract models from JSON or similar structures in a way that's concise, type-safe, and easy to extend

Argo is a library that lets you extract models from JSON or similar structures in a way that's concise, type-safe, and easy to extend. Using Argo

Arrow  🏹 Parse JSON with style
Arrow 🏹 Parse JSON with style

Arrow 🏹 Parse JSON with style

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

Reflection based (Dictionary, CKRecord, NSManagedObject, Realm, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift

EVReflection General information At this moment the master branch is tested with Swift 4.2 and 5.0 beta If you want to continue using EVReflection in

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

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

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

JASON is a faster JSON deserializer written in Swift.
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

Comments
  • Linux support

    Linux support

    Hi, hope you're accepting PRs.

    • added conditional import for CoreGraphics
    • added conditional test using CGFloat
    • changed linux tests run based on https://oleb.net/2020/swift-test-discovery/
    opened by jkrukowski 1
  • Use opaque RBBJSONQuery types

    Use opaque RBBJSONQuery types

    This simplifies the logic of what used to be the SearchIterator by spreading it across dedicated Sequence types per matcher, each hidden behind an opaque RBBJSONQuery protocol (protocols cannot be nested under RBBJSON unfortunately).

    opened by robb 0
  • Add ability to query multiple keys at once

    Add ability to query multiple keys at once

    Adds the ability to query multiple keys at once, e.g. json.store["book", "bicycle"][any: .descendantOrSelf]["price", "author"] results in

    [
        [
            "price": 19.95,
        ],
        [
            "price": 8.95,
            "author": "Nigel Rees"
        ],
        [
            "price": 12.99,
            "author": "Evelyn Waugh"
        ],
        [
            "price": 8.99,
            "author": "Herman Melville"
        ],
        [
            "price": 22.99,
            "author": "J. R. R. Tolkien"
        ],
    ]
    

    Note that there are slightly different semantics for a single key. For a single key, values are flattened into an array whereas for multiple keys, an array of objects with the given keys (where available) is returned. While slightly inconsistent, I think it's closer to what you want and only matters for trailing selectors.

    E.g. json.store["book", "bicycle"][any: .descendantOrSelf]["price"] results in

    [19.95, 8.95, 12.99, 8.99, 22.99]
    

    but if a single value object is required, you could always use a second bogus key to get single value objects json.store["book", "bicycle"][any: .descendantOrSelf]["price", "<INVALID>"] as null values are stripped.

    opened by robb 0
Owner
Robb Böhnke
It's dangerous to go alone,​ take this: 🖤
Robb Böhnke
Magical Data Modeling Framework for JSON - allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS and tvOS apps.

JSONModel - Magical Data Modeling Framework for JSON JSONModel allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS

JSONModel 6.8k Nov 19, 2021
Mappable - Flexible JSON to Model converter, specially optimized for immutable properties

Mappable is a lightweight, flexible, easy-to-use framework to convert JSON to model, specially optimized for immutable property initializatio

Leave 27 Aug 26, 2022
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
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
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-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
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
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
Functional JSON Parser - Linux Ready

Functional JSON Parser Feature Linux Ready Type-safe JSON parsing Functional value transformation Easy to parse nested value Dependency free No define

Ryo Aoyama 117 Sep 9, 2022