arrays - 将 XML 数据存储为数组,Swift

标签 arrays swift xml parsing struct

所以我正在开发一个项目,该项目将允许用户进行多个不同的测验。

我的 XML 在线托管,格式如下:

<questions>
    <question>
        <clue> sample clue 1 </clue>
        <correct_answer>2</correct_answer>
        <enumeration>1</enumeration>
        <info> sample info 1 </info>
        <location_clue>Sample locationClue (5,5)</location_clue>
        <option_a>Ans1</option_a>
        <option_b>Ans2</option_b>
        <option_c>Ans3</option_c>
    </question>
    <question>
        <clue> sample clue 2 </clue>
        <correct_answer>3</correct_answer>
        <enumeration>2</enumeration>
        <info> sample info 2 </info>
        <location_clue>Sample locationClue (4,2)</location_clue>
        <option_a>Ans1</option_a>
        <option_b>Ans2</option_b>
        <option_c>Ans3</option_c>
    </question>
</questions>

我的解析器启动如下所示:

if let urlString = URL(string: "realURL goes here.xml -- This has an actual url in my code obviously.")
    {

        if let parser = XMLParser(contentsOf: urlString)
        {
            parser.delegate = self
            parser.parse()
        }
    }

解析器DidStartElement:

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:])
{
    thisName = elementName

    if thisName == "hunt"
    {

    }
}

解析器发现字符:

func parser(_ parser: XMLParser, foundCharacters string: String)
{
    let data = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)


    if data.count != 0
    {
        switch thisName
        {
        case "clue": questionClue = data
            break
        case "info": questionInfo = data
            break
        case "location_clue": locationClue = data
            break
        case "option_a": questionAnswerA = data
            break
        case "option_b": questionAnswerB = data
            break
        case "option_c": questionAnswerC = data
        default:
            break

        }
    }

}

这是 HuntDetail.swift 类,它创建一个名为 QUIZ 的 scruct,该 scruct 内部当前有 4 个变量,question、answerA、answerB 和 answerC:

import Foundation

struct QUIZ {
    var question = ""
    var answerA = ""
    var answerB = ""
    var answerC = ""
}

本质上,该应用程序将允许用户进行多项选择测验。选择答案后,界面顶部的进度条将指示当前测验的进度。

我想知道是否可以将以下值存储在数组内:线索,信息,location_clue,option_a,b,c ...,从该数组我将开始制定实际的测验功能。

就目前而言,应用程序将仅显示前面提到的数据的最后一个元素。

我知道这很长,可能很难理解我想要做什么,但如果有人可以提供帮助,我将不胜感激。还应该指出的是,是的,我对 Swift 和整个 iOS 开发还相当陌生。

最佳答案

是的,您可以相当轻松地完成此操作,尽管我已经有一段时间没有使用 XMLParser 了。

注意:在下面的代码中,我已将您的QUIZ重命名为Question,因为该结构代表单个问题,而不是整个测验(问题列表)

因此,在解析每个项目时,您需要一个空数组:

var quiz = [Question]() // quiz is a list of questions. 

然后您想跟踪当前正在处理的问题

var currentQuestion: Question?

因此,每次开始和结束一个 Question 元素时,您就知道您已经完成了单个问题的解析,因此将其添加到列表中。

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {


    if elementName == "Question" {
        currentQuestion = Question()
    }
}

func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {

     if elementName == "Question", let question = currentQuestion {
          quiz.append(question)
     }
} 

所以您只是从上到下解析 XML 文档。一旦遇到开始问题元素,创建一个问题对象,填写其余内容,然后当遇到结束问题元素时,将当前问题添加到列表中。

在文档末尾,您的测验变量应包含文档中的所有问题。

编辑:

所以我必须做一些改变,找到的角色可以分为几部分,所以我们需要跟踪它。

这是一个工作 Playground ,它返回 2 个问题(基于上面的示例 XML)。答案 C 始终为空,看起来这是因为换行符,并且修剪正在剪切文本,您可能需要删除换行符,然后只修剪空格,但此代码将为您提供一个良好的开始。

import Foundation

let xmlData = """
<questions>
    <question>
        <clue> sample clue 1 </clue>
        <correct_answer>2</correct_answer>
        <enumeration>1</enumeration>
        <info> sample info 1 </info>
        <location_clue>Sample locationClue (5,5)</location_clue>
        <option_a>Ans1</option_a>
        <option_b>Ans2</option_b>
        <option_c>Ans3</option_c>
    </question>
    <question>
        <clue> sample clue 2 </clue>
        <correct_answer>3</correct_answer>
        <enumeration>2</enumeration>
        <info> sample info 2 </info>
        <location_clue>Sample locationClue (4,2)</location_clue>
        <option_a>Ans1</option_a>
        <option_b>Ans2</option_b>
        <option_c>Ans3</option_c>
    </question>
</questions>
""".data(using: .utf8)!

struct Question {
    var question: String?
    var clue: String?
    var info: String?
    var locationClue: String?
    var answerA: String?
    var answerB: String?
    var answerC: String?
}

class MySuperXMLParser: NSObject, XMLParserDelegate {
    private let parser: XMLParser
    private var quiz = [Question]()
    private var currentQuestion: Question?
    private var currentElement: String?
    private var foundCharacters = ""

    init(data: Data) {
        parser = XMLParser(data: data)
        super.init()
        parser.delegate = self
    }

    func parse() -> [Question] {
        parser.parse()
        return quiz
    }

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {

        if elementName == "question" {
            currentQuestion = Question()
        }

        print("Started element: \(elementName)")
        currentElement = elementName
    }

    func parser(_ parser: XMLParser, foundCharacters string: String) {
        print("found characters: \(string)")
        foundCharacters += string
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        print("ended element: \(elementName), text = \(foundCharacters)")

        let text = foundCharacters.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)

        switch currentElement
        {
        case "clue":
            currentQuestion?.clue = text
            break
        case "info":
            currentQuestion?.info = text
            break
        case "location_clue":
            currentQuestion?.locationClue = text
            break
        case "option_a":
            currentQuestion?.answerA = text
            break
        case "option_b":
            currentQuestion?.answerB = text
            break
        case "option_c": currentQuestion?.answerC = text
        default:
            break
        }

        foundCharacters = ""

        if elementName == "question", let question = currentQuestion {
            print("adding question: \(question)")
            quiz.append(question)
        }
    }
}

let parser = MySuperXMLParser(data: xmlData)
let quiz = parser.parse()
print(quiz.count, quiz)

关于arrays - 将 XML 数据存储为数组,Swift,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50950450/

相关文章:

Ruby - 如果数组中的元素包含某个字符,则返回关联的元素

c - 与数组访问混淆

c - 列出c目录中的文件

swift - 性能:Array.removeAll 与 `= []`

xml - 使用 Go 解码 XML : How to find attributes with the same value?

c# - 使用 XmlNamespaceManager 将命名空间添加到 XmlDocument

javascript - 如何按 element.name 对 javascript 对象数组进行排序

swift - Swift 中的 dispatch after 示例

ios - UIAlertController 的 NSNotification

java - 如何在 Java 中使用 xpath 查找 xml 中的节点值或属性并将其替换为另一个值?