Giter Club home page Giter Club logo

jay's Introduction

Jay

Build Status Latest Release Platforms Package Managers

Pure-Swift JSON parser & formatter. Fully streamable input and output. Linux & OS X ready. Replacement for NSJSONSerialization.

Jay conforms to the following specifications:

For extra convenience functions when working with the JSON enum, check out Jay-Extras.

โ“ Why?

We all use JSON. Especially when writing server-side Swift that needs to run on Linux. #0dependencies

This is my take on how a JSON parser should work. This is not another JSON mapping utility library. This is an actual JSON parser and formatter. Check out the code, it was fun to write ๐Ÿ˜‡

Features

  • Parsing: data -> JSON object
  • Formatting: JSON object -> data
  • Pretty printing
  • Streaming input and output, low memory footprint

Usage

Parsing from data (deserialization)

do {
	//get data from disk/network
	let data: [UInt8] = ...

	//ask Jay to parse your data
	let json = try Jay().jsonFromData(data) // JSON
	//or
	let json = try Jay().anyJsonFromData(data) // [String: Any] or [Any]

	//if it doesn't throw an error, all went well
	if let tasks = json.dictionary?["today"]?.array {
	    //you have a dictionary root object, with an array under the key "today"
	    print(tasks) //["laundry", "cook dinner for gf"]
	} 
} catch {
	print("Parsing error: \(error)")
}

Formatting into data (serialization)

do {
	//get a json object (works for both [String: Any] and typesafe versions - JSON)

	//ask Jay to generate data
	let anyContainer = ... // [String: Any] or [Any]
	let data = try Jay(formatting: .prettified).dataFromJson(any: json) // [UInt8]
	//or
	let json: JSON = ... // JSON
	let data = try Jay(formatting: .prettified).dataFromJson(json: json) // [UInt8]

	//send data over network, save to disk
} catch {
	print("Formatting error: \(error)")
}

Installation

Swift Package Manager

.Package(url: "https://github.com/czechboy0/Jay.git", majorVersion: 1)

๐Ÿ’™ Code of Conduct

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

๐Ÿ’ Contributing

Please create an issue with a description of your problem or open a pull request with a fix.

โœŒ๏ธ License

MIT

๐Ÿ‘ฝ Author

Honza Dvorsky - http://honzadvorsky.com, @czechboy0

jay's People

Contributors

basthomas avatar czechboy0 avatar endocrimes avatar krausefx avatar loganwright avatar remko avatar rothomp3 avatar tanner0101 avatar vzsg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jay's Issues

UTF-16 unsupported

When calling jsonFromData with an array of UTF-16-encoded bytes, Jay throws unimplemented.

The RFC recommends to determine the encoding by looking at the initial bytes (section 3).

I don't really want UTF-16, but if the API offers (only) a raw bytes interface, I would expect it to take any encoding like JSONSerialization does.

dataFromJson() Int 0,1 exchange Bool

Use Vapor 1.0.3 contains Jay. Xcode 8.0.

.Package(url: "https://github.com/DanToml/Jay.git", majorVersion: 1)

func dictionaryToJayType(_ maybeDictionary: Any) throws -> JSON? {

    let mirror = Mirror(reflecting: maybeDictionary)
    let childrenValues = mirror.children.map { $0.value }

    var obj = [String: JSON]()
    for i in childrenValues {

        let childMirror = Mirror(reflecting: i)
        let children = childMirror.children
        if childMirror.displayStyle == .tuple && children.count == 2 {
            let it = children.makeIterator()
            let maybeKey = it.next()!.value
            guard let key = maybeKey as? String else {
                throw JayError.keyIsNotString(maybeKey)
            }
            let value = it.next()!.value
            obj[key] = try self.toJayType(value) // Int 1 -> .boolean(true)
        } else {
            throw JayError.unsupportedType(childMirror)
        }
    }
    return .object(obj)
}

Can't parse exponents that starts with zero

The API that I consume (sinatra app) has numbers with exponent like this one: 6.8e-05. I don't think this is required by the current RFC4627 spec but I think this will make Jay (and Vapor) more compatible with other API services in the wild.

// this will fail
let data = "{ \"number\" : 6.8e-05 }".chars()
_ = try Jay().jsonFromData(data)

I can confirm that JS console and JSONSerialization support it.

Cannot parse JSON with integer values bigger than Int.max

I cannot parse a JSON object which has an integer value bigger than Int.max.

The error happens when I call try Jay(parsing: parsing).jsonFromData(serialized) where serialized is an array of UInt8 and represents a valid JSON object. Jay is failing in line 156 of NumberParser.swift:

//look for other number-allowed chars in this context
//although int-section terminating
if intTerm.contains(reader.curr()) {
    //gracefully end this section
    let intString = try digs.string()
    let int = Int(intString)! // FAILING LINE. intString = "9223372036854776000"    
    return int
}

The error is fatal error: unexpectedly found nil while unwrapping an Optional value. The stack trace looks like:

