Easy XML parsing using Codable protocols in Swift

Overview

XMLCoder

Encoder & Decoder for XML using Swift's Codable protocols.

Build Status Version License Platform Coverage

This package is a fork of the original ShawnMoore/XMLParsing with more features and improved test coverage. Automatically generated documentation is available on our GitHub Pages.

Example

import XMLCoder
import Foundation

let sourceXML = """
<note>
    <to>Bob</to>
    <from>Jane</from>
    <heading>Reminder</heading>
    <body>Don't forget to use XMLCoder!</body>
</note>
"""

struct Note: Codable {
    let to: String
    let from: String
    let heading: String
    let body: String
}

let note = try! XMLDecoder().decode(Note.self, from: Data(sourceXML.utf8))

let encodedXML = try! XMLEncoder().encode(note, withRootKey: "note")

Advanced features

The following features are available in 0.4.0 release or later (unless stated otherwise):

Stripping namespace prefix

Sometimes you need to handle an XML namespace prefix, like in the XML below:

<h:table xmlns:h="http://www.w3.org/TR/html4/">
  <h:tr>
    <h:td>Apples</h:td>
    <h:td>Bananas</h:td>
  </h:tr>
</h:table>

Stripping the prefix from element names is enabled with shouldProcessNamespaces property:

struct Table: Codable, Equatable {
    struct TR: Codable, Equatable {
        let td: [String]
    }

    let tr: [TR]
}


let decoder = XMLDecoder()

// Setting this property to `true` for the namespace prefix to be stripped
// during decoding so that key names could match.
decoder.shouldProcessNamespaces = true

let decoded = try decoder.decode(Table.self, from: xmlData)

Dynamic node coding

XMLCoder provides two helper protocols that allow you to customize whether nodes are encoded and decoded as attributes or elements: DynamicNodeEncoding and DynamicNodeDecoding.

The declarations of the protocols are very simple:

protocol DynamicNodeEncoding: Encodable {
    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding
}

protocol DynamicNodeDecoding: Decodable {
    static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding
}

The values returned by corresponding static functions look like this:

enum NodeDecoding {
    // decodes a value from an attribute
    case attribute

    // decodes a value from an element
    case element

    // the default, attempts to decode as an element first,
    // otherwise reads from an attribute
    case elementOrAttribute
}

enum NodeEncoding {
    // encodes a value in an attribute
    case attribute

    // the default, encodes a value in an element
    case element

    // encodes a value in both attribute and element
    case both
}

Add conformance to an appropriate protocol for types you'd like to customize. Accordingly, this example code:

struct Book: Codable, Equatable, DynamicNodeEncoding {
    let id: UInt
    let title: String
    let categories: [Category]

    enum CodingKeys: String, CodingKey {
        case id
        case title
        case categories = "category"
    }

    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
        switch key {
        case Book.CodingKeys.id: return .both
        default: return .element
        }
    }
}

works for this XML:

<book id="123">
    <id>123</id>
    <title>Cat in the Hat</title>
    <category>Kids</category>
    <category>Wildlife</category>
</book>

Please refer to PR #70 by @JoeMatt for more details.

Coding key value intrinsic

Suppose that you need to decode an XML that looks similar to this:

<?xml version="1.0" encoding="UTF-8"?>
<foo id="123">456</foo>

By default you'd be able to decode foo as an element, but then it's not possible to decode the id attribute. XMLCoder handles certain CodingKey values in a special way to allow proper coding for this XML. Just add a coding key with stringValue that equals "" (empty string). What follows is an example type declaration that encodes the XML above, but special handling of coding keys with those values works for both encoding and decoding.

struct Foo: Codable, DynamicNodeEncoding {
    let id: String
    let value: String

    enum CodingKeys: String, CodingKey {
        case id
        case value = ""
    }

    static func nodeEncoding(forKey key: CodingKey)
    -> XMLEncoder.NodeEncoding {
        switch key {
        case CodingKeys.id:
            return .attribute
        default:
            return .element
        }
    }
}

Thanks to @JoeMatt for implementing this in in PR #73.

Preserving whitespaces in element content

By default whitespaces are trimmed in element content during decoding. This includes string values decoded with value intrinsic keys. Starting with version 0.5 you can now set a property trimValueWhitespaces to false (the default value is true) on XMLDecoder instance to preserve all whitespaces in decoded strings.

Choice element coding

Starting with version 0.8, you can encode and decode enums with associated values by conforming your CodingKey type additionally to XMLChoiceCodingKey. This allows decoding XML elements similar in structure to this example:

<container>
    <int>1</int>
    <string>two</string>
    <string>three</string>
    <int>4</int>
    <int>5</int>
</container>

To decode these elements you can use this type:

enum IntOrString: Equatable {
    case int(Int)
    case string(String)
}

extension IntOrString: Codable {
    enum CodingKeys: String, XMLChoiceCodingKey {
        case int
        case string
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case let .int(value):
            try container.encode(value, forKey: .int)
        case let .string(value):
            try container.encode(value, forKey: .string)
        }
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            self = .int(try container.decode(Int.self, forKey: .int))
        } catch {
            self = .string(try container.decode(String.self, forKey: .string))
        }
    }
}

This is described in more details in PR #119 by @jsbean and @bwetherfield.

Integrating with Combine

Starting with XMLCoder version 0.9, when Apple's Combine framework is available, XMLDecoder conforms to the TopLevelDecoder protocol, which allows it to be used with the decode(type:decoder:) operator:

import Combine
import Foundation
import XMLCoder

func fetchBook(from url: URL) -> AnyPublisher<Book, Error> {
    return URLSession.shared.dataTaskPublisher(for: url)
        .map(\.data)
        .decode(type: Book.self, decoder: XMLDecoder())
        .eraseToAnyPublisher()
}

This was implemented in PR #132 by @sharplet.

Additionally, starting with XMLCoder 0.11 XMLEncoder conforms to the TopLevelEncoder protocol:

import Combine
import XMLCoder

