Ss-json - High-performance json parsing in swift

Overview

json
0.1.1

swift-json is a pure-Swift JSON parsing library designed for high-performance, high-throughput server-side applications. When compared using the test data captured.json, swift-json is nearly 7 times faster than Foundation.JSONDecoder (see benchmark source code).

example usage

The JSON module in swift-json enables you to express JSON parsing tasks as constructive parsers. This makes the JSON module very flexible without requiring much configuration from users who simply want to parse a JSON message from a remote peer.

To parse a complete JSON message, use the JSON.Rule<Location>.Root parsing rule:

import JSON 

@main 
enum Main 
{
    struct Decimal:Codable  
    {
        let units:Int 
        let places:Int 
    }
    struct Response:Codable 
    {
        let success:Bool 
        let value:Decimal
    }
    static 
    func main() throws
    {
        let string:String = 
        """
        {"success":true,"value":0.1}
        """
        let decoder:JSON        = try Grammar.parse(string.utf8, 
            as: JSON.Rule<String.Index>.Root.self)
        let response:Response   = try .init(from: decoder)
        
        print(response)
        
        let invalid:String = 
        """
        {"success":true,value:0.1}
        """
        do 
        {
            let _:JSON = try Grammar.parse(diagnosing: invalid.utf8, 
                as: JSON.Rule<String.Index>.Root.self)
        }
        catch let error as ParsingError<String.Index> 
        {
            let debug:String = error.annotate(source: invalid, 
                line: String.init(_:), newline: \.isNewline)
            print(debug)
        }
    }
}
$ .build/release/examples
Response(success: true, value: 
    JSONExamples.Main.Decimal(units: 1, places: 1))

Grammar.Expected<Grammar.Encoding.ASCII.Quote>: 
    expected construction by rule 'Quote'
{"success":true,value:0.1}
                ^
note: expected pattern 'Grammar.Encoding.ASCII.Quote'
{"success":true,value:0.1}
                ^
note: while parsing value of type 'String' by rule 
    'JSON.Rule.StringLiteral'
{"success":true,value:0.1}
                ^
note: while parsing value of type '((), (key: String, value: JSON))' 
    by rule '(Grammar.Pad<Grammar.Encoding.ASCII.Comma, 
    JSON.Rule.Whitespace>, JSON.Rule.Object.Item)'
{"success":true,value:0.1}
               ^~
note: while parsing value of type '[String: JSON]' by rule 
    'JSON.Rule.Object'
{"success":true,value:0.1}
^~~~~~~~~~~~~~~~~
note: while parsing value of type 'JSON' by rule 'JSON.Rule.Root'
{"success":true,value:0.1}
^~~~~~~~~~~~~~~~~

The JSON module supports parsing JSON fragments using the JSON.Rule<Location>.Value rule.

The nature of constructive parsing also means it is straightforward to parse multiple concatenated JSON messages, as is commonly encountered when interfacing with streaming JSON APIs.

adding swift-json as a dependency

To use swift-json in a project, add the following to your Package.swift file:

