Skip to content

Commit

Permalink
Add convenience method/properties for stream decoding
Browse files Browse the repository at this point in the history
Add a method `values()` on `JSONStreamDecoder` that eagerly parses all
values and returns it as a `[JSON]`. Also add properties `.json` and
`.error` to `JSONStreamValue`.
  • Loading branch information
lilyball committed Oct 20, 2016
1 parent bd1765d commit f8ce5d0
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 14 deletions.
27 changes: 27 additions & 0 deletions Sources/Decoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,17 @@ public struct JSONStreamDecoder<Seq: Sequence>: Sequence, IteratorProtocol where
decoder.streaming = true
}

/// Returns an array of all decoded values, or throws an error if one occurs.
///
/// This eagerly decodes the rest of the JSON stream and returns all values. If a parse error occurs at any point,
/// the error is thrown and all values are discarded.
///
/// - Returns: An array of `JSON` values.
/// - Throws: `JSONParserError`.
public func values() throws -> [JSON] {
return try map({ try $0.unwrap() })
}

public func makeIterator() -> JSONStreamDecoder<Seq> {
return self
}
Expand All @@ -247,6 +258,22 @@ public enum JSONStreamValue: Equatable {
case json(JSON)
case error(JSONParserError)

/// Returns the contained `JSON` value, otherwise `nil`.
public var json: JSON? {
switch self {
case .json(let json): return json
case .error: return nil
}
}

/// Returns the contained error, otherwise `nil`.
public var error: JSONParserError? {
switch self {
case .json: return nil
case .error(let error): return error
}
}

/// Unwraps the contained `JSON` value or throws the contained error.
///
/// - Throws: `JSONParserError`.
Expand Down
47 changes: 33 additions & 14 deletions Tests/JSONDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,23 +339,42 @@ class JSONStreamDecoderTests: XCTestCase {

func testStreamingDecoder() {
func assertMatchesValues(_ input: String, _ expected: [JSONStreamValue], file: StaticString = #file, line: UInt = #line) {
for (i, (value, expected)) in zip(JSON.decodeStream(input), expected).enumerated() {
switch (value, expected) {
case let (.json(value), .json(expected)):
if !matchesJSON(value, expected) {
XCTFail("value \(i+1) - (\(value)) does not equal \(expected)", file: file, line: line)
}
case let (.error(error), .error(expected)):
if error != expected {
XCTFail("error \(i+1) - (\(error)) does not equal \(expected)", file: file, line: line)
}
case let (.json(value), .error(expected)):
XCTFail("value \(i+1) - expected error \(expected), found value \(value)", file: file, line: line)
case let (.error(error), .json(expected)):
XCTFail("value \(i+1) - expected value \(expected), found error \(error)", file: file, line: line)
}
}
// Also check the behavior of values()
do {
for (i, (value, expected)) in zip(JSON.decodeStream(input), expected).enumerated() {
switch (value, expected) {
case let (.json(value), .json(expected)):
if !matchesJSON(value, expected) {
XCTFail("value \(i+1) - (\(value)) does not equal \(expected)", file: file, line: line)
}
case let (.error(error), .error(expected)):
if error != expected {
XCTFail("error \(i+1) - (\(error)) does not equal \(expected)", file: file, line: line)
}
case let (.json(value), .error(expected)):
XCTFail("value \(i+1) - expected error \(expected), found value \(value)")
case let (.error(error), .json(expected)):
XCTFail("value \(i+1) - expected value \(expected), found error \(error)", file: file, line: line)
let expectedValues = try expected.map({ try $0.unwrap() })
do {
let values = try JSON.decodeStream(input).values()
XCTAssertEqual(values, expectedValues, file: file, line: line)
} catch {
XCTFail("unexpected error found decoding JSON stream - \(error)", file: file, line: line)
}
} catch let expectedError as JSONParserError {
XCTAssertThrowsError(try JSON.decodeStream(input).values()) { (error) in
switch error {
case let error as JSONParserError:
XCTAssertEqual(error, expectedError, file: file, line: line)
default:
XCTFail("expected \(expectedError), found \(error)", file: file, line: line)
}
}
} catch {
XCTFail(file: file, line: line) // unreachable
}
}
func assertMatchesEvents(_ input: String, _ expected: [JSON], file: StaticString = #file, line: UInt = #line) {
Expand Down

0 comments on commit f8ce5d0

Please sign in to comment.