Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rename deserialization protocols for clarity, improve usability of error types, and broke up into multiple files. (7.0.0) #259

Merged
merged 16 commits into from
May 7, 2022
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
run: |
set -o pipefail
xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash OSX" | xcpretty
xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash iOS" -sdk iphonesimulator -destination "OS=15.0,name=iPhone 12" | xcpretty
xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash iOS" -sdk iphonesimulator -destination "OS=15.2,name=iPhone 13" | xcpretty
xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash tvOS" -sdk appletvsimulator -destination "name=Apple TV" | xcpretty
xcodebuild build $WORKSPACE -scheme "SWXMLHash watchOS" -sdk watchsimulator | xcpretty
bash <(curl -s https://codecov.io/bash) -t ${{secrets.CODECOV_TOKEN}}
Expand Down
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v7.0.0 (May 6, 2022)

- Added `XMLValueDeserialization` which implements both `XMLElementDeserializable` and `XMLAttributeDeserializable`. It makes it a lot easier and simpler to implement custom deserialization for types (e.g. Dates)
- Renamed `XMLIndexerDeserializable` to `XMLObjectDeserialization`. This is a more useful name and better indicates its purpose as compared to the `XMLValueDeserialization`.
- Changed all errors types so that they implement `LocalizedError` (previously, they implemented `CustomStringConvertible`). This should help a lot with handling errors.
- Internally, all files have been heavily reorganized - types are now in their own files.

## v6.0.0 (September 28, 2021)

- Rename main class from `SWXMLHash` to `XMLHash` so that the module name doesn't conflict with the class name. There are a decent number of issues related to naming conflicts so hopefully this will avoid them. Fixes [#242](https://github.com/drmohundro/SWXMLHash/issues/242).
Expand All @@ -21,7 +28,7 @@

## v4.8.0 (March 6, 2019)

- Added support for using String backed RawRepresentables in place of Strings for revelant APIs (via
- Added support for using String backed RawRepresentables in place of Strings for relevant APIs (via
[#204](https://github.com/drmohundro/SWXMLHash/pull/207))

## v4.7.6 (December 11, 2018)
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ I monitor [StackOverflow](http://stackoverflow.com) under the [SWXMLHash tag](ht
When reporting issues, please include:

* Which version of Xcode you're using
* Which OS or platform you're targetting
* Which OS or platform you're targeting
* Any stack trace or compiler error
* Code snippets that reproduce the behavior

Expand Down
152 changes: 79 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ platform :ios, '10.0'
use_frameworks!

target 'YOUR_TARGET_NAME' do
pod 'SWXMLHash', '~> 6.0.0'
pod 'SWXMLHash', '~> 7.0.0'
end
```

Expand All @@ -73,7 +73,7 @@ $ brew install carthage
Then add the following line to your `Cartfile`:

```
github "drmohundro/SWXMLHash" ~> 6.0
github "drmohundro/SWXMLHash" ~> 7.0
```

### Swift Package Manager
Expand All @@ -83,7 +83,7 @@ Swift Package Manager requires Swift version 4.0 or higher. First, create a

```swift
dependencies: [
.package(url: "https://github.com/drmohundro/SWXMLHash.git", from: "6.0.0")
.package(url: "https://github.com/drmohundro/SWXMLHash.git", from: "7.0.0")
]
```

Expand All @@ -92,14 +92,12 @@ dependencies: [
### Manual Installation

To install manually, you'll need to clone the SWXMLHash repository. You can do
this in a separate directory or you can make use of git submodules - in this
this in a separate directory, or you can make use of git submodules - in this
case, git submodules are recommended so that your repository has details about
which commit of SWXMLHash you're using. Once this is done, you can just drop the
`SWXMLHash.swift` file into your project.
which commit of SWXMLHash you're using. Once this is done, you can just drop all
of the relevant swift files into your project.

> NOTE: if you're targeting iOS 7, you'll have to install manually because
> embedded frameworks require a minimum deployment target of iOS 8 or OSX
> Mavericks.
If you're using a workspace, though, you can just include the entire `SWXMLHash.xcodeproj`.

## Getting Started

Expand Down Expand Up @@ -134,8 +132,8 @@ The available options at this time are:
will be returned as "\<table\>")
- Defaults to `false`
- `caseInsensitive`
- This setting allows for key lookups to be case insensitive. Typically XML is
a case sensitive language, but this option lets you bypass this if
- This setting allows for key lookups to be case-insensitive. Typically, XML is
a case-sensitive language, but this option lets you bypass this if
necessary.
- Defaults to `false`
- `encoding`
Expand Down Expand Up @@ -325,20 +323,20 @@ Given:
```xml
<root>
<catalog>
<book id=\"bk101\">
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre><price>44.95</price>
<publish_date>2000-10-01</publish_date>
</book>
<book id=\"bk102\">
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
</book>
<book id=\"bk103\">
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
Expand All @@ -349,7 +347,7 @@ Given:
</root>
```

The following will return return "Midnight Rain". Filtering can be by any part
The following will return "Midnight Rain". Filtering can be by any part
of the `XMLElement` class or by index as well.

```swift
Expand Down Expand Up @@ -388,60 +386,11 @@ lazy parsing doesn't actually occur until the `element` or `all` method are
called - as a result, there isn't any way to know prior to asking for an element
if it exists or not.

### Simple Type Conversion
### XML Deserialization Into Objects

Given:

```xml
<root>
<elem>Monday, 23 January 2016 12:01:12 111</elem>
</root>
```

With the following implementation for `Date` element and attribute
deserialization:

```swift
extension Date: XMLElementDeserializable, XMLAttributeDeserializable {
public static func deserialize(_ element: XMLElement) throws -> Date {
let date = stringToDate(element.text)

guard let validDate = date else {
throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element)
}

return validDate
}

public static func deserialize(_ attribute: XMLAttribute) throws -> Date {
let date = stringToDate(attribute.text)

guard let validDate = date else {
throw XMLDeserializationError.attributeDeserializationFailed(type: "Date", attribute: attribute)
}

return validDate
}

public func validate() throws {
// empty validate... only necessary for custom validation logic after parsing
}

private static func stringToDate(_ dateAsString: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, dd MMMM yyyy HH:mm:ss SSS"
return dateFormatter.date(from: dateAsString)
}
}
```

The below will return a date value:

```swift
let dt: Date = try xml["root"]["elem"].value()
```

### Complex Types Conversion
Even more often, you'll want to deserialize an XML tree into an
array of custom types. This is where `XMLObjectDeserialization`
comes into play.

Given:

Expand Down Expand Up @@ -480,10 +429,10 @@ Given:
</root>
```

with `Book` struct implementing `XMLIndexerDeserializable`:
with `Book` struct implementing `XMLObjectDeserialization`:

```swift
struct Book: XMLIndexerDeserializable {
struct Book: XMLObjectDeserialization {
let title: String
let price: Double
let year: Int
Expand Down Expand Up @@ -513,7 +462,7 @@ let books: [Book] = try xml["root"]["books"]["book"].value()
<img src="https://raw.githubusercontent.com/ncreated/SWXMLHash/assets/types-conversion%402x.png" width="600" alt="Types Conversion" />

You can convert any XML to your custom type by implementing
`XMLIndexerDeserializable` for any non-leaf node (e.g. `<book>` in the example
`XMLObjectDeserialization` for any non-leaf node (e.g. `<book>` in the example
above).

For leaf nodes (e.g. `<title>` in the example above), built-in converters
Expand All @@ -529,6 +478,63 @@ Types conversion supports error handling, optionals and arrays. For more
examples, look into `SWXMLHashTests.swift` or play with types conversion
directly in the Swift playground.

### Custom Value Conversion

Value deserialization is where a specific string value needs to be deserialized
into a custom type. So, date is a good example here - you'd rather deal with
date types than doing string parsing, right? That's what the `XMLValueDeserialization`
attribute is for.

Given:

```xml
<root>
<elem>Monday, 23 January 2016 12:01:12 111</elem>
</root>
```

With the following implementation for `Date` value deserialization:

```swift
extension Date: XMLValueDeserialization {
public static func deserialize(_ element: XMLElement) throws -> Date {
let date = stringToDate(element.text)

guard let validDate = date else {
throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element)
}

return validDate
}

public static func deserialize(_ attribute: XMLAttribute) throws -> Date {
let date = stringToDate(attribute.text)

guard let validDate = date else {
throw XMLDeserializationError.attributeDeserializationFailed(type: "Date", attribute: attribute)
}

return validDate
}

public func validate() throws {
// empty validate... only necessary for custom validation logic after parsing
}

private static func stringToDate(_ dateAsString: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, dd MMMM yyyy HH:mm:ss SSS"
return dateFormatter.date(from: dateAsString)
}
}
```

The below will return a date value:

```swift
let dt: Date = try xml["root"]["elem"].value()
```

## FAQ

### Does SWXMLHash handle URLs for me?
Expand All @@ -546,7 +552,7 @@ deserialization, etc.).
### I'm getting an "Ambiguous reference to member 'subscript'" when I call `.value()`.

`.value()` is used for deserialization - you have to have something that
implements `XMLIndexerDeserializable` (or `XMLElementDeserializable` if it is a
implements `XMLObjectDeserialization` (or `XMLElementDeserializable` if it is a
single element versus a group of elements) and that can handle deserialization
to the left-hand side of expression.

Expand Down Expand Up @@ -612,7 +618,7 @@ extension NSDate: XMLElementDeserializable {

### How do I handle deserialization with an enum?

Check out this great suggestion/example from @woolie up at https://github.com/drmohundro/SWXMLHash/discussions/245.
Check out this great suggestion/example from @woolie up at <https://github.com/drmohundro/SWXMLHash/discussions/245>.

### Have a different question?

Expand Down
Loading