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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ Available writing options:
- `.escapeUnicode` — Escape non-ASCII characters as `\uXXXX`
- `.newlineAtEnd` — Add trailing newline `\n`

Non-standard options (unavailable when `strictStandardJSON` trait is enabled):

- `.allowInfAndNaN` — Write `Infinity` and `NaN` literals
- `.infAndNaNAsNull` — Write `Infinity` and `NaN` as `null` (takes precedence)

## Read and Write Options

### Reading Options
Expand Down Expand Up @@ -375,7 +380,7 @@ Available options:
Non-standard options (unavailable when `strictStandardJSON` trait is enabled):

- `.allowInfAndNaN` — Write `Infinity` and `NaN` literals
- `.infAndNaNAsNull` — Write `Infinity` and `NaN` as `null`
- `.infAndNaNAsNull` — Write `Infinity` and `NaN` as `null` (takes precedence)

## Package Traits

Expand Down
8 changes: 6 additions & 2 deletions Sources/YYJSON/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,14 @@ public struct YYJSONWriteOptions: OptionSet, Sendable {

#if !YYJSON_DISABLE_NON_STANDARD

/// Write inf and nan number as 'Infinity' and 'NaN' literal.
/// Writes infinity and NaN values as `Infinity` and `NaN` literals.
///
/// If you set `infAndNaNAsNull`, it takes precedence.
public static let allowInfAndNaN = YYJSONWriteOptions(rawValue: YYJSON_WRITE_ALLOW_INF_AND_NAN)

/// Write inf and nan number as null literal.
/// Writes infinity and NaN values as `null` literals.
///
/// This option takes precedence over `allowInfAndNaN`.
public static let infAndNaNAsNull = YYJSONWriteOptions(rawValue: YYJSON_WRITE_INF_AND_NAN_AS_NULL)

#endif // !YYJSON_DISABLE_NON_STANDARD
Expand Down
98 changes: 68 additions & 30 deletions Sources/YYJSON/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ import Foundation
codingPath: [],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: writeOptions
)

try value.encode(to: encoder)
Expand Down Expand Up @@ -135,21 +136,24 @@ import Foundation
let userInfo: [CodingUserInfoKey: Any]
let dateEncodingStrategy: DateEncodingStrategy
let dataEncodingStrategy: DataEncodingStrategy
let writeOptions: YYJSONWriteOptions

init(
doc: UnsafeMutablePointer<yyjson_mut_doc>,
value: UnsafeMutablePointer<yyjson_mut_val>? = nil,
codingPath: [CodingKey] = [],
userInfo: [CodingUserInfoKey: Any] = [:],
dateEncodingStrategy: DateEncodingStrategy = .deferredToDate,
dataEncodingStrategy: DataEncodingStrategy = .base64
dataEncodingStrategy: DataEncodingStrategy = .base64,
writeOptions: YYJSONWriteOptions = .default
) {
self.doc = doc
self.value = value
self.codingPath = codingPath
self.userInfo = userInfo
self.dateEncodingStrategy = dateEncodingStrategy
self.dataEncodingStrategy = dataEncodingStrategy
self.writeOptions = writeOptions
}

func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
Expand Down Expand Up @@ -188,6 +192,15 @@ import Foundation
dataEncodingStrategy: dataEncodingStrategy
)
}

func numberValue(_ value: Double) -> UnsafeMutablePointer<yyjson_mut_val> {
#if !YYJSON_DISABLE_NON_STANDARD
if !value.isFinite && writeOptions.contains(.infAndNaNAsNull) {
return yyjson_mut_null(doc)
}
#endif
return yyjson_mut_real(doc, value)
}
}

// MARK: - Encoding Containers
Expand Down Expand Up @@ -248,7 +261,7 @@ import Foundation

