Skip to content

[4.2][JSONSerialization] Always use reference types to match Darwin behavior #1636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Foundation/JSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ open class JSONDecoder {
/// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not valid JSON.
/// - throws: An error if any value throws an error during decoding.
open func decode<T : Decodable>(_ type: T.Type, from data: Data) throws -> T {
let topLevel = try JSONSerialization.jsonObject(with: data, options: [.useReferenceNumericTypes])
let topLevel = try JSONSerialization.jsonObject(with: data, options: [])
let decoder = _JSONDecoder(referencing: topLevel, options: self.options)
return try T(from: decoder)
}
Expand Down
39 changes: 10 additions & 29 deletions Foundation/JSONSerialization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@ import CoreFoundation

extension JSONSerialization {
public struct ReadingOptions : OptionSet {
public let rawValue : UInt
public let rawValue: UInt
public init(rawValue: UInt) { self.rawValue = rawValue }

public static let mutableContainers = ReadingOptions(rawValue: 1 << 0)
public static let mutableLeaves = ReadingOptions(rawValue: 1 << 1)
public static let allowFragments = ReadingOptions(rawValue: 1 << 2)
internal static let useReferenceNumericTypes = ReadingOptions(rawValue: 1 << 15)
}

public struct WritingOptions : OptionSet {
public let rawValue : UInt
public let rawValue: UInt
public init(rawValue: UInt) { self.rawValue = rawValue }

public static let prettyPrinted = WritingOptions(rawValue: 1 << 0)
Expand Down Expand Up @@ -806,6 +805,7 @@ private struct JSONReader {
|| (byte >= 0x41 && byte <= 0x46)
|| (byte >= 0x61 && byte <= 0x66)
}

func parseCodeUnit(_ input: Index) -> (UTF16.CodeUnit, Index)? {
let hexParser = takeMatching(isHexChr)
guard let (result, index) = hexParser([], input).flatMap(hexParser).flatMap(hexParser).flatMap(hexParser),
Expand All @@ -820,6 +820,7 @@ private struct JSONReader {
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, // 0...9
0x2E, 0x2D, 0x2B, 0x45, 0x65, // . - + E e
]

func parseNumber(_ input: Index, options opt: JSONSerialization.ReadingOptions) throws -> (Any, Index)? {
func parseTypedNumber(_ address: UnsafePointer<UInt8>, count: Int) -> (Any, IndexDistance)? {
let temp_buffer_size = 64
Expand All @@ -837,32 +838,15 @@ private struct JSONReader {
let doubleResult = strtod(startPointer, doubleEndPointer)
let doubleDistance = startPointer.distance(to: doubleEndPointer[0]!)

guard intDistance > 0 || doubleDistance > 0 else {
return nil
}

let shouldUseReferenceType = opt.contains(.useReferenceNumericTypes)

guard doubleDistance > 0 else { return nil }
if intDistance == doubleDistance {
return (shouldUseReferenceType ? NSNumber(value: intResult) : intResult,
intDistance)
return (NSNumber(value: intResult), intDistance)
}
guard doubleDistance > 0 else {
return nil
}

if doubleResult == doubleResult.rounded() {
return (shouldUseReferenceType ? NSNumber(value: Int(doubleResult)) : Int(doubleResult),
doubleDistance)
}

return (shouldUseReferenceType ? NSNumber(value: doubleResult) : doubleResult,
doubleDistance)
return (NSNumber(value: doubleResult), doubleDistance)
}
}

if source.encoding == String.Encoding.utf8 {

if source.encoding == .utf8 {
return parseTypedNumber(source.buffer.baseAddress!.advanced(by: input), count: source.buffer.count - input).map { return ($0.0, input + $0.1) }
}
else {
Expand All @@ -872,7 +856,6 @@ private struct JSONReader {
numberCharacters.append(ascii)
index = nextIndex
}

numberCharacters.append(0)

return numberCharacters.withUnsafeBufferPointer {
Expand All @@ -887,12 +870,10 @@ private struct JSONReader {
return (value, parser)
}
else if let parser = try consumeASCIISequence("true", input: input) {
let result: Any = opt.contains(.useReferenceNumericTypes) ? NSNumber(value: true) : true
return (result, parser)
return (NSNumber(value: true), parser)
}
else if let parser = try consumeASCIISequence("false", input: input) {
let result: Any = opt.contains(.useReferenceNumericTypes) ? NSNumber(value: false) : false
return (result, parser)
return (NSNumber(value: false), parser)
}
else if let parser = try consumeASCIISequence("null", input: input) {
return (NSNull(), parser)
Expand Down
30 changes: 15 additions & 15 deletions TestFoundation/TestJSONSerialization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//

// Exposing internal ReadingOptions for tests.
extension JSONSerialization.ReadingOptions {
fileprivate static let useReferenceNumericTypes = JSONSerialization.ReadingOptions(rawValue: 1 << 15)
}

class TestJSONSerialization : XCTestCase {

let supportedEncodings: [String.Encoding] = [
Expand Down Expand Up @@ -477,7 +472,7 @@ extension TestJSONSerialization {
XCTFail("Unable to convert string to data")
return
}
let result = try getjsonObjectResult(data, objectType, options: [.useReferenceNumericTypes]) as? [Any]
let result = try getjsonObjectResult(data, objectType) as? [Any]
XCTAssertEqual(result?[0] as? NSNumber, true)
XCTAssertEqual(result?[1] as? NSNumber, false)
XCTAssertEqual(result?[2] as? String, "hello")
Expand All @@ -492,7 +487,7 @@ extension TestJSONSerialization {

//MARK: - Number parsing
func deserialize_numbers(objectType: ObjectType) {
let subject = "[1, -1, 1.3, -1.3, 1e3, 1E-3]"
let subject = "[1, -1, 1.3, -1.3, 1e3, 1E-3, 10]"

do {
for encoding in supportedEncodings {
Expand All @@ -501,33 +496,38 @@ extension TestJSONSerialization {
return
}
let result = try getjsonObjectResult(data, objectType) as? [Any]
XCTAssertEqual(result?[0] as? Int, 1)
XCTAssertEqual(result?[1] as? Int, -1)
XCTAssertEqual(result?[2] as? Double, 1.3)
XCTAssertEqual(result?[3] as? Double, -1.3)
XCTAssertEqual(result?[4] as? Int, 1000)
XCTAssertEqual(result?[0] as? Int, 1)
XCTAssertEqual(result?[1] as? Int, -1)
XCTAssertEqual(result?[2] as? Double, 1.3)
XCTAssertEqual(result?[3] as? Double, -1.3)
XCTAssertEqual(result?[4] as? Int, 1000)
XCTAssertEqual(result?[5] as? Double, 0.001)
XCTAssertEqual(result?[6] as? Int, 10)
XCTAssertEqual(result?[6] as? Double, 10.0)
}
} catch {
XCTFail("Unexpected error: \(error)")
}
}

func deserialize_numbers_as_reference_types(objectType: ObjectType) {
let subject = "[1, -1, 1.3, -1.3, 1e3, 1E-3]"
let subject = "[1, -1, 1.3, -1.3, 1e3, 1E-3, 10]"

do {
for encoding in supportedEncodings {
guard let data = subject.data(using: encoding) else {
XCTFail("Unable to convert string to data")
return
}
let result = try getjsonObjectResult(data, objectType, options: [.useReferenceNumericTypes]) as? [Any]
let result = try getjsonObjectResult(data, objectType) as? [Any]
XCTAssertEqual(result?[0] as? NSNumber, 1)
XCTAssertEqual(result?[1] as? NSNumber, -1)
XCTAssertEqual(result?[2] as? NSNumber, 1.3)
XCTAssertEqual(result?[3] as? NSNumber, -1.3)
XCTAssertEqual(result?[4] as? NSNumber, 1000)
XCTAssertEqual(result?[5] as? NSNumber, 0.001)
XCTAssertEqual(result?[6] as? NSNumber, 10)
XCTAssertEqual(result?[6] as? NSNumber, 10.0)
}
} catch {
XCTFail("Unexpected error: \(error)")
Expand Down Expand Up @@ -1449,7 +1449,7 @@ extension TestJSONSerialization {
}
do {
let data = decimalArray.data(using: String.Encoding.utf8)
let result = try JSONSerialization.jsonObject(with: data!, options: [.useReferenceNumericTypes]) as? [Any]
let result = try JSONSerialization.jsonObject(with: data!, options: []) as? [Any]
XCTAssertEqual(result?[0] as! NSNumber, 12.1)
XCTAssertEqual(result?[1] as! NSNumber, 10)
XCTAssertEqual(result?[2] as! NSNumber, 0)
Expand Down