func encode(book: Book) -> AnyPublisher<Data, Error> {
    return Just(book)
        .encode(encoder: XMLEncoder())
        .eraseToAnyPublisher()
}

The resulting XML in the example above will start with <book, to customize capitalization of the root element (e.g. <Book) you'll need to set an appropriate keyEncoding strategy on the encoder. To change the element name altogether you'll have to change the name of the type, which is an unfortunate limitation of the TopLevelEncoder API.

Root element attributes

Sometimes you need to set attributes on the root element, which aren't directly related to your model type. Starting with XMLCoder 0.11 the encode function on XMLEncoder accepts a new rootAttributes argument to help with this:

struct Policy: Encodable {
    var name: String
}

let encoder = XMLEncoder()
let data = try encoder.encode(Policy(name: "test"), rootAttributes: [
    "xmlns": "http://www.nrf-arts.org/IXRetail/namespace",
    "xmlns:xsd": "http://www.w3.org/2001/XMLSchema",
    "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
])

The resulting XML will look like this:

<policy xmlns="http://www.nrf-arts.org/IXRetail/namespace"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <name>test</name>
</policy>

This was implemented in PR #160 by @portellaa.

Installation

Requirements

Apple Platforms

  • Xcode 11.0 or later
    • IMPORTANT: compiling XMLCoder with Xcode 11.2.0 (11B52) and 11.2.1 (11B500) is not recommended due to crashes with EXC_BAD_ACCESS caused by a compiler bug, please use Xcode 11.3 or later instead. Please refer to #150 for more details.
  • Swift 5.1 or later
  • iOS 9.0 / watchOS 2.0 / tvOS 9.0 / macOS 10.10 or later deployment targets

Linux

  • Ubuntu 18.04 or later
  • Swift 5.1 or later

Swift Package Manager

Swift Package Manager is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies on all platforms.

Once you have your Swift package set up, adding XMLCoder as a dependency is as easy as adding it to the dependencies value of your Package.swift.

dependencies: [
    .package(url: "https://github.com/MaxDesiatov/XMLCoder.git", from: "0.12.0")
]

If you're using XMLCoder in an app built with Xcode, you can also add it as a direct dependency using Xcode's GUI.

CocoaPods

CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects for Apple's platfoms. You can install it with the following command:

$ gem install cocoapods

Navigate to the project directory and create Podfile with the following command:

$ pod install

Inside of your Podfile, specify the XMLCoder pod:

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'YourApp' do
  # Comment the next line if you're not using Swift or don't want
  # to use dynamic frameworks
  use_frameworks!

  # Pods for YourApp
  pod 'XMLCoder', '~> 0.12.0'
end

Then, run the following command:

$ pod install

Open the the YourApp.xcworkspace file that was created. This should be the file you use everyday to create your app, instead of the YourApp.xcodeproj file.

Carthage

Carthage is a dependency manager for Apple's platfoms that builds your dependencies and provides you with binary frameworks.

Carthage can be installed with Homebrew using the following command:

$ brew update
$ brew install carthage

Inside of your Cartfile, add GitHub path to XMLCoder:

github "MaxDesiatov/XMLCoder" ~> 0.12.0

Then, run the following command to build the framework:

$ carthage update

Drag the built framework into your Xcode project.

Contributing

This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to [email protected].

Sponsorship

If this library saved you any amount of time or money, please consider sponsoring the work of its maintainer. While some of the sponsorship tiers give you priority support or even consulting time, any amount is appreciated and helps in maintaining the project.

Coding Style

This project uses SwiftFormat and SwiftLint to enforce formatting and coding style. We encourage you to run SwiftFormat within a local clone of the repository in whatever way works best for you either manually or automatically via an Xcode extension, build phase or git pre-commit hook etc.

To guarantee that these tools run before you commit your changes on macOS, you're encouraged to run this once to set up the pre-commit hook:

brew bundle # installs SwiftLint, SwiftFormat and pre-commit
pre-commit install # installs pre-commit hook to run checks before you commit

Refer to the pre-commit documentation page for more details and installation instructions for other platforms.

SwiftFormat and SwiftLint also run on CI for every PR and thus a CI build can fail with incosistent formatting or style. We require CI builds to pass for all PRs before merging.

Test Coverage

Our goal is to keep XMLCoder stable and to serialize any XML correctly according to XML 1.0 standard. All of this can be easily tested automatically and we're slowly improving test coverage of XMLCoder and don't expect it to decrease. PRs that decrease the test coverage have a much lower chance of being merged. If you add any new features, please make sure to add tests, likewise for changes and any refactoring in existing code.

