Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a4c349d
Add lossless Decimal encoding and decoding
mattt May 22, 2026
bd1c254
Implement precise decoding for YYJSONSerialization
mattt May 22, 2026
358ef97
Implement precise decoding for YYJSONValue
mattt May 22, 2026
af56d8b
Skip String allocation when decoding raw numbers
mattt May 22, 2026
99cf19d
Add numberDecodingStrategy for opting into native-number decoding
mattt May 22, 2026
5bb637c
Harden raw integer parsing in YYJSONDecoder
mattt May 22, 2026
45ae9e1
Parse Decimal values with a POSIX locale
mattt May 22, 2026
02bd550
Format encoded Decimal values with a POSIX locale
mattt May 22, 2026
659e546
Force-unwrap expected Decimal values in decoder tests
mattt May 22, 2026
5025375
Drop writer dependency from yyNumberText
mattt May 25, 2026
7d70bda
Factor duplicated decodeDecimal into shared helper
mattt May 26, 2026
a06585e
Accept JSON5 hex literals in raw-number parsing
mattt May 26, 2026
1516b86
Format Decimal test fixtures with a POSIX locale
mattt May 26, 2026
25c156c
Sample roundtripDecimalPreservesPrecision instead of looping 10k
mattt May 26, 2026
191d564
Align NumberDecodingStrategy.fast docs with actual behavior
mattt May 26, 2026
9a5cdea
Import libc explicitly for strtoll/strtod/errno
mattt May 31, 2026
f700971
Reject Decimal NaN when decoding
mattt May 31, 2026
6eaa129
Reformat doc comments with semantic line breaks
mattt May 31, 2026
64baab6
Import Musl libc for the static Linux SDK
mattt May 31, 2026
3f869e6
Copy-edit and reflow doc comments with semantic line breaks
mattt May 31, 2026
ac812f6
Consolidate POSIX locale into a shared declaration
mattt May 31, 2026
e612b99
Reuse yyRawText in yyNumberText
mattt May 31, 2026
811bb31
Clarify NumberDecodingStrategy.fast precision note
mattt May 31, 2026
173ef7f
Update README
mattt May 31, 2026
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
53 changes: 49 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,49 @@ if let name = value["users"]?[0]?["name"]?.string {
}
```

### Number Precision

By default, `YYJSONDecoder` decodes numbers losslessly.
Each number is read as its original text and parsed directly into the
requested Swift type, so values round-trip exactly —
including fractional `Decimal` values like `0.1` and integers larger than `UInt64`.
This matches the precision contract of Foundation's `JSONDecoder`.

```swift
struct Account: Codable {
let balance: Decimal
}

let data = Data(#"{"balance": 9999999999999999.99}"#.utf8)
let account = try YYJSONDecoder().decode(Account.self, from: data)
print(account.balance) // 9999999999999999.99 (no precision loss)
```

If you don't need exact decimals and want maximum throughput on number-heavy payloads,
opt into the faster (but lossy) strategy,
which routes every number through `Double`:

```swift
var decoder = YYJSONDecoder()
decoder.numberDecodingStrategy = .fast
```

`YYJSONEncoder` writes `Decimal` values from their exact text
rather than going through `Double`,
so encoded decimals preserve full precision regardless of the host locale.

For DOM-style access, parse with `.numberAsRaw`
and read the `decimal` property to recover the exact value:

```swift
let value = try YYJSONValue(string: #"{"price": 19.99}"#, options: .numberAsRaw)
print(value["price"]?.decimal) // Optional(19.99)
```

The `number` property returns a `Double` for convenience.
`YYJSONSerialization` likewise preserves precision, bridging fractional
and oversized integer values to `NSDecimalNumber`.

### In-Place Parsing

For maximum performance with large JSON files,
Expand Down Expand Up @@ -502,17 +545,19 @@ However, there are some differences:
`keyEncodingStrategy` or `nonConformingFloatEncodingStrategy`

- **Output formatting**: Uses `writeOptions` instead of `outputFormatting`
- **Number precision**: yyjson parses numbers as 64-bit integers or doubles;
extremely large integers may lose precision
- **Number precision**: `YYJSONDecoder` decodes numbers losslessly by default, matching `JSONDecoder`.
Opt into the faster, `Double`-based strategy with `numberDecodingStrategy = .fast`
(see [Number Precision](#number-precision)).

## Thread Safety

- `YYJSONDecoder` and `YYJSONEncoder` are value types and safe to use from
multiple threads, as long as each `encode`/`decode` call is not shared concurrently.
- `YYJSONValue`, `YYJSONObject`, and `YYJSONArray` are safe to share across threads
for read-only access; they wrap an immutable yyjson document.
- The `number` property on `YYJSONValue` returns a `Double`. For exact representation
of very large numbers, parse using `.bigNumberAsRaw` and read them as strings.
- The `number` property on `YYJSONValue` returns a `Double`.
For exact representation, parse using `.numberAsRaw` (or `.bigNumberAsRaw`)
and read the `decimal` property.

## License

Expand Down
Loading
Loading