let package = Package(
    ...
    dependencies: 
    [
        // other dependencies
        .package(name: "swift-json", 
            url: "https://github.com/kelvin13/ss-json", from: "0.1.1"),
    ],
    targets: 
    [
        .target(name: "example", 
            dependencies: 
            [
                // other dependencies
                .product(name: "JSON", package: "swift-json"),
            ]),
        // other targets
    ]
)
Comments
  • Is there a way to use this to decode JSON lines?

    Is there a way to use this to decode JSON lines?

    I have a JSON line file and I'm trying to parse it, I'm wondering if this can be used?

    Currently swift-json is ~4 times slower than the Foundation implementation, but I suspect this is because I am recreating the decoder each time:

    swift-json (~97 seconds)

    let (bytes, urlResponse) = try! await URLSession.shared.bytes(from: file)
    var logs: [Log] = []
    do {
        let startTime = CFAbsoluteTimeGetCurrent()
        for try await line in bytes.lines {
            let trimmed = line.trimmingCharacters(in: .whitespaces)
            if !trimmed.isEmpty {
                let decoder: JSON = try Grammar.parse(trimmed.utf8, as: JSON.Rule<String.Index>.Root.self)
                let log: Log = try .init(from: decoder)
                logs.append(log)
            }
        }
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        print("Time elapsed: \(timeElapsed) s.")
    } catch {
        print("Reading file failed with error \(error)")
    }
    

    Foundation (~24 seconds)

    let decoder: JSONDecoder = JSONDecoder()
    let (bytes, urlResponse) = try! await URLSession.shared.bytes(from: file)
    var logs: [Log] = []
    do {
        let startTime = CFAbsoluteTimeGetCurrent()
        for try await line in bytes.lines {
            let data = line.trimmingCharacters(in: .whitespaces).data(using: .utf8) ?? Data()
            if !data.isEmpty {
                let log: Log = try! decoder.decode(Log.self, from: data)
                logs.append(log)
            }
        }
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        print("Time elapsed: \(timeElapsed) s.")
    } catch {
        print("Reading file failed with error \(error)")
    }
    

    Tested on a ~100MB file with 140000 lines

    needs-repro 
    opened by rj93 10
  • rename JSON.lint(_:) to JSON.lint(whitelisting:)

    rename JSON.lint(_:) to JSON.lint(whitelisting:)

    the existing API isn’t very clear about whether the passed keys are explicitly allowed to appear, or explicitly prohibited from appearing. we should give it an argument label, like whitelisting:

    enhancement 
    opened by kelvin13 2
  • add a “one-step” decode API

    add a “one-step” decode API

    feedback on current API is that is is too complex and expects users to interact with swift-grammar types. this API exists to support high-performance use-cases, but many other use cases just want to parse a String and dispatch to Decodable.

    in particular, we should have

    let _:SomeDecodable = try .init(from: try JSON.init(parsing: #"{ "x": 1 }"#))
    
    enhancement 
    opened by kelvin13 1
  • GeneralDecoding.swift performance regression

    GeneralDecoding.swift performance regression

    benchmarks CI caught a major performance regression with commit 59d34c5f8c337b068d07ad5b0e4caa2fd0b8176e

    before:

     [15/16] Linking benchmark
    Build complete! (50.69s)
    swift-json: decoded 38295 messages
    foundation: decoded 38295 messages
    swift-json decoding time: 0.497709959 seconds
    foundation decoding time: 4.90599839 seconds
    

    after:

    [15/16] Linking benchmark
    Build complete! (45.21s)
    swift-json: decoded 38295 messages
    foundation: decoded 38295 messages
    swift-json decoding time: 3.21054002 seconds
    foundation decoding time: 4.539217732 seconds
    
    bug 
    opened by kelvin13 1
  • Unfail device cross compilation

    Unfail device cross compilation

    xcodebuild crashes if there exists a directory named _Snippets, and for some reason the SPM has not implemented snippetsDirectory, so we must disable snippets for now.

    this means the snippets have lost CI coverage, so this is only a short-term solution.

    opened by kelvin13 0
  • stop reexporting Grammar

    stop reexporting Grammar

    the v0.3.0 API should greatly reduce the likelihood of users needing to interact with the Grammar module, so we should stop importing it with @_exported.

    enhancement 
    opened by kelvin13 0
  • 8/15/22 toolchain breakage

    8/15/22 toolchain breakage

    _move has broken again, due to compiler drift:

    error: Can not use feature when experimental move only is disabled! Pass the frontend flag -enable-experimental-move-only to swift to enable the usage of this language feature
                    return try body(_move value)
    

    we should reevaluate usage of _move in this API, since it likely serves no purpose.

    opened by kelvin13 0
  • Debugging trace improvements

    Debugging trace improvements

    adds API taking closure arguments to LintingDictionary and [JSON] that records debugging information if decoding fails, and removes unnecessary closure arguments elsewhere

    opened by kelvin13 0
  • Why a custom `Decimal` instead of using the built-in type?

    Why a custom `Decimal` instead of using the built-in type?

    struct Decimal:Codable  
    {
        let units:Int 
        let places:Int 
    }
    

    I'm trying to wrap my head around the need for having a user-defined Decimal type for decoding, as opposed to leveraging Swift's own Decimal type. It's rather odd to me, a user of the library, having introduce an arbitrary abstraction to be able to decode something into a basic numeric type.

    opened by davdroman 2
  • JSON5

    JSON5

    It would be great, if this library could eventually support JSON5, which comes with a bundle of amazing features.

    Foundation on macOS supports it by now (through .json5Allowed), but it looks like swift-corelibs-foundation doesn't (yet?).

    enhancement 
    opened by mickeyl 1
Releases(v0.3.0)
Owner
kelvin
kope with klossy
kelvin
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
Precise JSON decimal parsing for Swift 🧮

PreciseDecimal Introduction Swift has long suffered a problem with its Decimal type: unapparent loss of precision. This happens with all common ways o

David Roman 22 Dec 15, 2022
Developed with use Swift language. As a third party library used SDWebImage. JSON parsing using URLSession with TMDB API. This app provide by the Core Data structure.

Capstone Project ?? About Developed with use Swift language. As a third party library used SDWebImage. JSON parsing using URLSession with TMDB API. Ad

Ensar Batuhan Unverdi 9 Aug 22, 2022
Networking, JSON Parsing, APIs and Core Location

Clima Our Goal It’s time to take our app development skills to the next level. We’re going to introduce you to the wonderful world of Application Prog

Dessana Caldeira M. Santos 1 Nov 25, 2021
📱 A comprehensive test task for creating an autolayout interface, requesting an API and JSON parsing from Effective Mobile.

ECOMMERCE A comprehensive test task for creating an autolayout interface, requesting an API and JSON parsing from Effective Mobile. ??‍?? Design ✨ Fea

Daniel Tvorun 4 Nov 21, 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
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
A lightweight CSS parser for parsing and creating CSS stylesheets

SwiftCSSParser A lightweight CSS parser for Swift that uses cssparser (cpp) under the hood. Basic usage Here's a simple code snippet to get you starte

null 9 Jul 20, 2022
Runners High with swift

RunnersHigh 環境構築 HomeBrewなければ入れる BrewでRbEnvを入れる brew install rbenv rbenvでrubyのversion2.7.1をインストール rbenv install 2.7.1 rbenvでrubyのversionを指定 rbenv loca

null 0 Dec 5, 2021
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
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
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
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