में libxml2 साथ डाउनलोड करने मैं स्विफ्ट 3.
मैं आईओएस में एंड्रॉयड से XMLPullParser
की तरह कुछ चाहता हूँ में libxml2 से SAX पार्सर के साथ एक समस्या है। जो सर्वर से एक्सएमएल डाउनलोड करता है और इसे डाउनलोड करते समय स्ट्रीम को पार करता है।पार्सिंग बड़े XML जबकि स्विफ्ट 3
मेरे एक्सएमएल इस तरह दिखता है:
<?xml version="1.0" encoding="UTF-8" ?>
<ResultList id="12345678-0" platforms="A;B;C;D;E">
<Book id="1111111111" author="Author A" title="Title A" price="9.95" ... />
<Book id="1111111112" author="Author B" title="Title B" price="2.00" ... />
<Book id="1111111113" author="Author C" title="Title C" price="5.00" ... />
<ResultInfo bookcount="3" />
</ResultList>
तो सभी डेटा बच्चे नोड्स के बजाय विशेषताओं में संग्रहित है।
मैं अपने आप को द्वारा निम्नलिखित वर्ग ज्यादातर इन उदाहरणों द्वारा आधारित कर दिया है:
XMLPerformance, XMLPerformance-Swift और iOS-XML-Streaming
import Foundation
class LibXMLParser: NSObject, URLSessionDataDelegate {
var url: URL?
var delegate: LibXMLParserDelegate?
var done = false
var context: xmlParserCtxtPtr?
var simpleSAXHandlerStruct: xmlSAXHandler = {
var handler = xmlSAXHandler()
handler.initialized = XML_SAX2_MAGIC
handler.startElementNs = startElementSAX
handler.endElementNs = endElementSAX
handler.characters = charactersFoundSAX
//handler.error = errorEncounteredSAX
return handler
}()
init(url: URL) {
super.init()
self.url = url
}
func parse() {
self.done = false
let session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue.main)
let dataTask = session.dataTask(with: URLRequest(url: url!))
dataTask.resume()
self.context = xmlCreatePushParserCtxt(&simpleSAXHandlerStruct, Unmanaged.passUnretained(self).toOpaque(), nil, 0, nil)
self.delegate?.parserDidStartDocument()
repeat {
RunLoop.current.run(mode: .defaultRunLoopMode, before: Date.distantFuture)
} while !self.done
xmlFreeParserCtxt(self.context)
self.delegate?.parserDidEndDocument()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print("Did receive data")
data.withUnsafeBytes { (bytes: UnsafePointer<CChar>) -> Void in
xmlParseChunk(self.context, bytes, CInt(data.count), 0)
}
}
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
xmlParseChunk(self.context, nil, 0, 1)
self.done = true
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
self.done = true
//self.delegate?.parserErrorOccurred(error)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
self.done = true
//self.delegate?.parserErrorOccurred(error)
}
}
private func startElementSAX(_ ctx: UnsafeMutableRawPointer?, name: UnsafePointer<xmlChar>?, prefix: UnsafePointer<xmlChar>?, URI: UnsafePointer<xmlChar>?, nb_namespaces: CInt, namespaces: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?, nb_attributes: CInt, nb_defaulted: CInt, attributes: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?) {
let parser = Unmanaged<LibXMLParser>.fromOpaque(ctx!).takeUnretainedValue()
parser.delegate?.parserDidStartElement(String(cString: name!), nb_attributes: nb_attributes, attributes: attributes)
}
private func endElementSAX(_ ctx: UnsafeMutableRawPointer?, name: UnsafePointer<xmlChar>?,
prefix: UnsafePointer<xmlChar>?,
URI: UnsafePointer<xmlChar>?) {
let parser = Unmanaged<LibXMLParser>.fromOpaque(ctx!).takeUnretainedValue()
parser.delegate?.parserDidEndElement(String(cString: name!))
}
private func charactersFoundSAX(_ ctx: UnsafeMutableRawPointer?, ch: UnsafePointer<xmlChar>?, len: CInt) {
let parser = Unmanaged<LibXMLParser>.fromOpaque(ctx!).takeUnretainedValue()
parser.delegate?.parserFoundCharacters(String(cString: ch!))
}
मैं एक URL
के साथ इस वर्ग को प्रारंभ। जब मैं parse()
पर कॉल करता हूं तो यह विधि को ओवरराइड करने के लिए स्वयं को एक प्रतिनिधि के साथ URLSession
और URLSessionDataTask
बनाता है। उसके बाद मैं xmlParserCtxtPtr
और लूप को डेटाटास्क समाप्त होने तक लूप बना देता हूं।
जब यह डेटा प्राप्त करता है तो मैं इसे xmlParseChunk
विधि और startElementSAX
के साथ पार्स करता हूं जिसे मैंने व्यू कंट्रोलर क्लास से सेट किया है। (मुझे केवल तत्व का नाम, गुणों और विशेषताओं की संख्या चाहिए।)
अभी तक इतना अच्छा है।
मेरी ViewController (UITableViewController) में मैं निम्नलिखित कोड है:
func downloadBooksLibXML() {
print("Downloading…")
UIApplication.shared.isNetworkActivityIndicatorVisible = true
DispatchQueue.global().async {
print("Setting up parser")
let parser = LibXMLParser(url: URL(string: self.baseUrl + self.parameters!)!)
parser.delegate = self
parser.parse()
}
}
func parserDidStartDocument() {
}
func parserDidEndDocument() {
DispatchQueue.main.sync {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
self.isDone = true
print("Finished")
}
}
func parserDidStartElement(_ elementName: String, nb_attributes: CInt, attributes: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?) {
print(elementName)
switch elementName {
case "Book":
DispatchQueue.main.async {
let book = self.buildBook(nb_attributes: nb_attributes, attributes: attributes)
self.books.append(book)
self.tableView.beginUpdates()
self.tableView.insertRows(at: [IndexPath(row: self.books.count - 1, section: 0)], with: .automatic)
self.tableView.endUpdates()
self.navigationItem.title = String(format: NSLocalizedString("books_found", comment: "Books found"), "\(self.books.count)")
}
case "ResultList":
break
case "ResultInfo":
break
default:
break
}
}
func buildBook(nb_attributes: CInt, attributes: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?) -> Book {
let fields = 5 /* (localname/prefix/URI/value/end) */
let book = Book()
for i in 0..<Int(nb_attributes) {
if let localname = attributes?[i * fields + 0],
//let prefix = attributes?[i * fields + 1],
//let URI = attributes?[i * fields + 2],
let value_start = attributes?[i * fields + 3],
let value_end = attributes?[i * fields + 4] {
let localnameString = String(cString: localname)
let string_start = String(cString: value_start)
let string_end = String(cString: value_end)
let diff = string_start.characters.count - string_end.characters.count
if diff > 0 {
let value = string_start.substring(to: string_start.index(string_start.startIndex, offsetBy: diff))
book.setValue(value, forKey: localnameString)
}
}
}
return book
}
func parserDidEndElement(_ elementName: String) {
}
func parserFoundCharacters(_ string: String) {
}
func parserErrorOccurred(_ parseError: Error?) {
}
------
अद्यतन
तो समस्या विशेषता मान हो रही द्वारा निर्धारित किया गया है nwellnhof से जवाब। मैंने अपना कोड ऊपर एक बेहतर कोड में अपडेट किया है। यह अब सभी विशेषताओं के माध्यम से फिर से शुरू नहीं होता है। अब मेरी नई समस्या:
मैंने XML गुणों की Book
ऑब्जेक्ट प्राप्त करने के लिए विधि buildBook
बनाई है। मैंने अधिकतर What is the right way to get attribute value in libXML sax parser (C++)? से स्विफ्ट में विधि का अनुवाद किया है और मेरी पुस्तक ऑब्जेक्ट के गुण सेट करने के लिए setValue(value: Any?, forKey: String)
का उपयोग किया है।
लेकिन अब मेरी समस्या यह है कि यह तालिका दृश्य को अद्यतन नहीं करता है। मैंने को DispatchQueue.global().sync
का उपयोग करके पृष्ठभूमि थ्रेड में सिंक्रोनस विधि को निष्पादित करने का प्रयास किया है और DispatchQueue.main.async
का उपयोग करके एक असीमित मुख्य थ्रेड में तालिका दृश्य अपडेट करें। लेकिन फिर यह tableView.endUpdates()
पर दुर्घटनाग्रस्त हो जाता है हालांकि यह मुख्य धागे में है।
------
किसी भी मदद की अत्यधिक सराहना की जाएगी।
हाँ, यह एक-एक-एक त्रुटि थी। बहुत धन्यवाद कि आपको यह मिला। लेकिन अब मुझे एक और समस्या है। मैंने अपना प्रश्न अपडेट कर लिया है। शायद आप इसके लिए एक समाधान जानते हैं? या हो सकता है कि आपके पास एक ट्यूटोरियल है कि IOS (स्विफ्ट (3) या Obj-C) के लिए XmlTextReader का उपयोग कैसे करें? – ElegyD
@ElegyD यदि आपके पास कोई अलग समस्या है, तो एक अलग सवाल पूछना बेहतर है। – nwellnhof