Skip to content

Commit

Permalink
Merge pull request #169 from drmohundro/add-handling-for-xml-parsing-…
Browse files Browse the repository at this point in the history
…errors

Add handling for XML parsing errors
  • Loading branch information
drmohundro authored Mar 10, 2018
2 parents 86b6d86 + 4639386 commit 17d992b
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 8 deletions.
47 changes: 40 additions & 7 deletions Source/SWXMLHash.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public class SWXMLHashOptions {

/// Any contextual information set by the user for encoding
public var userInfo = [CodingUserInfoKey: Any]()

/// Detect XML parsing errors... defaults to false as this library will
/// attempt to handle HTML which isn't always XML-compatible
public var detectParsingErrors = false
}

/// Simple XML parser
Expand Down Expand Up @@ -223,9 +227,11 @@ extension XMLParserDelegate {
didStartMappingPrefix prefix: String,
toURI namespaceURI: String) { }

func parser(_ parser: Foundation.XMLParser, didEndMappingPrefix prefix: String) { }
func parser(_ parser: Foundation.XMLParser,
didEndMappingPrefix prefix: String) { }

func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) { }
func parser(_ parser: Foundation.XMLParser,
foundCharacters string: String) { }

func parser(_ parser: Foundation.XMLParser,
foundIgnorableWhitespace whitespaceString: String) { }
Expand All @@ -234,18 +240,21 @@ extension XMLParserDelegate {
foundProcessingInstructionWithTarget target: String,
data: String?) { }

func parser(_ parser: Foundation.XMLParser, foundComment comment: String) { }
func parser(_ parser: Foundation.XMLParser,
foundComment comment: String) { }

func parser(_ parser: Foundation.XMLParser, foundCDATA CDATABlock: Data) { }
func parser(_ parser: Foundation.XMLParser,
foundCDATA CDATABlock: Data) { }

func parser(_ parser: Foundation.XMLParser,
resolveExternalEntityName name: String,
systemID: String?) -> Data? { return nil }

func parser(_ parser: Foundation.XMLParser, parseErrorOccurred parseError: NSError) { }
func parser(_ parser: Foundation.XMLParser,
parseErrorOccurred parseError: Error) { }

func parser(_ parser: Foundation.XMLParser,
validationErrorOccurred validationError: NSError) { }
validationErrorOccurred validationError: Error) { }
}

#endif
Expand Down Expand Up @@ -360,6 +369,7 @@ class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
let root: XMLElement
var parentStack = Stack<XMLElement>()
let options: SWXMLHashOptions
var parsingError: ParsingError?

func parse(_ data: Data) -> XMLIndexer {
// clear any prior runs of parse... expected that this won't be necessary,
Expand All @@ -373,7 +383,11 @@ class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
parser.delegate = self
_ = parser.parse()

return XMLIndexer(root)
if options.detectParsingErrors, let err = parsingError {
return XMLIndexer.parsingError(err)
} else {
return XMLIndexer(root)
}
}

func parser(_ parser: Foundation.XMLParser,
Expand Down Expand Up @@ -410,6 +424,19 @@ class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
current.addText(cdataText)
}
}

func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
#if os(Linux)
if let err = parseError as? NSError {
parsingError = ParsingError(line: err.userInfo["NSXMLParserErrorLineNumber"] as? Int ?? 0,
column: err.userInfo["NSXMLParserErrorColumn"] as? Int ?? 0)
}
#else
let err = parseError as NSError
parsingError = ParsingError(line: err.userInfo["NSXMLParserErrorLineNumber"] as? Int ?? 0,
column: err.userInfo["NSXMLParserErrorColumn"] as? Int ?? 0)
#endif
}
}

/// Represents an indexed operation against a lazily parsed `XMLIndexer`
Expand Down Expand Up @@ -465,6 +492,11 @@ public class IndexOps {
}
}

public struct ParsingError: Error {
public let line: Int
public let column: Int
}

/// Error type that is thrown when an indexing or parsing operation fails.
public enum IndexingError: Error {
case attribute(attr: String)
Expand Down Expand Up @@ -510,6 +542,7 @@ public enum XMLIndexer {
case list([XMLElement])
case stream(IndexOps)
case xmlError(IndexingError)
case parsingError(ParsingError)

// swiftlint:disable identifier_name
// unavailable
Expand Down
27 changes: 26 additions & 1 deletion Tests/SWXMLHashTests/XMLParsingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import SWXMLHash
import XCTest

// swiftlint:disable line_length
// swiftlint:disable type_body_length

class XMLParsingTests: XCTestCase {
let xmlToParse = """
Expand Down Expand Up @@ -302,6 +303,29 @@ class XMLParsingTests: XCTestCase {
XCTAssertEqual(subIndexer.children[1].element?.text, "Fantasy")
XCTAssertEqual(subIndexer.children[2].element?.text, "5.95")
}

func testShouldThrowErrorForInvalidXML() {
let invalidXML = "<uh oh>what is this"
var err: ParsingError? = nil
let parser = SWXMLHash.config { config in
config.detectParsingErrors = true
}.parse(invalidXML)

switch parser {
case .parsingError(let error):
err = error
default:
err = nil
}

XCTAssertNotNil(err)

#if !os(Linux)
if err != nil {
XCTAssert(err!.line == 1)
}
#endif
}
}

extension XMLParsingTests {
Expand Down Expand Up @@ -329,7 +353,8 @@ extension XMLParsingTests {
("testShouldProvideAnErrorElementWhenIndexersDontMatch", testShouldProvideAnErrorElementWhenIndexersDontMatch),
("testShouldStillReturnErrorsWhenAccessingViaSubscripting", testShouldStillReturnErrorsWhenAccessingViaSubscripting),
("testShouldBeAbleToCreateASubIndexerFromFilter", testShouldBeAbleToCreateASubIndexerFromFilter),
("testShouldBeAbleToFilterOnIndexer", testShouldBeAbleToFilterOnIndexer)
("testShouldBeAbleToFilterOnIndexer", testShouldBeAbleToFilterOnIndexer),
("testShouldThrowErrorForInvalidXML", testShouldThrowErrorForInvalidXML)
]
}
}

0 comments on commit 17d992b

Please sign in to comment.