mutating func encode(_ value: Double, forKey key: Key) throws {
guard let obj = self.value else { return }
let numVal = yyjson_mut_real(doc, value)
let numVal = encoder.numberValue(value)
let keyVal = yyFromString(key.stringValue, in: doc)
_ = yyjson_mut_obj_put(obj, keyVal, numVal)
}
Expand Down Expand Up @@ -332,7 +345,8 @@ import Foundation
codingPath: codingPath + [key],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
let encodedValue = try encodeValue(value, using: encoder)
let keyVal = yyFromString(key.stringValue, in: doc)
Expand All @@ -349,7 +363,8 @@ import Foundation
codingPath: codingPath + [key],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
return encoder.container(keyedBy: type)
}
Expand All @@ -362,7 +377,8 @@ import Foundation
codingPath: codingPath + [key],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
return encoder.container(keyedBy: type)
}
Expand All @@ -375,7 +391,8 @@ import Foundation
codingPath: codingPath + [key],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
return nestedEncoder.unkeyedContainer()
}
Expand All @@ -388,7 +405,8 @@ import Foundation
codingPath: codingPath + [key],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
return _YYUnkeyedEncodingContainer(
doc: doc,
Expand All @@ -408,7 +426,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
}

Expand All @@ -420,7 +439,8 @@ import Foundation
codingPath: codingPath + [key],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
}
let nestedObj = yyjson_mut_obj(doc)
Expand All @@ -432,7 +452,8 @@ import Foundation
codingPath: codingPath + [key],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
}

Expand All @@ -447,7 +468,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try date.encode(to: encoder)
guard let val = encoder.value else {
Expand Down Expand Up @@ -481,7 +503,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try closure(date, encoder)
guard let val = encoder.value else {
Expand Down Expand Up @@ -509,7 +532,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try data.encode(to: encoder)
guard let val = encoder.value else {
Expand All @@ -527,7 +551,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try closure(data, encoder)
guard let val = encoder.value else {
Expand Down Expand Up @@ -612,7 +637,7 @@ import Foundation

mutating func encode(_ value: Double) throws {
guard let arr = self.value else { return }
let numVal = yyjson_mut_real(doc, value)
let numVal = encoder.numberValue(value)
_ = yyjson_mut_arr_append(arr, numVal)
}

Expand Down Expand Up @@ -689,7 +714,8 @@ import Foundation
codingPath: codingPath + [AnyCodingKey(index: count)],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
let encodedValue = try encodeValue(value, using: encoder)
_ = yyjson_mut_arr_append(arr, encodedValue)
Expand All @@ -708,7 +734,8 @@ import Foundation
codingPath: codingPath + [AnyCodingKey(index: count)],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
return encoder.container(keyedBy: type)
}
Expand All @@ -724,7 +751,8 @@ import Foundation
codingPath: codingPath + [AnyCodingKey(index: count)],
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
return _YYUnkeyedEncodingContainer(
doc: doc,
Expand All @@ -744,7 +772,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
}

Expand All @@ -759,7 +788,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try date.encode(to: encoder)
guard let val = encoder.value else {
Expand Down Expand Up @@ -793,7 +823,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try closure(date, encoder)
guard let val = encoder.value else {
Expand Down Expand Up @@ -821,7 +852,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try data.encode(to: encoder)
guard let val = encoder.value else {
Expand All @@ -839,7 +871,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try closure(data, encoder)
guard let val = encoder.value else {
Expand Down Expand Up @@ -910,7 +943,7 @@ import Foundation
}

mutating func encode(_ value: Double) throws {
self.value = yyjson_mut_real(doc, value)
self.value = encoder.numberValue(value)
encoder.value = self.value
}

Expand Down Expand Up @@ -981,7 +1014,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try value.encode(to: nestedEncoder)
self.value = nestedEncoder.value
Expand All @@ -997,7 +1031,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try date.encode(to: nestedEncoder)
guard let val = nestedEncoder.value else {
Expand Down Expand Up @@ -1031,7 +1066,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try closure(date, nestedEncoder)
guard let val = nestedEncoder.value else {
Expand All @@ -1057,7 +1093,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try data.encode(to: nestedEncoder)
guard let val = nestedEncoder.value else {
Expand All @@ -1075,7 +1112,8 @@ import Foundation
codingPath: codingPath,
userInfo: userInfo,
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy
dataEncodingStrategy: dataEncodingStrategy,
writeOptions: encoder.writeOptions
)
try closure(data, nestedEncoder)
guard let val = nestedEncoder.value else {
Expand Down
Loading