SwiftでXML解析を行うのは初めてで、このコードは SwiftのURLからXMLを解析する で見つかりましたが、試してみるとEXC_BAD_INSTRUCTION
エラーが発生します。コードを実行します。エラーの説明は次のとおりです。fatal error: unexpectedly found nil while unwrapping an Optional value
これは私の単純なXMLファイルです。
<xml>
<book>
<title>Book Title</title>
<author>Book Author</author>
</book>
</xml>
次のコードは、XMLParser
オブジェクトを作成し、ドキュメントにあるXMLファイルを解析します。
// get xml file path from Documents and parse
let filePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last?.appendingPathComponent("example.xml")
let parser = XMLParser(contentsOf: filePath!)
parser?.delegate = self
if (parser?.parse())! {
print(self.results)
}
ここでは、XMLParserDelegate
メソッドを実装し、辞書を定義します。
// a few constants that identify what element names we're looking for inside the XML
let recordKey = "book"
let dictionaryKeys = ["title","author"]
// a few variables to hold the results as we parse the XML
var results: [[String: String]]! // the whole array of dictionaries
var currentDictionary: [String: String]! // the current dictionary
var currentValue: String? // the current value for one of the keys in the dictionary
// start element
//
// - If we're starting a "record" create the dictionary that will hold the results
// - If we're starting one of our dictionary keys, initialize `currentValue` (otherwise leave `nil`)
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
if elementName == recordKey {
currentDictionary = [String : String]()
} else if dictionaryKeys.contains(elementName) {
currentValue = String()
}
}
// found characters
//
// - If this is an element we care about, append those characters.
// - If `currentValue` still `nil`, then do nothing.
func parser(_ parser: XMLParser, foundCharacters string: String) {
currentValue? += string
}
// end element
//
// - If we're at the end of the whole dictionary, then save that dictionary in our array
// - If we're at the end of an element that belongs in the dictionary, then save that value in the dictionary
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == recordKey {
results.append(currentDictionary)
currentDictionary = nil
} else if dictionaryKeys.contains(elementName) {
currentDictionary[elementName] = currentValue
currentValue = nil
}
}
// Just in case, if there's an error, report it. (We don't want to fly blind here.)
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print(parseError)
currentValue = nil
currentDictionary = nil
results = nil
}
エラーは、didEndElement
がcurrentDictionary
ディクショナリに追加されたときにresults
メソッドで見つかります。
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == recordKey {
results.append(currentDictionary) // Line with Error
currentDictionary = nil
} else if dictionaryKeys.contains(elementName) {
currentDictionary[elementName] = currentValue
currentValue = nil
}
}
この問題の解決にご協力ください。 SwiftのURLからXMLを解析する で提供されているものとまったく同じコードを使用していますが、問題はないようです。私は何か間違ったことをしていますか?
コードが実際にresults
を初期化することはないため、初めてコードを使用しようとすると、nil
オプション値を強制的にアンラップしようとします。良くないね。そして、それを暗黙的にアンラップされたオプションとして宣言する理由はありません。
変更する必要があります:
_var results: [[String: String]]!
_
に:
_var results = [[String: String]]()
_
また、次の行を削除する必要があります。
_results = nil
_
parser(_:parseErrorOccurred:)
メソッドから。
results
をオプションにしたい場合は、コードに次の変更を加えることができます。
results
の宣言を次のように変更します。
_var results: [[String: String]]? = [[String: String]]()
_
変化する:
_results.append(currentDictionary)
_
に:
_results?.append(currentDictionary)
_
そして、parser(_:parseErrorOccurred:)
に_results = nil
_行を残します。
rmaddyは問題を正しく診断しました(+1)。 results
は初期化されませんでした。
コードはほとんどそのままにしておくことをお勧めしますが、次のようにparserDidStartDocument
を初期化する results
メソッドを追加するだけです。
func parserDidStartDocument(_ parser: XMLParser) {
results = [[:]]
}