Skip to content

Commit

Permalink
improve json decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
lovetodream committed Jul 9, 2024
1 parent d071935 commit 1e08ed7
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 74 deletions.
232 changes: 163 additions & 69 deletions Sources/OracleNIO/Data/OracleJSON.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,28 +175,16 @@ struct OracleKeyedDecodingContainer<Key: CodingKey>: KeyedDecodingContainerProto
return value
}

func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
let value = try self.getValue(forKey: key)
guard case .double(let value) = value else {
throw self.createTypeMismatchError(type: type, forKey: key, value: value)
}
return value
func decode(_: Double.Type, forKey key: Key) throws -> Double {
try self.decodeFloatingPointNumber(forKey: key)
}

func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
let value = try self.getValue(forKey: key)
guard case .float(let value) = value else {
throw self.createTypeMismatchError(type: type, forKey: key, value: value)
}
return value
func decode(_: Float.Type, forKey key: Key) throws -> Float {
try self.decodeFloatingPointNumber(forKey: key)
}

func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
let value = try self.getValue(forKey: key)
guard case .int(let value) = value else {
throw self.createTypeMismatchError(type: type, forKey: key, value: value)
}
return value
func decode(_: Int.Type, forKey key: Key) throws -> Int {
try self.decodeBinaryInteger(forKey: key)
}