Comments
  • Simplify string isAllWhitespace implementation

    Simplify string isAllWhitespace implementation

    This patch is really very simple one, but I believe can be both a simplification on implementation and a perf improvement in the sense that as it is if we take for example as reference the implementation of trimmingCharacters we note that it will do a lot extra work as allocating buffers or a whole new string maybe, when this only need a simple check that can exit early and also avoid those extra allocations.

    I'm not sure if is significant, because I cannot tell if this is used in hot paths within the project and also I didn't measure, so perf improvement is just a guess that makes sense to me, and since it was a simple change I thought in open the PR. So let me know if this makes sense :)

    opened by LucianoPAlmeida 19
  • Add support for root attributes propagation

    Add support for root attributes propagation

    Sometimes we want some attributes in the root node of our xml that don't should or need to be in the model, for example the attributes for the namespace or schema.

    This pull request uses the existing attributes in the boxes to add those attributes to the root node.

    Also, we can now infer the root node name instead of providing it in the encode method, making it optional in the API.

    I'm not sure if this is the best place, or add them as a configuration for the coders instead of use them all the time we call the encode method.

    It would be nice if those attributes were moved to the coder configuration, so we could have the clean coding methods and looking more like the Apple JSON coders. What you think?

    enhancement 
    opened by portellaa 17
  • Fix `trimValueWhitespaces` removing needed white-spaces

    Fix `trimValueWhitespaces` removing needed white-spaces

    Hello everyone!

    I found that the logic for trimming white-spaces (trimValueWhitespaces) may inadvertently remove white-spaces from inside the string value of the element, if XMLParser reports the content of a single element as multiple strings.

    This happens for example with non-ascii characters like German umlauts: Lösen is split up and reported separately as L and ösen. Or where we have "mixed" strings, e.g. containing a segment of Japanese or Chinese characters: Japanese 日本語 is split into Japanese and 日本語.

    If there's a white-space adjacent to the split, as in the Japanese example above, the white-space gets trimmed before it's joined together. Resulting in Japanese日本語 without a space in the middle.

    I added failing tests with the first commit.


    see also: https://stackoverflow.com/a/1076951/2064473

    The parser object may send the delegate several parser:foundCharacters: messages to report the characters of an element. Because string may be only part of the total character content for the current element, you should append it to the current accumulation of characters until the element changes.

    opened by MartinP7r 16
  • Attributed Intrinsic (value coding key)

    Attributed Intrinsic (value coding key)

    Overview

    This PR fixes #12: decoding of unkeyed single value elements that contain attributes as reported in that issue.

    Example

    <?xml version="1.0" encoding="UTF-8"?>
    <foo id="123">456</foo>
    
    private struct Foo: Codable, DynamicNodeEncoding {
        let id: String
        let value: String
    
        enum CodingKeys: String, CodingKey {
            case id
            case value
            // case value = "" would also work
        }
    
        static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
            switch key {
            case CodingKeys.id:
                return .attribute
            default:
                return .element
            }
        }
    }
    

    Previously this XML example would fail to decode. This PR allows two different methods of decoding discussed in usage.

    Usage

    This PR will support decoding the example XML in two cases as long as the prerequisite cases are matched.

    Prerequisites

    1. No keyed child elements exist
    2. Any keyed child nodes are supported Attribute types and are indicated to decode as such

    Supported cases

    1. An instance var with the key value of any decodable type.
    2. An instance var of any key that has a Decoding key of String value "value" or "".

    The decoder will look for the case where an element was keyed with either "value" or "", but not both, and only one of those values (ie; no other keyed elements). It will automatically find the correct value based on the CodingKey supplied.

    Other considerations

    The choice to decode as either "value" or "" keys was purely to try to support the inverse to XML version which would only work if an instance var specifically has a CodingKey with associated value type String that returns an empty string, if PR #70 is commited as-is, which adds XML coding support for unkeyed attributed value elements.

    The 'value' variant was added as a simpler means to support decoding a nested unkeyed element without having to provide custom CodingKey enum for a struct. Something needed to be provided since Swift doesn't have empty string iVars let "" : String, isn't a valid iVar token for example, so value was chosen as a logical default.

    Notes

    This PR is an extension of #70 , though it could be recoded to work off of master. The last commit in this PR is the only commit specific to this feature, though #70 provides the inverse solution of creating XML from an attributed value wrapping struct.

    Coding and decoding unit tests of String and Int values are included.

    opened by JoeMatt 16
  • how to

    how to "skip" certain XML tags / element in a TCX file

    I'm working on a fitness app that uses the pod from TcxDataProtocol ( https://github.com/FitnessKit/TcxDataProtocol/issues/10#issue-987377764 ) and having some issues in decoding some TCX files due to the way how TCXs are formatted across different sites.

    eg: (From Strava)

    <TrainingCenterDatabase 
    xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd" 
    xmlns:ns2="http://www.garmin.com/xmlschemas/UserProfile/v2" 
    xmlns:ns3="http://www.garmin.com/xmlschemas/ActivityExtension/v2" 
    xmlns:ns5="http://www.garmin.com/xmlschemas/ActivityGoals/v1" 
    xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    

    From Garmin

    <TrainingCenterDatabase
      xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd"
    	xmlns:ns5="http://www.garmin.com/xmlschemas/ActivityGoals/v1"
    	xmlns:ns3="http://www.garmin.com/xmlschemas/ActivityExtension/v2"
    	xmlns:ns2="http://www.garmin.com/xmlschemas/UserProfile/v2"
    	xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:ns4="http://www.garmin.com/xmlschemas/ProfileExtension/v1">
    

    From: RideWithGPS

    <TrainingCenterDatabase 
    xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.garmin.com/xmlschemas/ProfileExtension/v1 
    http://www.garmin.com/xmlschemas/UserProfilePowerExtensionv1.xsd 
    http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 
    http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd 
    http://www.garmin.com/xmlschemas/UserProfile/v2 
    http://www.garmin.com/xmlschemas/UserProfileExtensionv2.xsd">
    

    As you can see, the headers are all slightly different.

    This is what the current CodingKeys look like and if I were to manually comment out the missing cases, then the decoding process will go thru. I'm hoping to get some pointers as to how to make this decoding process a little bit more flexible.

    https://github.com/FitnessKit/TcxDataProtocol/blob/master/Sources/TcxDataProtocol/Elements/TrainingCenterDatabase.swift

        /// Coding Keys
        enum CodingKeys: String, CodingKey {
            case schemaLocation = "xsi:schemaLocation"
    
            case xmlnsNs2 = "xmlns:ns2"
            case xmlnsNs3 = "xmlns:ns3"
            case xmlnsNs4 = "xmlns:ns4"
            case xmlnsNs5 = "xmlns:ns5"
    
            case xmlns = "xmlns"
            case xmlnsXsi = "xmlns:xsi"
    
            case activities = "Activities"
            case author = "Author"
        }
    }
    
    extension TrainingCenterDatabase: DynamicNodeEncoding {
        
        public static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
            switch key {
            case TrainingCenterDatabase.CodingKeys.schemaLocation:
                return .attribute
            case TrainingCenterDatabase.CodingKeys.xmlnsNs2:
                return .attribute
            case TrainingCenterDatabase.CodingKeys.xmlnsNs3:
                return .attribute
            case TrainingCenterDatabase.CodingKeys.xmlnsNs4:
                return .attribute
            case TrainingCenterDatabase.CodingKeys.xmlnsNs5:
                return .attribute
            case TrainingCenterDatabase.CodingKeys.xmlns:
                return .attribute
            case TrainingCenterDatabase.CodingKeys.xmlnsXsi:
                return .attribute
            default:
                return .element
            }
        }
    }
    

    Thanks.

    Attached is an example file. zzz_Demo(STRAVA)-copy.tcx.zip

    This is the error

    TCXDecode Error:keyNotFound(CodingKeys(stringValue: "xmlns:ns4", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "xmlns:ns4", intValue: nil)], debugDescription: "No attribute or element found for key CodingKeys(stringValue: \"xmlns:ns4\", intValue: nil) (\"xmlns:ns4\").", underlyingError: nil))
    
    opened by mjunkmyjunk 13
  • Decoding special whitespace characters

    Decoding special whitespace characters

    Hi,

    I'm running into an issue where I think decoding is happening improperly with special characters.

    For example:

    Assuming the following structure:

    struct SimpleScalar: Equatable, Decodable {
        public let stringValue: String?
    }
    

    I believe I should be able to write the following code:

    func decodeScalar_withSpaces() {
        let sourceXML = """
        <SimpleScalar>
            <stringValue>escaped data: &amp;lt;&#xD;&#10;</stringValue>
        </SimpleScalar>
        """
    
        let decoder = XMLDecoder()
        let decoded = try! decoder.decode(SimpleScalar.self, from: Data(sourceXML.utf8))
    
        assert(decoded.stringValue == "escaped data: &lt;\r\n")
    }
    

    However, the assert fails because this string is decoded as escaped data: &lt; instead of the expected value of escaped data: &lt;\r\n.

    1. Is this a bug in the way we are handling these special characters?
    2. I've attempted to remedy this by turning trimValueWhitespace to false (which does allow the test to pass), but this doesn't seem to be the correct path to resolving this issue. Does anyone have any advice on making this work without having to set to trimValueWhitespace to false? I have some other cases where turning trimValueWhitespace to false will make other things difficult for my use case.
    question 
    opened by wooj2 12
  • Bad access error when running on device

    Bad access error when running on device

    After working on simulator on my app, I wanted to test it on my iPad but I have a EXC_BAD_ACCESS on line 382 of XMLDecoderImplementation.swift. This error isn't present when I run the app on the simulator.

    I run the app on a 2017 iPad with iOS 12.1.4 and for the simulator I used the iPad Pro 11" with iOS 12.2.

    more info needed 
    opened by flowbe 11
  • XmlEncoder: ordering of elements

    XmlEncoder: ordering of elements

    Hi, is there any possibility to set the order of attributes in an element while encoding a model-instance? I'm trying to build a GPX reader/writer with this little awesome framework and have noticed that the order of my attributes in an element varies from encode to encode. Am I missing something?

    Thanks in advance

    opened by Ma-He 11
  • [WIP / Proof of concept] Add Decoding support for enums with associated values

    [WIP / Proof of concept] Add Decoding support for enums with associated values

    This PR is an initial step towards adding support for Decoding union-type–like enums-with-associated-values (as presented in #25 and #91).

    There is a single regression test which is currently failing (NestingTests.testDecodeUnkeyedWithinUnkeyed()), though it doesn't seem out of reach to fix it.

    I have added test cases for both #25 and #91, which are passing. For the case of #91, I haven't had success decoding the empty Break type.

    The changes here seem both a bit magical as well as grotesque, but it seems like it is at a good place to stop and talk about further directions!

    There are three changes made to the source here:

    • In XMLCoderElement.transformToBoxTree(), a special case is made to prevent enums-with-associated-value–like types getting represented as attributed unkeyed values.
    • In XMLDecoderImplementation.unkeyedContainer(), keyed boxes get transformed into unkeyed boxes wherein each key-value pair from the original is packaged up into its own KeyedBox carrying a single element representing the key and value. This allows the key to be retrievable downstream in the XMLUnkeyedDecodingContainer.
    • In XMLUnkeyedDecodingContainer.decode(...), the container packaged up by XMLDecoderImplementation.unkeyedContainer() gets unpackaged in the case it may hold the contents of an enum-with-associated-value.
    opened by jsbean 10
  • Add support for kebab-case KeyDecodingStrategy

    Add support for kebab-case KeyDecodingStrategy

    See: https://en.wikipedia.org/wiki/Letter_case#Special_case_styles

    It would also be nice to make all the convert... functions declared under KeyEncodingStrategy and KeyDecodingStrategy public, so consumers can use them when performing a custom conversion.

    I will be adding tests in case this contribution is desired.

    opened by acecilia 10
  • [RFC] Add OrderedDictionary

    [RFC] Add OrderedDictionary

    This PR adds an OrderedDictionary structure to the project, though does not use it for anything yet.

    This should be useful for maintaining order of ~~attributes~~ elements (#17), and was useful in an experimental outing to solve #25, and I can imagine it being useful for #12, as well.

    I thought I'd propose this here to start some conversation about these issues and how to approach solving them. If this won't be of help, feel free to close!

    opened by jsbean 9
  • Extract all parts of the text

    Extract all parts of the text

    Dear All,

    Thanks for this amazing project! I struggle to extract Text Part 2 from the following example.

    <Result Identifier="123" Date="2022-12-01 12:00:00">
    <Result.Description>
    Text Part 1
    <a href="xxx">Text Part 3</a>
    Text Part 2
    </Result.Description>
    </Result>
    

    My struct looks like this 👇

    struct ResultDescription: Codable, DynamicNodeEncoding {
       let text: String // This extracts Text Part 1
       let reference: String? // This extracts Text Part 3
       enum CodingKeys: String, CodingKey {
          case text = ""
          case reference = "a"
       }
    }
    

    Any suggestions on how to extract Text Part 2?

    Thanks 🙌

    opened by tomasbek 0
  • Decoding AWS Response and missing attribute or element

    Decoding AWS Response and missing attribute or element

    I am trying to decode this response

          <?xml version= \"1.0\" encoding= \"UTF-8\"?>
            <ListAllMyBucketsResult xmlns= \"http://s3.amazonaws.com/doc/2006-03-01/\">
                <Buckets>
                    <Bucket>
                        <CreationDate>2022-12-25T04:22:03.812Z</CreationDate>
                        <Name>images</Name>
                    </Bucket>
                    <Bucket>
                        <CreationDate>2022-12-25T04:23:03.352Z</CreationDate>
                        <Name>videos</Name>
                    </Bucket>
                </Buckets>
                <Owner>
                    <DisplayName>asdfasdfasdfasdfa</DisplayName>
                    <ID>asdfasdfasdfasdfa</ID>
                </Owner>
            </ListAllMyBucketsResult>
    

    My structure looks like this.

    public struct ListAllMyBucketsResult: Codable {
        public let buckets: [Bucket]
        public let owner: Owner
    
        public struct Bucket: Codable {
            public let name: String
            public let creationDate: Date
    
            enum CodingKeys: String, CodingKey {
                case name = "Name"
                case creationDate = "CreationDate"
            }
        }
    
        public struct Owner: Codable {
            public let id: String
            public let displayName: String
    
            enum CodingKeys: String, CodingKey {
                case id = "ID"
                case displayName = "DisplayName"
            }
        }
    
        enum CodingKeys: String, CodingKey {
            case buckets = "Buckets"
            case owner = "Owner"
        }
    }
    

    I am getting this error:

    debugDescription : "No attribute or element found for key CodingKeys(stringValue: \"Name\", intValue: nil) (\"Name\")."

    Thanks for helping,sorry if this is not allowed here.

    opened by wmalloc 3
  • DynamicNodeDecoding cause producing incorrect error on failure in nested struct

    DynamicNodeDecoding cause producing incorrect error on failure in nested struct

    The issue makes me crazy when I'm looking error logs. Probably easy to fix, but will help a lot understanding what's went wrong.

    // Assume we have XML:
    let xml =
    """
    <Root>
       <field attr1="value_1" />
    </Root>
    """
    
    // And want to decode it into struct:
    struct Model: Decodable {
        var field: Field
        
        struct Field: Decodable, DynamicNodeDecoding {
            var attr1: String
            var attr2: String
            
            static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding {
                .attribute
            }
        }
    }
    
    // Using XMLDecoder:
    do {
        _ = try XMLDecoder().decode(Model.self, from: Data(xml.utf8))
    } catch {
        print(error)
    }
    

    Actual The error tells that whole property field is null

    ▿ DecodingError
      ▿ valueNotFound : 2 elements
        ▿ .1 : Context
          ▿ codingPath : 1 element
            - 0 : CodingKeys(stringValue: "field", intValue: nil)
          - debugDescription : "Expected Field value but found null instead."
          - underlyingError : nil
    

    Expected If we remove conformance to DynamicNodeDecoding, we get correct error

    ▿ DecodingError
      ▿ keyNotFound : 2 elements
        - .0 : CodingKeys(stringValue: "attr2", intValue: nil)
        ▿ .1 : Context
          ▿ codingPath : 2 elements
            - 0 : CodingKeys(stringValue: "field", intValue: nil)
            - 1 : CodingKeys(stringValue: "attr2", intValue: nil)
          - debugDescription : "No attribute or element found for key CodingKeys(stringValue: \"attr2\", intValue: nil) (\"attr2\")."
          - underlyingError : nil
    
    opened by Alkenso 1
  • Decoding attribute and element with same name but different values

    Decoding attribute and element with same name but different values

    Hello! @MaxDesiatov thanks for this great tool.

    Is it possible to decode object with attribute and child element with same name but different values? For example xml:

    <Shelf List="first">
    	<List>
    		<Book id="1"/>
    		<Book id="2"/>
    	</List>
    </Shelf>
    
    opened by azhabort 1
  • This framework really works?  keyNotFound

    This framework really works? keyNotFound

    I'm try to decode an XML returned by an API. the result is an error:

    Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "ns2:body", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "ns2:body", intValue: nil)], debugDescription: "No attribute or element found for key CodingKeys(stringValue: "ns2:body", intValue: nil) ("ns2:body").", underlyingError: nil))

    this is the struct `struct AirportList:Codable { let ns2:String

    enum CodingKeys: String, CodingKey {
        case ns2 = "ns2:body"
    }
    

    }` I've changed the key in model class, with any tag I need but the result is the same.

    this framework really works?

    opened by Rufy86 4
  • Test error context feature independently from Swift version

    Test error context feature independently from Swift version

    This test output seemed to be pretty stable up to this point, and I haven't found a different way to test the error context functionality end-to-end without being exposed to these caveats with how Foundation handle this.

    Originally posted by @MaxDesiatov in https://github.com/CoreOffice/XMLCoder/pull/245#discussion_r954033736

    opened by MaxDesiatov 0
