You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
let content: [Student] = try decoder.decode([Student].self, from: URL("~/Desktop/Student.csv"))
333
338
```
334
339
335
-
If you are dealing with a big CSV file, it is preferred to used direct file decoding, a `.sequential` or `.unrequested` buffering strategy, and set *presampling* to false; since then memory usage is drastically reduced.
340
+
If you are dealing with a big CSV file, it is preferred to used direct file decoding, a `.sequential` or `.unrequested` buffering strategy, and set _presampling_ to false; since then memory usage is drastically reduced.
336
341
337
342
### Decoder Configuration
338
343
339
344
The decoding process can be tweaked by specifying configuration values at initialization time. `CSVDecoder` accepts the [same configuration values as `CSVReader`](#Reader-Configuration) plus the following ones:
340
345
341
-
- `nilStrategy` (default: `.empty`) indicates how the `nil` *concept* (absence of value) is represented on the CSV.
346
+
- `nilStrategy` (default: `.empty`) indicates how the `nil` _concept_ (absence of value) is represented on the CSV.
342
347
343
348
- `boolStrategy` (default: `.insensitive`) defines how strings are decoded to `Bool` values.
344
349
@@ -352,7 +357,7 @@ The decoding process can be tweaked by specifying configuration values at initia
352
357
353
358
- `bufferingStrategy` (default `.keepAll`) controls the behavior of `KeyedDecodingContainer`s.
354
359
355
-
Selecting a buffering strategy affects the the decoding performance and the amount of memory used during the process. For more information check this README's [Tips using `Codable`](#Tips-using-codable) section and the [`Strategy.DecodingBuffer` definition](sources/Codable/Decodable/DecodingStrategy.swift).
360
+
Selecting a buffering strategy affects the decoding performance and the amount of memory used during the process. For more information check this README's [Tips using `Codable`](#Tips-using-codable) section and the [`Strategy.DecodingBuffer` definition](sources/Codable/Decodable/DecodingStrategy.swift).
356
361
357
362
The configuration values can be set during `CSVDecoder` initialization or at any point before the `decode` function is called.
358
363
@@ -394,7 +399,7 @@ If you are dealing with a big CSV content, it is preferred to use direct file en
394
399
395
400
The encoding process can be tweaked by specifying configuration values. `CSVEncoder` accepts the [same configuration values as `CSVWriter`](#Writer-Configuration) plus the following ones:
396
401
397
-
- `nilStrategy` (default: `.empty`) indicates how the `nil` *concept* (absence of value) is represented on the CSV.
402
+
- `nilStrategy` (default: `.empty`) indicates how the `nil` _concept_ (absence of value) is represented on the CSV.
398
403
399
404
- `boolStrategy` (default: `.deferredToString`) defines how Boolean values are encoded to `String` values.
`Codable` is fairly easy to use and most Swift standard library types already conform to it. However, sometimes it is tricky to get custom types to comply to `Codable` for specific functionality. That is why I am leaving here some tips and advices concerning its usage:
443
+
`Codable` is fairly easy to use and most Swift standard library types already conform to it. However, sometimes it is tricky to get custom types to comply to `Codable` for specific functionality.
439
444
440
445
<ul>
441
446
<details><summary>Basic adoption.</summary><p>
442
447
443
448
When a custom type conforms to `Codable`, the type is stating that it has the ability to decode itself from and encode itself to a external representation. Which representation depends on the decoder or encoder chosen. Foundation provides support for [JSON and Property Lists](https://developer.apple.com/documentation/foundation/archives_and_serialization) and the community provide many other formats, such as: [YAML](https://github.com/jpsim/Yams), [XML](https://github.com/MaxDesiatov/XMLCoder), [BSON](https://github.com/OpenKitten/BSON), and CSV (through this library).
444
449
445
-
Lets see a regular CSV encoding/decoding usage through `Codable`'s interface. Let's suppose we have a list of students formatted in a CSV file:
450
+
Usually a CSV represent a long list of _entities_. The following is a simple example representing a list of students.
446
451
447
452
```swift
448
-
let data = """
449
-
name,age,hasPet
450
-
John,22,true
451
-
Marine,23,false
452
-
Alta,24,true
453
-
"""
453
+
let string = """
454
+
name,age,hasPet
455
+
John,22,true
456
+
Marine,23,false
457
+
Alta,24,true
458
+
"""
454
459
```
455
460
456
-
In Swift, a _student_ has the following structure:
461
+
A _student_ can be represented as a structure:
457
462
458
463
```swift
459
464
struct Student: Codable {
@@ -463,11 +468,11 @@ struct Student: Codable {
463
468
}
464
469
```
465
470
466
-
To decode the CSV data, we just need to create a decoder and call `decode` on it passing the given data.
471
+
To decode the list of students, create a decoder and call `decode` on it passing the CSV sample.
467
472
468
473
```swift
469
474
let decoder = CSVDecoder { $0.headerStrategy = .firstLine }
470
-
let students = try decoder.decode([Student], from: data)
475
+
let students = try decoder.decode([Student].self, from: string)
471
476
```
472
477
473
478
The inverse process (from Swift to CSV) is very similar (and simple).
@@ -485,9 +490,11 @@ When encoding/decoding CSV data, it is important to keep several points in mind:
485
490
486
491
</p>
487
492
<ul>
488
-
<details><summary>Default behavior requires a CSV with a headers row.</summary><p>
493
+
<details><summary><code>Codable</code>'s automatic synthesis requires CSV files with a headers row.</summary><p>
489
494
490
-
The default behavior (i.e. not including `init(from:)` and `encode(to:)`) rely on the existance of the synthesized `CodingKey`s whose `stringValue`s are the property names. For these properties to match any CSV field, the CSV data must contain a _headers row_ at the very beginning. If your CSV doesn't contain a _headers row_, you can specify coding keys with integer values representing the field index.
495
+
`Codable` is able to synthesize `init(from:)` and `encode(to:)` for your custom types when all its members/properties conform to `Codable`. This automatic synthesis create a hidden `CodingKeys` enumeration containing all your property names.
496
+
497
+
During decoding, `CSVDecoder` tries to match the enumeration string values with a field position within a row. For this to work the CSV data must contain a _headers row_ with the property names. If your CSV doesn't contain a _headers row_, you can specify coding keys with integer values representing the field index.
491
498
492
499
```swift
493
500
struct Student: Codable {
@@ -506,7 +513,7 @@ struct Student: Codable {
506
513
> Using integer coding keys has the added benefit of better encoder/decoder performance. By explicitly indicating the field index, you let the decoder skip the functionality of matching coding keys string values to headers.
507
514
508
515
</p></details>
509
-
<details><summary>A CSV is a long list of records/rows.</summary><p>
516
+
<details><summary>A CSV is a long list of rows/records.</summary><p>
510
517
511
518
CSV formatted data is commonly used with flat hierarchies (e.g. a list of students, a list of car models, etc.). Nested structures, such as the ones found in JSON files, are not supported by default in CSV implementations (e.g. a list of users, where each user has a list of services she uses, and each service has a list of the user's configuration values).
[SE167](https://github.com/apple/swift-evolution/blob/master/proposals/0167-swift-encoders.md) proposal introduced to Foundation a new JSON and PLIST encoder/decoder. This proposal also featured encoding/decoding strategies as a new way to configure the encoding/decoding process. `CodableCSV` continues this _tradition_ and mirrors such strategies including some new ones specific to the CSV file format.
613
+
614
+
To configure the encoding/decoding process, you need to set the configuration values of the `CSVEncoder`/`CSVDecoder` before calling the `encode()`/`decode()` functions. There are two ways to set configuration values:
615
+
616
+
- At initialization time, passing the `Configuration` structure to the initializer.
617
+
618
+
```swift
619
+
var config = CSVDecoder.Configuration()
620
+
config.nilStrategy = .empty
621
+
config.decimalStrategy = .local(.current)
622
+
config.dataStrategy = .base64
623
+
config.bufferingStrategy = .sequential
624
+
config.trimStrategy = .whitespaces
625
+
config.encoding = .utf16
626
+
config.delimiters.row = "\r\n"
627
+
628
+
let decoder = CSVDecoder(configuration: config)
629
+
```
630
+
631
+
Alternatively, there are convenience initializers accepting a closure with a `inout Configuration` value.
632
+
633
+
```swift
634
+
let decoder = CSVDecoder {
635
+
$0.nilStrategy = .empty
636
+
$0.decimalStrategy = .local(.current)
637
+
// and so on and so forth
638
+
}
639
+
```
640
+
641
+
- `CSVEncoder` and `CSVDecoder` implement `@dynamicMemberLookup` exclusively for their configuration values. Therefore you can set configuration values after initialization or after a encoding/decoding process has been performed.
642
+
643
+
```swift
644
+
let decoder = CSVDecoder()
645
+
decoder.bufferingStrategy = .sequential
646
+
decoder.decode([Student].self, from: url1)
647
+
648
+
decoder.bufferingStrategy = .keepAll
649
+
decoder.decode([Pets].self, from: url2)
650
+
```
651
+
652
+
The strategies labeled with `.custom` let you insert behavior into the encoding/decoding process without forcing you to manually conform to `init(from:)` and `encode(to:)`. When set, they will reference the targeted type for the whole process. For example, if you want to decode a CSV file where empty fields are marked with the word `null` (for some reason). You could do the following:
653
+
654
+
```swift
655
+
let decoder = CSVDecoder()
656
+
decoder.nilStrategy = .custom({ (decoder) -> Bool in
657
+
do {
658
+
let container = try decoder.singleValueContainer()
The library has been heavily documented and any contribution is welcome. Please take a look at the [How to contribute](docs/CONTRIBUTING.md) document or peer into a more detailed roadmap on the [Github projects](https://github.com/dehesa/CodableCSV/projects).
682
+
The library has been heavily documented and any contribution is welcome. Check the small [How to contribute](docs/CONTRIBUTING.md) document or take a look at the [Github projects](https://github.com/dehesa/CodableCSV/projects) for a more in-depth roadmap.
623
683
624
684
### Community
625
685
626
-
If `CodableCSV` is not of your liking, the Swift community has other CSV solutions:
627
-
- [CSV.swift](https://github.com/yaslab/CSV.swift) is a simpler library with a focus on conforming to the [RFC4180](https://tools.ietf.org/html/rfc4180) standard.
628
-
629
-
It offers an imperative CSV reader/writer and a row decoder. However, it lacks a CSV encoder, whole file decoding, and configurability (e.g. custom field/row delimiters, escaping scalar selection, presampling, etc.).
630
-
631
-
- [SwiftCSV](https://github.com/swiftcsv/SwiftCSV) is an older/popular CSV parse-only library.
632
-
633
-
It offers a well-tested imperative CSV parser with a slower development cycle. It lacks an imperative writer, an encoder, a decoder, and parsing configuration values.
634
-
635
-
- [SwiftCSVExport](https://github.com/vigneshuvi/SwiftCSVExport) reads/writes CSV imperatively with great Objective-C support.
686
+
If `CodableCSV` is not of your liking, the Swift community has developed other CSV solutions:
636
687
637
-
It offers an imperative CSV reader/writer relying on the Objective-C toolchain, which makes it great to use on Objective-C project.
688
+
- [CSV.swift](https://github.com/yaslab/CSV.swift) offers an imperative CSV reader/writer and a _lazy_ row decoder and adheres to the [RFC4180](https://tools.ietf.org/html/rfc4180) standard.
689
+
- [SwiftCSV](https://github.com/swiftcsv/SwiftCSV) is a well-tested parse-only library which loads the whole CSV in memory (not intended for large files).
690
+
- [CSwiftV](https://github.com/Daniel1of1/CSwiftV) is a parse-only library which loads the CSV in memory and parses it in a single go (no imperative reading).
691
+
- [CSVImporter](https://github.com/Flinesoft/CSVImporter) is an asynchronous parse-only library with support for big CSV files (incremental loading).
692
+
- [SwiftCSVExport](https://github.com/vigneshuvi/SwiftCSVExport) reads/writes CSV imperatively with Objective-C support.
693
+
- [swift-csv](https://github.com/brutella/swift-csv) offers an imperative CSV reader/writer based on Foundation's streams.
694
+
- [CSV](https://github.com/skelpo/CSV) offers synchronous and asynchronous imperative CSV reader/writer and encoders/decoders.
695
+
- [CommonCoding](https://github.com/Lantua/CommonCoding) provides CSV encoder/decoder conforming to the [RFC4180](https://tools.ietf.org/html/rfc4180) standard.
638
696
639
697
There are many good tools outside the Swift community. Since writing them all would be a hard task, I will just point you to the great [AwesomeCSV](https://github.com/secretGeek/awesomeCSV) github repo. Take it a look! There are a lot of treasures to be found there.
Copy file name to clipboardExpand all lines: docs/CONTRIBUTING.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,7 +15,7 @@ The only contribution requirements are:
15
15
16
16
That is it. Thank you for taking the time to read this and for contributing.
17
17
18
-
## Looking where to start
18
+
## Where to start
19
19
20
20
This repo's [Github projects](https://github.com/dehesa/CodableCSV/projects) are kept up to date and they are a good place to look if you don't know where to start.
0 commit comments