Skip to content
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
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ let package = Package(
.copy("Models/random.json"),
.copy("Models/twitter.json"),
.copy("Models/twitter2.json"),
.copy("Models/twitterescaped.json")
.copy("Models/twitterescaped.json"),
.copy("Resources")
]
)
]
Expand Down
50 changes: 44 additions & 6 deletions Sources/ReerJSON/JSON.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,25 @@ extension JSON {
return yyjson_is_bool(pointer) ? unsafe_yyjson_get_bool(pointer) : nil
}

@inline(__always)
var stringWhenJSON5: String? {
guard let cString = yyjson_get_str(pointer) else { return nil }
let length = yyjson_get_len(pointer)
if memchr(cString, 0, length) != nil {
return nil
}
return String(cString: cString)
}

@inline(__always)
var string: String? {
guard let cString = yyjson_get_str(pointer) else { return nil }
let length = yyjson_get_len(pointer)
if memchr(cString, 0, length) != nil {
let length = yyjson_get_len(pointer)
let rawBuffer = UnsafeRawBufferPointer(start: cString, count: length)
return String(bytes: rawBuffer, encoding: .utf8)
}
return String(cString: cString)
}

Expand All @@ -71,7 +87,13 @@ extension JSON {
guard let cString = yyjson_get_raw(pointer) else { return false }
var convertedVal = yyjson_val()
var error = yyjson_read_err()
guard let _ = yyjson_read_number(cString, &convertedVal, 0, nil, &error) else {
guard let _ = yyjson_read_number(
cString,
&convertedVal,
YYJSON_READ_ALLOW_EXT_NUMBER | YYJSON_READ_ALLOW_INF_AND_NAN,
nil,
&error
) else {
return false
}
return yyjson_is_num(&convertedVal)
Expand All @@ -82,8 +104,16 @@ extension JSON {
guard let cString = yyjson_get_raw(pointer) else { return nil }
var convertedVal = yyjson_val()
var error = yyjson_read_err()
guard let _ = yyjson_read_number(cString, &convertedVal, 0, nil, &error),
yyjson_is_num(&convertedVal) else {
guard
let _ = yyjson_read_number(
cString,
&convertedVal,
YYJSON_READ_ALLOW_EXT_NUMBER | YYJSON_READ_ALLOW_INF_AND_NAN,
nil,
&error
),
yyjson_is_num(&convertedVal)
else {
return nil
}
return yyjson_get_num(&convertedVal)
Expand All @@ -94,8 +124,16 @@ extension JSON {
guard let cString = yyjson_get_raw(pointer) else { return 0 }
var convertedVal = yyjson_val()
var error = yyjson_read_err()
guard let _ = yyjson_read_number(cString, &convertedVal, 0, nil, &error),
yyjson_is_num(&convertedVal) else {
guard
let _ = yyjson_read_number(
cString,
&convertedVal,
YYJSON_READ_ALLOW_EXT_NUMBER | YYJSON_READ_ALLOW_INF_AND_NAN,
nil,
&error
),
yyjson_is_num(&convertedVal)
else {
return 0
}
return yyjson_get_num(&convertedVal)
Expand All @@ -111,7 +149,7 @@ extension JSON {
guard let cString = yyjson_get_raw(pointer) else { return nil }
var convertedVal = yyjson_val()
var error = yyjson_read_err()
guard let _ = yyjson_read_number(cString, &convertedVal, 0, nil, &error) else {
guard let _ = yyjson_read_number(cString, &convertedVal, YYJSON_READ_ALLOW_EXT_NUMBER, nil, &error) else {
return nil
}
if yyjson_is_uint(&convertedVal) {
Expand Down
18 changes: 9 additions & 9 deletions Sources/ReerJSON/JSONDecoderImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ final class JSONDecoderImpl: Decoder {
func unboxString<K: CodingKey>(from value: JSON, for codingPathNode: CodingPathNode, _ additionalKey: K? = nil) throws -> String {
try checkNotNull(value, expectedType: String.self, for: codingPathNode, additionalKey)

guard let string = value.string else {
guard let string = options.json5 ? value.stringWhenJSON5 : value.string else {
throw createTypeMismatchError(type: String.self, for: codingPathNode.path(byAppending: additionalKey), value: value)
}
return string
Expand All @@ -359,7 +359,7 @@ final class JSONDecoderImpl: Decoder {
) throws -> F where F: LosslessStringConvertible {
if let numberValue = value.number {

guard numberValue.isFinite else {
guard numberValue.isFinite || options.json5 else {
throw DecodingError.dataCorrupted(.init(
codingPath: codingPath,
debugDescription: "Number \(value.debugDataTypeDescription) is not representable in Swift."
Expand All @@ -372,7 +372,7 @@ final class JSONDecoderImpl: Decoder {

// Try to decode from a string, for non-conforming float strategy.
if case .convertFromString(let posInf, let negInf, let nan) = options.nonConformingFloatDecodingStrategy,
let string = value.string {
let string = options.json5 ? value.stringWhenJSON5 : value.string {
if string == posInf {
return F.infinity
}
Expand Down Expand Up @@ -403,7 +403,7 @@ extension JSONDecoderImpl: SingleValueDecodingContainer {
}

func decode(_: String.Type) throws -> String {
guard let string = topValue.string else {
guard let string = options.json5 ? topValue.stringWhenJSON5 : topValue.string else {
throw createTypeMismatchError(type: String.self, for: codingPath, value: topValue)
}
return string
Expand Down Expand Up @@ -559,7 +559,7 @@ private final class DefaultKeyedDecodingContainer<K: CodingKey>: KeyedDecodingCo

func decode(_ type: String.Type, forKey key: K) throws -> String {
let jsonValue = try getValue(forKey: key)
guard let string = jsonValue.string else {
guard let string = impl.options.json5 ? jsonValue.stringWhenJSON5 : jsonValue.string else {
throw createTypeMismatchError(type: String.self, forKey: key, value: jsonValue)
}
return string
Expand All @@ -569,7 +569,7 @@ private final class DefaultKeyedDecodingContainer<K: CodingKey>: KeyedDecodingCo
guard let jsonValue = getValueIfPresent(forKey: key), !jsonValue.isNull else {
return nil
}
guard let string = jsonValue.string else {
guard let string = impl.options.json5 ? jsonValue.stringWhenJSON5 : jsonValue.string else {
throw createTypeMismatchError(type: String.self, forKey: key, value: jsonValue)
}
return string
Expand Down Expand Up @@ -932,7 +932,7 @@ private final class PreTransformKeyedDecodingContainer<K: CodingKey>: KeyedDecod

func decode(_ type: String.Type, forKey key: K) throws -> String {
let jsonValue = try getValue(forKey: key)
guard let string = jsonValue.string else {
guard let string = impl.options.json5 ? jsonValue.stringWhenJSON5 : jsonValue.string else {
throw createTypeMismatchError(type: String.self, forKey: key, value: jsonValue)
}
return string
Expand All @@ -942,7 +942,7 @@ private final class PreTransformKeyedDecodingContainer<K: CodingKey>: KeyedDecod
guard let jsonValue = getValueIfPresent(forKey: key), !jsonValue.isNull else {
return nil
}
guard let string = jsonValue.string else {
guard let string = impl.options.json5 ? jsonValue.stringWhenJSON5 : jsonValue.string else {
throw createTypeMismatchError(type: String.self, forKey: key, value: jsonValue)
}
return string
Expand Down Expand Up @@ -1340,7 +1340,7 @@ private struct JSONUnkeyedDecodingContainer: UnkeyedDecodingContainer {

mutating func decode(_ type: String.Type) throws -> String {
let value = try valueFromIterator(ofType: String.self)
guard let string = value.string else {
guard let string = impl.options.json5 ? value.stringWhenJSON5 : value.string else {
throw impl.createTypeMismatchError(type: type, for: currentCodingPath, value: value)
}
advanceToNextValue()
Expand Down
28 changes: 26 additions & 2 deletions Sources/ReerJSON/ReerJSONDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ open class ReerJSONDecoder {
options.userInfo = newValue
}
}

/// Set to `true` to allow parsing of JSON5. Defaults to `false`.
open var allowsJSON5: Bool {
get {
options.json5
}
set {
options.json5 = newValue
}
}

/// Options set on the top-level encoder to pass down the decoding hierarchy.
struct Options {
Expand Down Expand Up @@ -175,11 +185,15 @@ open class ReerJSONDecoder {
/// - 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, path: [String] = []) throws -> T {
var flag: yyjson_read_flag = YYJSON_READ_NUMBER_AS_RAW
if options.json5 {
flag |= YYJSON_READ_JSON5
}
let doc = data.withUnsafeBytes {
yyjson_read(
$0.bindMemory(to: CChar.self).baseAddress,
data.count,
YYJSON_READ_NUMBER_AS_RAW
flag
)
}
guard let doc else {
Expand Down Expand Up @@ -224,11 +238,15 @@ open class ReerJSONDecoder {
path: [String] = [],
configuration: T.DecodingConfiguration
) throws -> T {
var flag: yyjson_read_flag = YYJSON_READ_NUMBER_AS_RAW
if options.json5 {
flag |= YYJSON_READ_JSON5
}
let doc = data.withUnsafeBytes {
yyjson_read(
$0.bindMemory(to: CChar.self).baseAddress,
data.count,
YYJSON_READ_NUMBER_AS_RAW
flag
)
}
guard let doc else {
Expand Down Expand Up @@ -277,6 +295,11 @@ open class ReerJSONDecoder {
decoder.keyDecodingStrategy = keyDecodingStrategy
decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy
decoder.userInfo = userInfo
#if !os(Linux)
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, visionOS 1, *) {
decoder.allowsJSON5 = allowsJSON5
}
#endif
return try decoder.decode(type, from: data)
}

Expand All @@ -293,6 +316,7 @@ open class ReerJSONDecoder {
decoder.keyDecodingStrategy = keyDecodingStrategy
decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy
decoder.userInfo = userInfo
decoder.allowsJSON5 = allowsJSON5
return try decoder.decode(type, from: data, configuration: configuration)
}
#endif
Expand Down
Loading