Releases(0.15.0)
  • 0.15.0(Nov 16, 2022)

    What's Changed

    • Test with Xcode 14.0 and Swift 5.7 by @MaxDesiatov in https://github.com/CoreOffice/XMLCoder/pull/245
    • Update doc comments, add PropertyWrappersTest by @MaxDesiatov in https://github.com/CoreOffice/XMLCoder/pull/246
    • Implement support for encoding DocTypes by @Joannis in https://github.com/CoreOffice/XMLCoder/pull/253

    New Maintainer

    • @Joannis is maintaining XMLCoder now, starting with PR https://github.com/CoreOffice/XMLCoder/pull/253

    Full Changelog: https://github.com/CoreOffice/XMLCoder/compare/0.14.0...0.15.0

    Source code(tar.gz)
    Source code(zip)
  • 0.14.0(Jul 31, 2022)

    What's Changed

    • Simplify string isAllWhitespace implementation by @LucianoPAlmeida in https://github.com/CoreOffice/XMLCoder/pull/235
    • Bump cocoapods-downloader from 1.5.1 to 1.6.3 by @dependabot in https://github.com/CoreOffice/XMLCoder/pull/239
    • Update link to project's GitHub Pages by @christopherweems in https://github.com/CoreOffice/XMLCoder/pull/240
    • Fix iOS 11 support warnings by @kikeenrique in https://github.com/CoreOffice/XMLCoder/pull/242
    • Make AnySequence public XMLDecodableSequence by @MaxDesiatov in https://github.com/CoreOffice/XMLCoder/pull/244

    New Contributors

    • @LucianoPAlmeida made their first contribution in https://github.com/CoreOffice/XMLCoder/pull/235
    • @dependabot made their first contribution in https://github.com/CoreOffice/XMLCoder/pull/239
    • @christopherweems made their first contribution in https://github.com/CoreOffice/XMLCoder/pull/240
    • @kikeenrique made their first contribution in https://github.com/CoreOffice/XMLCoder/pull/242

    Full Changelog: https://github.com/CoreOffice/XMLCoder/compare/0.13.1...0.14.0

    Source code(tar.gz)
    Source code(zip)
  • 0.13.1(Nov 7, 2021)

    This is a bugfix release that fixes an edge case with the use of trimValueWhitespaces configuration on XMLDecoder, and adds official Windows support for users of Swift 5.5. Many thanks to @MartinP7r for multiple contributions to this release!

    Closed issues:

    • Encoding an Attribute doesn't work anymore (#231)
    • How to "skip" certain XML tags / element in a TCX file (#227)
    • Encode element with empty key, no elements, and attributes (#224)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.13.0(Aug 6, 2021)

    This release adds two new features and a bugfix.

    Namely:

    • removeWhitespaceElements boolean flag on XMLDecoder allows removing elements that have purely whitespace content.
    • convertFromUppercase case on KeyDecodingStrategy allows converting ALL_CAPS_SNAKE_CASE to camelCase.
    • an edge case in intrinsic key value decoding has been fixed.

    Many thanks to (in alphabetical order) @huwr, @kneekey23, and @wooj2 for their contributions!

    Closed issues:

    • Decoding special whitespace characters (#219)
    • Help with mix of attributes and elements (#212)

    Merged pull requests:

    • Encode element with empty key, empty element, and attributes (#223) via @wooj2
    • Implement removeWhitespaceElements on XMLDecoder (#222) via @wooj2
    • Add convert from UPPERCASE decoding key strategy (#214) via @huwr
    Source code(tar.gz)
    Source code(zip)
  • 0.12.0(Jan 26, 2021)

    This release adds a few new features:

    • New charactersEscapedInAttributes and charactersEscapedInElements properties on XMLEncoder that allow customizing how certain characters are escaped.
    • You can now override the implementation of TopLevelEncoder Combine protocol conformance when subclassing XMLEncoder.
    • New prettyPrintIndentation property on XMLEncoder, which can take XMLEncoder.PrettyPrintIndentation values such as .tabs(1) or .spaces(2).

    Thanks to Kenta Kubo for the contribution!

    Closed issues:

    • How to decode <itunes:episode> tags (#201)
    • Fail to build in Xcode 12 beta (#196)
    • Changing the root node name ? (#191)
    • " in XML element may not always be escaping (#187)
    • &#10; in XML attributes (#185)
    • " and &quot; are not decoded equally (#184)
    • Use 2 spaces instead of 4 when .prettyPrinted (#183)
    • (Help using) How to decode this XML? (#180)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.11.1(May 3, 2020)

    This release fixes an issue, where non-string values used CDATA encoding. Thanks to @ksoftllc for reporting it!

    Closed issues:

    • Non-string values are being encoded as CData when stringEncodingStrategy = .cdata (#178)
    • How to encode as an empty element (#177)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.11.0(Apr 13, 2020)

    This is a bugfix and feature release, which fixes an issue with CDATA decoding and adds TopLevelEncoder conformance to XMLEncoder. New rootAttributes argument has been added to the encode function on XMLEncoder that allows adding attributes on root elements without adding them to your model types. Thanks to @portellaa, @Kirow and others for their contributions and bug reports!

    Closed issues:

    • CDATA Decoding not working (#168)
    • Decode special XML Structure (#156)
    • Root level attributes don't get encoded back to attribute when converting back to XML file from Plist (#127)
    • Bad access error when running on device (#100)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.10.0(Apr 4, 2020)

    This is a bugfix release, which improves encoding and decoding of enums with associated values (also known as "choice coding") with the XMLChoiceCodingKey protocol. This release is also tested on Xcode 11.4 and Swift 5.2.1 on Linux. A few breaking changes were introduced, which were needed to simplify and improve internals of the library. Please refer to the corresponding section below for more details.

    Thanks to @bwetherfield and @ultramiraculous for their contributions!

    Breaking changes:

    This change was needed to accommodate for multiple edges cases with how arrays of empty elements and empty strings are decoded.

    The value intrinsic now only accepts the empty string key "", as the previous "value" key caused naming collisions with attributes and elemenents that had the same name.

    Closed issues:

    • Bundle identifier in wrong format (#164)
    • Can inheritance be implemented? (#159)
    • EXC_BAD_ACCESS when running tests (#153)
    • EXC_BAD_ACCESS on XCode 11.2 and iOS13.2 (#150)
    • Date formatting on 24h region with display set to 12h (#148)
    • Decoding containers with (potentially)-empty elements (#123)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Oct 19, 2019)

    This release fixes a few bugs with Float type parsing and Swift 5.1 support on Linux. It also adds a helper extension to improve compatibility with Combine and adds a few tests to confirm that a few edges cases are working well. Thanks to @bwetherfield, @DJBen, @jsbean, @mxcl, @marcblanchet and @sharplet for bug reports and pull requests!

    Implemented enhancements:

    • Conditionally conform to Combine.TopLevelDecoder #132 (sharplet)

    Fixed bugs:

    • Value with copyright symbol © has its preceding whitespace trimmed off even trimValueWhitespaces is set to false #141
    • Float vs Float64=Double not parsing 3.14 #130

    Closed issues:

    • TrackPoint position parameter is ignored #125
    • TCX file need an XML root node #124
    • [Swift 5.1] Import FoundationXML rather than Foundation #121

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Aug 4, 2019)

    This release adds support for decoding and encoding ordered sequences of different elements as enums with associated values. In addition, XMLCoder now supports Linux. Many thanks to @jsbean, @bwetherfield and @drewag for implementing this!

    Breaking changes:

    • Fixed typo in XMLDecoder property: errorContextLenght has been renamed to errorContextLength in #114.

    Closed issues:

    • XML with autoclosed tags #116
    • Arrays of enums #91
    • Array of enums with associated values #25

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Jul 2, 2019)

    This release changes the behavior of attributes coding: now order of XML attributes is fully preserved. One of the benefits is that it improves unit testing for users of XMLCoder, which allows testing against specific encoded attributes without accounting for their randomized order. Also a small coding style fix is included. In addition, XMLCoder now uses Azure Pipelines instead of Travis for CI with great improvements to overall CI stability, speed, and parallel builds. Thanks to Andrés Cecilia Luque and Jay Hickey for the contributions!

    Merged pull requests

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Jun 17, 2019)

    An improvement release that introduces convertFromKebabCase and convertToKebabCase key decoding strategies. There were a few changes that aren't visible to end-users: the way that keys and values are stored internally has changed and a few more tests added. Thanks to Andrés Cecilia Luque and Vincent Esche for the contributions!

    Merged pull requests

    Source code(tar.gz)
    Source code(zip)
    XMLCoder.framework.zip(16.73 MB)
  • 0.5.1(May 2, 2019)

  • 0.5.0(May 2, 2019)

    A small improvement release tagged early to resolve blocking issues in CoreXLSX.

    Notable changes

    • Empty value strings are no longer decoded as nil when a String is expected, but are decoded as empty strings, which represents the actual value.
    • trimValueWhitespaces property was added on XMLDecoder, which allows overriding the default behaviour, where starting and trailing whitespaces are trimmed from string values.

    Closed issues

    • Trimmed whitespace on decoding String #94

    Merged pull requests

    • Fixed a bug when decoding a key with one character only #96 (TheFlow95)
    • Add more cases to AttributedIntrinsicTest #95 (MaxDesiatov)
    • Use map instead of mapValues/shuffle in XMLCoderElement.flatten #93 (jsbean)
    • Fix decoding empty element as optional #92 (MaxDesiatov)
    Source code(tar.gz)
    Source code(zip)
    XMLCoder.framework.zip(17.23 MB)
  • 0.4.1(Apr 12, 2019)

  • 0.4.0(Apr 8, 2019)

    This is a release with plenty of new features that allow you to parse many more XML variations than previously. Compatibility with Xcode 10.2 and Swift 5.0 is also improved. A huge thank you to @JoeMatt and @regexident for their contributions, to @hodovani for maintaining the project, and to @Inukinator, @qmoya, @Ma-He, @khoogheem and @thecb4 for reporting issues during development!

    Notable changes

    • Ordered encoding: this was one of the most requested changes and it's finally here! 🎉 Now both keyed and unkeyed elements are encoded in the exactly same order that was used in original containers. This is applicable to both compiler-generated encoding implementations (just reorder properties or cases in your CodingKeys enum if you have it) and manually implemented func encode(to: Encoder).
    • Stripping namespace prefix: now if your coding key string values contain an XML namespace prefix (e.g. prefix h in <h:td>Apples</h:td>), you can set shouldProcessNamespaces property to true on your XMLDecoder instance for the prefix to be stripped before decoding keys in your Decodable types.
    • Previously it was possible to customize encoding with NodeEncodingStrategy, but no such type existed for decoding. A corresponding NodeDecodingStrategy type was added with nodeDecodingStrategy property on XMLDecoder.
    • Thanks to the previous change, XMLCoder now provides two helper protocols that allow you to easily customize whether nodes are encoded and decoded as attributes or elements for conforming types: DynamicNodeEncoding and DynamicNodeDecoding.
    • Previously if you needed to decode or encode an XML element with both attributes and values, this was impossible to do with XMLCoder. Now with the addition of coding key value intrinsic, this is as easy as adding one coding key with a specific string raw value ("value" or empty string "" if you already have an XML attribute named "value").

    Closed issues

    • Crash: Range invalid bounds in XMLStackParser.swift #83
    • Document DynamicNodeEncoding and attributed intrinsic #80
    • Fix nested attributed intrinsic #78
    • nodeEncodingStrategy #49
    • XmlEncoder: ordering of elements #17
    • Can’t reach an XML value #12

    Merged pull requests

    Source code(tar.gz)
    Source code(zip)
    XMLCoder.framework.zip(17.12 MB)
  • 0.3.1(Feb 6, 2019)

  • 0.3.0(Jan 22, 2019)

    A maintenance release focused on fixing bugs, improving error reporting and overall internal architecture of the library. For this release we've started tracking test coverage and were able to increase it from 11.8% to 75.6%. 🎉 Thanks to @hodovani and @regexident for their work on improving test coverage in this release.

    Additions

    You can now set errorContextLength: UInt property on XMLDecoder instance, which will make it add a snippet of XML of at most this length from parser state when a parsing error occurs. This change was provided by @hodovani and can greatly help with attempts to parse invalid XML, where previously only a line and column number were reported.

    Deprecations

    NodeEncodingStrategies was renamed to NodeEncodingStrategy for consistency. NodeEncodingStrategies is still available as a deprecated typealias, which will be removed in future versions. Thanks to @regexident for cleaning this up and providing many more changes in this release that make XMLCoder better and easier to use.

    Changes

    Source code(tar.gz)
    Source code(zip)
    XMLCoder.framework.zip(16.63 MB)
  • 0.2.1(Nov 18, 2018)

  • 0.2.0(Nov 18, 2018)

  • 0.1.1(Nov 18, 2018)

  • 0.1.0(Nov 8, 2018)

    • Add support for decoupled, type-dependent node-encoding strategies (@regexident)
    • Add missing visibility declarations (@regexident)
    • Improve .gitignore and remove tracked *.xcuserdata files (@regexident)
    • Make XMLEncoder.OutputFormatting.prettyPrinted actually do something (@regexident)
    • Add tvOS deployment target to podspec (@edc1591)
    • Fix Carthage command (@salavert)
    • Set deployment versions to allow older SDKs (@Lutzifer)
    • Add Info.plist to allow Framework use in App Store Connect via Carthage (@Lutzifer)
    • Set CURRENT_PROJECT_VERSION (@Lutzifer)
    • Add convertFromCapitalized strategy, simple test (@MaxDesiatov)
    • Allow older iOS/tvOS deployment targets in podspec (@MaxDesiatov)
    Source code(tar.gz)
    Source code(zip)
Owner
Max Desiatov
Software consultant, mostly coding for iOS and web in Swift and TypeScript. I co-maintain @SwiftWasm and @TokamakUI.
Max Desiatov
Simple XML parsing in Swift

SWXMLHash SWXMLHash is a relatively simple way to parse XML in Swift. If you're familiar with NSXMLParser, this library is a simple wrapper around it.

David Mohundro 1.3k Jan 3, 2023
CheatyXML is a Swift framework designed to manage XML easily

CheatyXML CheatyXML is a Swift framework designed to manage XML easily. Requirements iOS 8.0 or later tvOS 9.0 or later Installation Cocoapods If you'

Louis Bodart 24 Mar 31, 2022
The most swifty way to deal with XML data in swift 5.

SwiftyXML SwiftyXML use most swifty way to deal with XML data. Features Infinity subscript dynamicMemberLookup Support (use $ started string to subscr

Kevin 99 Sep 6, 2022
A simple way to map XML to Objects written in Swift

XMLMapper XMLMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from XML. Ex

Giorgos Charitakis 109 Jan 6, 2023
A fast & lightweight XML & HTML parser in Swift with XPath & CSS support

Fuzi (斧子) A fast & lightweight XML/HTML parser in Swift that makes your life easier. [Documentation] Fuzi is based on a Swift port of Mattt Thompson's

Ce Zheng 994 Jan 2, 2023
Ji (戟) is an XML/HTML parser for Swift

Ji 戟 Ji (戟) is a Swift wrapper on libxml2 for parsing XML/HTML. Features Build XML/HTML Tree and Navigate. XPath Query Supported. Comprehensive Unit T

HongHao Zhang 824 Dec 15, 2022
Kanna(鉋) is an XML/HTML parser for Swift.

Kanna(鉋) Kanna(鉋) is an XML/HTML parser for cross-platform(macOS, iOS, tvOS, watchOS and Linux!). It was inspired by Nokogiri(鋸). ℹ️ Documentation Fea

Atsushi Kiwaki 2.3k Dec 31, 2022
Simple XML Parser implemented in Swift

Simple XML Parser implemented in Swift What's this? This is a XML parser inspired by SwiftyJSON and SWXMLHash. NSXMLParser in Foundation framework is

Yahoo! JAPAN 531 Jan 1, 2023
A sensible way to deal with XML & HTML for iOS & macOS

Ono (斧) Foundation lacks a convenient, cross-platform way to work with HTML and XML. NSXMLParser is an event-driven, SAX-style API that can be cumbers

Mattt 2.6k Dec 14, 2022
Generate styled SwiftUI Text from strings with XML tags.

XMLText is a mini library that can generate SwiftUI Text from a given XML string with tags. It uses AttributedString to compose the final text output.

null 15 Dec 7, 2022
Fetch a XML feed and parse it into objects

AlamofireXmlToObjects ?? This is now a subspec of EVReflection and the code is maintained there. ?? You can install it as a subspec like this: use_fra

Edwin Vermeer 65 Dec 29, 2020
Pick a date and explore websites from the early days of the internet to now all in an easy-to-use browser format! 💻

Pick a date and explore websites from the early days of the internet to now all in an easy-to-use browser format! ??

Liam 5 Nov 26, 2022
Mathias Köhnke 1.1k Dec 16, 2022
Library for scanning documents via MRZ (Machine Readable Zones) using  Vision API

MRZScanner Library for scanning documents via MRZ using  Vision API. Example The example project is located inside the Example folder. To run it, you

App in the Air 48 Nov 22, 2022
SwiftSoup: Pure Swift HTML Parser, with best of DOM, CSS, and jquery (Supports Linux, iOS, Mac, tvOS, watchOS)

SwiftSoup is a pure Swift library, cross-platform (macOS, iOS, tvOS, watchOS and Linux!), for working with real-world HTML. It provides a very conveni

Nabil Chatbi 3.7k Jan 6, 2023
📄 A Swift DSL for writing type-safe HTML/CSS in SwiftUI way

?? swift-web-page (swep) Swep is a Swift DSL for writing type-safe HTML/CSS in SwiftUI way. Table of Contents Motivation Examples Safety Design FAQ In

Abdullah Aljahdali 14 Dec 31, 2022
Swift package to convert a HTML table into an array of dictionaries.

Swift package to convert a HTML table into an array of dictionaries.

null 1 Jun 18, 2022
Mongrel is a Swift and HTML hybrid with a bit of support for CSS and Javascript.

Mongrel is a Swift and HTML hybrid with a bit of support for CSS and Javascript. Using a declaritive style of programming, Mongrel makes writing HTML feel natural and easy. Mongrel also uses a SwiftUI like body structure allowing structs to be completely dedicated as an HTML page or element.

Nicholas Bellucci 12 Sep 22, 2022
Codable, but with Super power made custom Codable behavior easy.

Codable, but with Super power made custom Codable behavior easy.

Tsungyu Yu 24 Aug 30, 2022
Swift minion for simple and lightweight XML parsing

AEXML Swift minion for simple and lightweight XML parsing I made this for personal use, but feel free to use it or contribute. For more examples check

Marko Tadić 975 Dec 26, 2022