Current stack trace:
0    libswiftCore.dylib                 0x0000000100f03580 swift_reportError + 132
1    libswiftCore.dylib                 0x0000000100f20850 _swift_stdlib_reportFatalError + 61
2    libswiftCore.dylib                 0x0000000100d1c550 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355
3    libswiftCore.dylib                 0x0000000100e94600 partial apply for (_fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never).(closure #2) + 109
4    libswiftCore.dylib                 0x0000000100d1c550 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355
5    libswiftCore.dylib                 0x0000000100e4f970 specialized _fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never + 96
6    Jay                                0x000000010081b170 NumberParser.parseInt<A where ...> (A) throws -> Int + 2140
7    Jay                                0x0000000100819f10 NumberParser.parse<A where ...> (with : A) throws -> JSON + 898
8    Jay                                0x0000000100827030 ValueParser.parse<A where ...> (with : A) throws -> JSON + 1653
9    Jay                                0x000000010081d270 ObjectParser.parse<A where ...> (with : A) throws -> JSON + 2269
10   Jay                                0x0000000100827030 ValueParser.parse<A where ...> (with : A) throws -> JSON + 587
11   Jay                                0x000000010081d270 ObjectParser.parse<A where ...> (with : A) throws -> JSON + 2269
12   Jay                                0x0000000100827030 ValueParser.parse<A where ...> (with : A) throws -> JSON + 587
13   Jay                                0x000000010081d270 ObjectParser.parse<A where ...> (with : A) throws -> JSON + 2269
14   Jay                                0x0000000100827030 ValueParser.parse<A where ...> (with : A) throws -> JSON + 587
15   Jay                                0x000000010081d270 ObjectParser.parse<A where ...> (with : A) throws -> JSON + 2269
16   Jay                                0x0000000100827030 ValueParser.parse<A where ...> (with : A) throws -> JSON + 587
17   Jay                                0x00000001007fdf10 ArrayParser.parse<A where ...> (with : A) throws -> JSON + 766
18   Jay                                0x0000000100827030 ValueParser.parse<A where ...> (with : A) throws -> JSON + 1112
19   Jay                                0x000000010081d270 ObjectParser.parse<A where ...> (with : A) throws -> JSON + 2269
20   Jay                                0x0000000100822be0 RootParser.parse<A where ...> (with : A) throws -> JSON + 362
21   Jay                                0x0000000100820920 Parser.parseJsonFromReader<A where ...> (A) throws -> JSON + 132
22   Jay                                0x0000000100820d80 Parser.parseJsonFromData([UInt8]) throws -> JSON + 118
23   Jay                                0x0000000100811950 Jay.jsonFromData([UInt8]) throws -> JSON + 95

I'm using Jay version 1.0.0.

Incorrect String->Int map detection

When I run the following code:

let value = ["foo": 42]
print(value)
if let data = try? Jay().dataFromJson(any: value) {
print(String(bytes: data, encoding: String.Encoding.utf8))
}

it outputs

["foo": 42]
Optional("{\"foo\":true}")

Is it expected that Jay interprets the number as a boolean?

Add header documentation

The public surface area is pretty small, but it's not well documented, would be nice to make its use as seamless as possible

Incorrect parsing of Double values if the fraction part starts with zeroes

Consider the following JSON with three double values: [0.608, 0.0608, 0.00608]. When parsed with Jay, the resulting array contains 0.608 three times, so the scale is completely lost.

In other words:

import Jay

let testData = Array("[0.608, 0.0608, 0.00608]".utf8)

let jay = Jay()
let json = try jay.anyJsonFromData(testData)
print(json) // [0.60799999999999998, 0.60799999999999998, 0.60799999999999998]

After some investigation, the culprit is NumberParser.swift:202, where the collected fractional digits are naively converted to an integer. For example when parsing the third number, the digs array ["0", "0", "6", "0", "8"] becomes 608, losing the two initial zeroes.

Tagging a new version

With #53 and #59 closed, could you tag a new point release so SPM can pick up the fixes, or is there anything else you consider to be blocking it?

Performance comparisons?

I'm curious how parsing performance compares to other JSON parsers like

  • NSJSONSerialization
  • yajl
  • jsonsl

It would be nice to see some timing results on realistic JSON documents.

Offer a String API

Am I correct that the only JSON serialization format supported right now is raw data? I'm guessing this is because this is what JSONSerialization offers?

In practice, I find it annoying to have to work with raw data, as I always work with Strings when using JSON, and let later stages care about which encoding to put it on the wire. As far as I understand, this means I have to always run this through String:validatingUTF8 (and know that the raw data Jay returns is UTF-8, which is undocumented at the moment), and force-unpack the optional (which I can be 100% sure is valid utf-8 anyway); vice versa, it means I have to call .utf8 every time I want to parse something with Jay (which is slightly less annoying than the former).

Wouldn't it be good to offer an API that parses and serializes to Strings?

Also, the fact that the API only takes raw bytes means you have to document which encoding it is in and which encoding it outputs. JSONSerialization takes any encoding, Jay only seems to take UTF-8, (see #54 ).

Also escape other whitespace control characters

Currently we don't escape all characters in the 0x00 to 0x1F range, as the spec describes:

All Unicode characters may be placed within the
quotation marks except for the characters that must be escaped:
quotation mark, reverse solidus, and the control characters (U+0000
through U+001F).

We should escape them all, to properly conform.

Purpose of Jay struct

It seems that the Jay struct should have static functions instead of member functions, since the member functions only create instances of NativeParser and NativeTypeConverter.

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.