尝试使用 swift 将 XML 从 URL 解析为 PickerView
Trying to parse XML from URL to PickerView with swift
我正在尝试使用 XMLParser 解析 XML 并将其放在 PickerView 上。这是我的代码:
主要代码:
import UIKit
class ViewController: UIViewController, XMLParserDelegate {
@IBOutlet weak var containerView: UIView!
@IBOutlet weak var pickerView: UIPickerView!
@IBOutlet weak var Image: UIImageView!
@IBOutlet weak var Button: UIButton!
var arrayCategorias = [Categories]()
var parser = XMLParser()
override func viewDidLoad() {
super.viewDidLoad()
let urlString = URL(string: "http://thecatapi.com/api/categories/list")
self.parser = XMLParser(contentsOf: urlString!)!
let success:Bool = self.parser.parse()
parser.delegate = self
if success {
print("success")
} else {
print("parse failure!")
}
print(arrayCategorias.count)
}
@IBAction func botonPulsado(_ sender: Any) {
}
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if(elementName=="category")
{
let categoria = Categories()
for string in attributeDict {
let strvalue = string.value as NSString
switch string.key {
case "id":
categoria.id = strvalue.integerValue
break
case "name":
categoria.name = strvalue as String
break
default:
break
}
}
arrayCategorias.append(categoria)
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print("failure error: ", parseError)
}
}
}
Class 类别代码:
import Foundation
class Categories{
var id: Int = 0
var name: String = ""
}
我不知道到底是什么问题,但是当我尝试读取 arrayCategorias()
只是为了查看是否有任何数据时,它只显示 0。我发现错误的地方是显示给我:"parse failure".
如果有人能帮助我,我只是一个android程序员,我不知道为什么它不起作用。
我需要帮助将数据放在 PickerView 上。
有一大堆问题:
您在解析后设置了 delegate
,而您必须在调用 parse()
之前执行此操作。
您在 didStartElement
中实现了一些委托方法。这些需要是您的解析器委托的顶级方法。
不相关的小问题,但是您的 class 名称 Categories
不太正确,因为它代表一个“类别”,而不是很多。所以我将其重命名为 Category
。 (我个人也会将其设为 struct
,但这取决于您。)
您的 id
和 name
值不是 category
元素的属性。它们是它们自己的元素,因此您必须单独解析它们(使用 foundCharacters
并在 category
的 didEndElement
上构建 Category
对象。
类别的 id
似乎是一个整数,所以我将 Category
的 id
设为 Int
。
您正在从主线程调用 XMLParser
的 parse()
,而后者正在使用 URL。这是不可取的,因为您在主线程执行请求时阻塞了主线程。就个人而言,我会使用 URLSession
异步请求数据,在后台队列中处理它,并且只将模型对象的最终更新和 UI 更新分派到主队列。
就个人而言,我自己会采用异步模式与 @escaping
完成处理程序,以帮助隔离 UI 我在解析后触发的更新与解析本身
风格问题,但我不会将所有这些 XMLParserDelegate
代码都放在视图控制器中。至少,把它放在一个扩展中,这样它就可以很好地组织起来。更好的是,本着 single responsibility principle 的精神定义一个独立的解析器委托对象,并确保您不会在 UI 可能引用它时意外更新视图控制器模型对象。它更安全地确保线程安全并使您的代码更好地封装。
将所有这些放在一起,您可以执行以下操作:
struct Category {
let id: Int
let name: String
}
class ViewController: UIViewController {
@IBOutlet weak var pickerView: UIPickerView!
var categories = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
startRequestAndParse() { categories, error in
guard let categories = categories, error == nil else {
print(error?.localizedDescription ?? "Unknown error")
return
}
// if you got here, everything is OK, so update model and UI on main thread
DispatchQueue.main.async {
self.categories = categories
print(self.categories)
// trigger whatever UI update you want here, too;
self.pickerView.reloadAllComponents()
}
}
}
/// Initiate request from server and parse results
///
/// - Parameters:
/// - completion: This is called when the request/parsing is done. This may be called
/// on background thread. If parsing failed, the array of categories
/// will be `nil` and we should have `error`.
/// - categories: First parameter of the `completion` closure is the array of `Category` objects, or `nil` on error.
/// - error: Second parameter of the `completion` closure is the resulting `Error`, if any.
private func startRequestAndParse(completion: @escaping (_ categories: [Category]?, _ error: Error?) -> Void) {
let url = URL(string: "http://thecatapi.com/api/categories/list")!
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
completion(nil, error)
return
}
// ok, now parse
let parser = XMLParser(data: data)
let delegate = ParserDelegate()
parser.delegate = delegate
parser.parse()
completion(delegate.categories, parser.parserError)
}
task.resume()
}
}
// this assumes you set the picker's `delegate` to be the view controller (either in IB or programmatically in `viewDidLoad`
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return categories[row].name
}
}
// this assumes you set the picker's `dataSource` to be the view controller (either in IB or programmatically in `viewDidLoad`
extension ViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return categories.count
}
}
/// Parser delegate for categories
class ParserDelegate: NSObject, XMLParserDelegate {
private var id: Int?
private var name: String?
private var value: String?
var categories: [Category]?
// initialize `categories`
func parserDidStartDocument(_ parser: XMLParser) {
categories = []
}
// if `id` or `name`, initialize `value` so we'll capture the appropriate value
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if elementName == "id" || elementName == "name" {
value = ""
}
}
// if `value` is not `nil`, go ahead and concatenate the additional characters
func parser(_ parser: XMLParser, foundCharacters string: String) {
value? += string
}
// if `id` or `name`, update the appropriate property
// if `category`, build a `Category` object and add it to our array
// regardless, reset `value` to `nil`
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
switch elementName {
case "id":
if let value = value {
id = Int(value)
}
case "name":
name = value
case "category":
if let id = self.id, let name = self.name {
categories!.append(Category(id: id, name: name))
}
id = nil
name = nil
default:
()
}
value = nil
}
// if any error, reset `categories` so caller knows there was an issue
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
categories = nil
}
}
我正在尝试使用 XMLParser 解析 XML 并将其放在 PickerView 上。这是我的代码:
主要代码:
import UIKit
class ViewController: UIViewController, XMLParserDelegate {
@IBOutlet weak var containerView: UIView!
@IBOutlet weak var pickerView: UIPickerView!
@IBOutlet weak var Image: UIImageView!
@IBOutlet weak var Button: UIButton!
var arrayCategorias = [Categories]()
var parser = XMLParser()
override func viewDidLoad() {
super.viewDidLoad()
let urlString = URL(string: "http://thecatapi.com/api/categories/list")
self.parser = XMLParser(contentsOf: urlString!)!
let success:Bool = self.parser.parse()
parser.delegate = self
if success {
print("success")
} else {
print("parse failure!")
}
print(arrayCategorias.count)
}
@IBAction func botonPulsado(_ sender: Any) {
}
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if(elementName=="category")
{
let categoria = Categories()
for string in attributeDict {
let strvalue = string.value as NSString
switch string.key {
case "id":
categoria.id = strvalue.integerValue
break
case "name":
categoria.name = strvalue as String
break
default:
break
}
}
arrayCategorias.append(categoria)
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print("failure error: ", parseError)
}
}
}
Class 类别代码:
import Foundation
class Categories{
var id: Int = 0
var name: String = ""
}
我不知道到底是什么问题,但是当我尝试读取 arrayCategorias()
只是为了查看是否有任何数据时,它只显示 0。我发现错误的地方是显示给我:"parse failure".
如果有人能帮助我,我只是一个android程序员,我不知道为什么它不起作用。
我需要帮助将数据放在 PickerView 上。
有一大堆问题:
您在解析后设置了
delegate
,而您必须在调用parse()
之前执行此操作。您在
didStartElement
中实现了一些委托方法。这些需要是您的解析器委托的顶级方法。不相关的小问题,但是您的 class 名称
Categories
不太正确,因为它代表一个“类别”,而不是很多。所以我将其重命名为Category
。 (我个人也会将其设为struct
,但这取决于您。)您的
id
和name
值不是category
元素的属性。它们是它们自己的元素,因此您必须单独解析它们(使用foundCharacters
并在category
的didEndElement
上构建Category
对象。类别的
id
似乎是一个整数,所以我将Category
的id
设为Int
。您正在从主线程调用
XMLParser
的parse()
,而后者正在使用 URL。这是不可取的,因为您在主线程执行请求时阻塞了主线程。就个人而言,我会使用URLSession
异步请求数据,在后台队列中处理它,并且只将模型对象的最终更新和 UI 更新分派到主队列。就个人而言,我自己会采用异步模式与
@escaping
完成处理程序,以帮助隔离 UI 我在解析后触发的更新与解析本身风格问题,但我不会将所有这些
XMLParserDelegate
代码都放在视图控制器中。至少,把它放在一个扩展中,这样它就可以很好地组织起来。更好的是,本着 single responsibility principle 的精神定义一个独立的解析器委托对象,并确保您不会在 UI 可能引用它时意外更新视图控制器模型对象。它更安全地确保线程安全并使您的代码更好地封装。
将所有这些放在一起,您可以执行以下操作:
struct Category {
let id: Int
let name: String
}
class ViewController: UIViewController {
@IBOutlet weak var pickerView: UIPickerView!
var categories = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
startRequestAndParse() { categories, error in
guard let categories = categories, error == nil else {
print(error?.localizedDescription ?? "Unknown error")
return
}
// if you got here, everything is OK, so update model and UI on main thread
DispatchQueue.main.async {
self.categories = categories
print(self.categories)
// trigger whatever UI update you want here, too;
self.pickerView.reloadAllComponents()
}
}
}
/// Initiate request from server and parse results
///
/// - Parameters:
/// - completion: This is called when the request/parsing is done. This may be called
/// on background thread. If parsing failed, the array of categories
/// will be `nil` and we should have `error`.
/// - categories: First parameter of the `completion` closure is the array of `Category` objects, or `nil` on error.
/// - error: Second parameter of the `completion` closure is the resulting `Error`, if any.
private func startRequestAndParse(completion: @escaping (_ categories: [Category]?, _ error: Error?) -> Void) {
let url = URL(string: "http://thecatapi.com/api/categories/list")!
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
completion(nil, error)
return
}
// ok, now parse
let parser = XMLParser(data: data)
let delegate = ParserDelegate()
parser.delegate = delegate
parser.parse()
completion(delegate.categories, parser.parserError)
}
task.resume()
}
}
// this assumes you set the picker's `delegate` to be the view controller (either in IB or programmatically in `viewDidLoad`
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return categories[row].name
}
}
// this assumes you set the picker's `dataSource` to be the view controller (either in IB or programmatically in `viewDidLoad`
extension ViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return categories.count
}
}
/// Parser delegate for categories
class ParserDelegate: NSObject, XMLParserDelegate {
private var id: Int?
private var name: String?
private var value: String?
var categories: [Category]?
// initialize `categories`
func parserDidStartDocument(_ parser: XMLParser) {
categories = []
}
// if `id` or `name`, initialize `value` so we'll capture the appropriate value
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if elementName == "id" || elementName == "name" {
value = ""
}
}
// if `value` is not `nil`, go ahead and concatenate the additional characters
func parser(_ parser: XMLParser, foundCharacters string: String) {
value? += string
}
// if `id` or `name`, update the appropriate property
// if `category`, build a `Category` object and add it to our array
// regardless, reset `value` to `nil`
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
switch elementName {
case "id":
if let value = value {
id = Int(value)
}
case "name":
name = value
case "category":
if let id = self.id, let name = self.name {
categories!.append(Category(id: id, name: name))
}
id = nil
name = nil
default:
()
}
value = nil
}
// if any error, reset `categories` so caller knows there was an issue
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
categories = nil
}
}