2016-12-14 7 views
7

में 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() पर दुर्घटनाग्रस्त हो जाता है हालांकि यह मुख्य धागे में है।

------

किसी भी मदद की अत्यधिक सराहना की जाएगी।

उत्तर

0

एक साधारण ऑफ-बाय-वन त्रुटि की तरह लगता है।सी में गुण सरणी पुनरावृति करने के लिए, मैं की तरह कुछ लिखते हैं:

for (int i = 0; i < nb_attributes; i++) 

लेकिन आप closed range operator जो ऊपरी बाध्य शामिल उपयोग कर रहे हैं:

for i in 0...Int(nb_attributes) 

तो तुम half-open range operator बजाय का उपयोग करना चाहिए:

for i in 0..<Int(nb_attributes) 

वैसे, libxml2 भी एक pull parser interface के बाद सी # के XmlTextReader मॉडलिंग की है जो सैक्स पार्सर की तुलना में उपयोग करना बहुत आसान है।

+0

हाँ, यह एक-एक-एक त्रुटि थी। बहुत धन्यवाद कि आपको यह मिला। लेकिन अब मुझे एक और समस्या है। मैंने अपना प्रश्न अपडेट कर लिया है। शायद आप इसके लिए एक समाधान जानते हैं? या हो सकता है कि आपके पास एक ट्यूटोरियल है कि IOS (स्विफ्ट (3) या Obj-C) के लिए XmlTextReader का उपयोग कैसे करें? – ElegyD

+0

@ElegyD यदि आपके पास कोई अलग समस्या है, तो एक अलग सवाल पूछना बेहतर है। – nwellnhof