func decode(_: Int8.Type, forKey key: Key) throws -> Int8 {
Expand Down Expand Up @@ -318,12 +306,56 @@ extension OracleKeyedDecodingContainer {
}

Check warning on line 306 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L300-L306

Added lines #L300 - L306 were not covered by tests

@inline(__always)
private func decodeBinaryInteger<T: BinaryInteger>(of type: T.Type = T.self, forKey key: Key) throws -> T {
private func decodeBinaryInteger<T: BinaryInteger>(forKey key: Key) throws -> T {
let value = try self.getValue(forKey: key)
guard case .int(let value) = value else {
throw self.createTypeMismatchError(type: type, forKey: key, value: value)
switch value {
case .int(let value):
return T(value)

Check warning on line 313 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L313

Added line #L313 was not covered by tests
case .float(let value):
guard let value = T(exactly: value) else {
throw DecodingError.dataCorruptedError(
forKey: key,
in: self,
debugDescription: "Number \(value) does not fit in \(T.self)."
)
}
return value

Check warning on line 322 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L315-L322

Added lines #L315 - L322 were not covered by tests
case .double(let value):
guard let value = T(exactly: value) else {
throw DecodingError.dataCorruptedError(
forKey: key,
in: self,
debugDescription: "Number \(value) does not fit in \(T.self)."
)

Check warning on line 329 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L325-L329

Added lines #L325 - L329 were not covered by tests
}
return value
default:
throw self.createTypeMismatchError(type: T.self, forKey: key, value: value)

Check warning on line 333 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L333

Added line #L333 was not covered by tests
}
}

@inline(__always)
private func decodeFloatingPointNumber<T: BinaryFloatingPoint>(forKey key: Key) throws -> T {
let value = try self.getValue(forKey: key)
let (float, original): (T?, any Numeric) = switch value {
case .int(let value):
(T(exactly: value), value)

Check warning on line 342 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L342

Added line #L342 was not covered by tests
case .double(let value):
(T(value), value)
case .float(let value):
(T(value), value)

Check warning on line 346 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L346

Added line #L346 was not covered by tests
default:
throw self.createTypeMismatchError(type: T.self, forKey: key, value: value)

Check warning on line 348 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L348

Added line #L348 was not covered by tests
}

guard let float else {
throw DecodingError.dataCorruptedError(
forKey: key,
in: self,
debugDescription: "Number \(original) does not fit in \(T.self)."
)

Check warning on line 356 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L352-L356

Added lines #L352 - L356 were not covered by tests
}
return T(value)
return float
}
}

Expand Down Expand Up @@ -360,25 +392,16 @@ struct OracleSingleValueDecodingContainer: SingleValueDecodingContainer {
return value
}

Check warning on line 393 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L388-L393

Added lines #L388 - L393 were not covered by tests

func decode(_ type: Double.Type) throws -> Double {
guard case .double(let value) = self.value else {
throw self.createTypeMismatchError(type: type, value: value)
}
return value
func decode(_: Double.Type) throws -> Double {
try self.decodeFloatingPointNumber()
}

Check warning on line 397 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L395-L397

Added lines #L395 - L397 were not covered by tests

func decode(_ type: Float.Type) throws -> Float {
guard case .float(let value) = self.value else {
throw self.createTypeMismatchError(type: type, value: value)
}
return value
func decode(_: Float.Type) throws -> Float {
try self.decodeFloatingPointNumber()
}

Check warning on line 401 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L399-L401

Added lines #L399 - L401 were not covered by tests

func decode(_ type: Int.Type) throws -> Int {
guard case .int(let value) = self.value else {
throw self.createTypeMismatchError(type: type, value: value)
}
return value
func decode(_: Int.Type) throws -> Int {
try self.decodeBinaryInteger()
}

func decode(_: Int8.Type) throws -> Int8 {
Expand Down Expand Up @@ -452,11 +475,51 @@ extension OracleSingleValueDecodingContainer {
}

Check warning on line 475 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L470-L475

Added lines #L470 - L475 were not covered by tests

@inline(__always)
private func decodeBinaryInteger<T: BinaryInteger>(of type: T.Type = T.self) throws -> T {
guard case .int(let value) = self.value else {
throw self.createTypeMismatchError(type: type, value: value)
private func decodeBinaryInteger<T: BinaryInteger>() throws -> T {
switch self.value {
case .int(let value):
return T(value)

Check warning on line 481 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L481

Added line #L481 was not covered by tests
case .float(let value):
guard let value = T(exactly: value) else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Number \(value) does not fit in \(T.self)."
)
}
return value

Check warning on line 489 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L483-L489

Added lines #L483 - L489 were not covered by tests
case .double(let value):
guard let value = T(exactly: value) else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Number \(value) does not fit in \(T.self)."
)

Check warning on line 495 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L492-L495

Added lines #L492 - L495 were not covered by tests
}
return value
default:
throw self.createTypeMismatchError(type: T.self, value: self.value)

Check warning on line 499 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L499

Added line #L499 was not covered by tests
}
}

@inline(__always)
private func decodeFloatingPointNumber<T: BinaryFloatingPoint>() throws -> T {
let (float, original): (T?, any Numeric) = switch self.value {
case .int(let value):
(T(exactly: value), value)
case .double(let value):
(T(value), value)
case .float(let value):
(T(value), value)
default:
throw self.createTypeMismatchError(type: T.self, value: value)
}

guard let float else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Number \(original) does not fit in \(T.self)."
)
}
return T(value)
return float
}

Check warning on line 523 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L504-L523

Added lines #L504 - L523 were not covered by tests
}

Expand Down Expand Up @@ -505,34 +568,16 @@ struct OracleUnkeyedDecodingContainer: UnkeyedDecodingContainer {
return value
}

Check warning on line 569 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L561-L569

Added lines #L561 - L569 were not covered by tests

mutating func decode(_ type: Double.Type) throws -> Double {
let value = try self.getNextValue(ofType: type)
guard case .double(let value) = value else {
throw self.createTypeMismatchError(type: type, value: value)
}

self.currentIndex += 1
return value
mutating func decode(_: Double.Type) throws -> Double {
try self.decodeFloatingPointNumber()
}

Check warning on line 573 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L571-L573

Added lines #L571 - L573 were not covered by tests

mutating func decode(_ type: Float.Type) throws -> Float {
let value = try self.getNextValue(ofType: type)
guard case .float(let value) = value else {
throw self.createTypeMismatchError(type: type, value: value)
}

self.currentIndex += 1
return value
mutating func decode(_: Float.Type) throws -> Float {
try self.decodeFloatingPointNumber()
}

Check warning on line 577 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L575-L577

Added lines #L575 - L577 were not covered by tests

mutating func decode(_ type: Int.Type) throws -> Int {
let value = try self.getNextValue(ofType: type)
guard case .int(let value) = value else {
throw self.createTypeMismatchError(type: type, value: value)
}

self.currentIndex += 1
return value
mutating func decode(_: Int.Type) throws -> Int {
try self.decodeBinaryInteger()
}

Check warning on line 581 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L579-L581

Added lines #L579 - L581 were not covered by tests

mutating func decode(_: Int8.Type) throws -> Int8 {
Expand Down Expand Up @@ -691,14 +736,63 @@ extension OracleUnkeyedDecodingContainer {
}

Check warning on line 736 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L730-L736

Added lines #L730 - L736 were not covered by tests

@inline(__always)
private mutating func decodeBinaryInteger<T: BinaryInteger>(of type: T.Type = T.self) throws -> T {
private mutating func decodeBinaryInteger<T: BinaryInteger>() throws -> T {
let value = try self.getNextValue(ofType: Int.self)
guard case .int(let value) = value else {
throw self.createTypeMismatchError(type: type, value: value)
switch value {
case .int(let value):
self.currentIndex += 1
return T(value)

case .float(let value):
guard let value = T(exactly: value) else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Number \(value) does not fit in \(T.self)."
)
}

self.currentIndex += 1
return value

case .double(let value):
guard let value = T(exactly: value) else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Number \(value) does not fit in \(T.self)."
)
}

self.currentIndex += 1
return value

default:
throw self.createTypeMismatchError(type: T.self, value: value)
}
}

Check warning on line 771 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L739-L771

Added lines #L739 - L771 were not covered by tests

@inline(__always)
private mutating func decodeFloatingPointNumber<T: BinaryFloatingPoint>() throws -> T {
let value = try self.getNextValue(ofType: T.self)
let (float, original): (T?, any Numeric) = switch value {
case .int(let value):
(T(exactly: value), value)
case .double(let value):
(T(value), value)
case .float(let value):
(T(value), value)
default:
throw self.createTypeMismatchError(type: T.self, value: value)
}

guard let float else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Number \(original) does not fit in \(T.self)."
)
}

self.currentIndex += 1
return T(value)
return float
}

Check warning on line 796 in Sources/OracleNIO/Data/OracleJSON.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OracleNIO/Data/OracleJSON.swift#L774-L796

Added lines #L774 - L796 were not covered by tests
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/OracleNIO/OSON/OracleJSONParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ struct OracleJSONParser {
// skip the field name offsets array for now
let offsetsPosition = buffer.readerIndex
buffer.moveReaderIndex(forwardBy: fieldsCount * offsetsSize)
var slice = try buffer.throwingReadSlice(length: segmentSize)
let slice = try buffer.throwingReadSlice(length: segmentSize)
let finalPosition = buffer.readerIndex

// determine the names of the fields
Expand Down
29 changes: 25 additions & 4 deletions Tests/IntegrationTests/JSONTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ final class JSONTests: XCTIntegrationTest {
""")
try await connection.execute(
"""
INSERT INTO TestCompressedJson VALUES (1, '{"key": "value", "int": 8, "array": [1, 2, 3]}')
INSERT INTO TestCompressedJson VALUES (
1,
'{"key": "value", "int": 8, "array": [1, 2, 3], "bool1": true, "bool2": false, "nested": {"float": 1.2, "double": 1.23, "null": null}}'
)
""")
}
}
Expand All @@ -71,13 +74,31 @@ final class JSONTests: XCTIntegrationTest {
for try await (id, json) in stream.decode((Int, OracleJSON).self) {
XCTAssertEqual(id, 1)
let value = try json.decode(as: MyJSON.self)
XCTAssertEqual(value, MyJSON(key: "value", int: 8, array: [1, 2, 3]))
XCTAssertEqual(
value, MyJSON(
key: "value",
int: 8,
array: [1, 2, 3],
bool1: true,
bool2: false,
nested: .init(float: 1.2, double: 1.23, null: nil)
)
)
}

struct MyJSON: Decodable, Equatable {
var key: String
var int: Double
var array: [Double]
var int: Int
var array: [Int]
var bool1: Bool
var bool2: Bool
var nested: Nested

struct Nested: Decodable, Equatable {
var float: Float
var double: Double
var null: String?
}
}
}
}

0 comments on commit 1e08ed7

Please sign in to comment.