2017-02-01 22 views
12

W iOS, chciałbym przeanalizować strumień XML z serwera podczas pobierania. Nie musi czekać, aż serwer skończy tworzenie XML i pobieranie się zakończy. Serwer buduje XML w "porcjach" i wysyła go bezpośrednio do klienta. W mojej aplikacji mam UITableView, która powinna natychmiast pokazać elementy XML, jak tylko otrzymałem je z serwera.Parsowanie pliku XML w porcjach bez oczekiwania na pełne pobranie w Swift

Próbowałem go z konstruktorem XMLParser(contentsOf: URL), ale najpierw pobiera cały plik XML, a następnie analizuje go. Istnieje inny konstruktor: XMLParser(stream: InputStream), ale nie wiem, jak uzyskać InputStream od URLConnection. Jedyne, co znalazłem, to this question, które ma prawie 5 lat i nie mogłem zrozumieć, jak to zrobić w Swift 3, jeśli nawet działa.

Inną rzeczą, której próbowałem, był przez Libxml2, ale mam problemy z używaniem pamięci RAM lub inne rzeczy (zobacz mój old question).

W jaki sposób mogę przeanalizować strumień XML w porcjach, bez konieczności oczekiwania na pełne pobranie w Swift?

W Androidzie, chciałbym użyć XMLPullParser oraz:

URL url = new URL(urlString); 
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 
connection.setRequestMethod("GET"); 
connection.setDoInput(true); 
InputStream inputStream = connection.getInputStream(); 

XmlPullParserFactory xmlFactoryObject = XmlPullParserFactory.newInstance(); 
XmlPullParser pullParser = xmlFactoryObject.newPullParser(); 
pullParser.setInput(inputStream, null); 
// here comes the actual parsing 

Odpowiedz

2

Być może klient/serwer ma problem z niektórymi plikami cookie sesji? Spróbuj usunąć je po każdym żądaniu:

// Loops through each of the cookies and deletes them. 
let cookieStore = HTTPCookieStorage.shared 
for cookie in cookieStore.cookies ?? [] { 
    cookieStore.deleteCookie(cookie) 
} 
1

Poniższa rozwiązanie może pomóc. Zbadałem Twoje pytanie i odniosłem sukces, co w rezultacie daje poniżej. Ale nie mogę przetestować poniższego rozwiązania braku API. Wypróbuj dostępne punkty końcowe. Mam nadzieję, że ci to pomoże.

func xmlStream() { 
     let task = 
      URLSession.shared.streamTask(withHostName: "chat.example.com", port: 5555)               
     task.readData(ofMinLength: 16384, maxLength: 65536, timeout: 30.0) { (data, eof, error) in 

      let parser = XMLParser(data: data!) 
      parser.delegate = self 
      if parser.parse() { 
       //print result 
      } 

     } 
     task.resume() 

    } 

//XML parser methods 

Ponadto this mogą Cię.

+0

Próbowałem Twojego rozwiązania, ale dane są zerowe. Z jakiego portu mam korzystać? 443 dla HTTPS? Ponieważ mój punkt końcowy używa protokołu HTTPS, adres URL wygląda następująco: 'https: //www.example.com/api/query.php? Title = Test' – ElegyD

3

Istnieją zasadniczo dwa typy analizatorów składni: SAX i DOM. Zgodnie z twoim wymaganiem, gdy aktualizujesz UITableView, gdy tylko otrzymasz element XML, jest całkowicie jasne, że będziesz musiał używać tylko parsera SAX. (Analizator DOM przeanalizował cały dokument i zbudował reprezentację w pamięci, w której można wyszukiwać różne elementy).

Istnieją dwa popularne parser SAX dla iOS, jak wymienione poniżej:

1) NSXMLParser (ten został przemianowany na XMLParser) - włączone domyślnie z iPhone SDK.

2) libxml2 - jest biblioteką Open Source, który jest domyślnie z iPhone SDK

Apple wykonane doskonałą code przykładowy nazwie XMLPerformance, który pozwala porównać czas potrzebny do analizowania ~ 900KB XML dokument zawierający 300 najlepszych utworów iTunes z interfejsami API NSXML i libxml2.

Ok, to wykres, który przedstawia wykorzystanie pamięci pików parser (ten uzyskano poprzez uruchamianie różnych sposobów przy użyciu narzędzia Allocations Object)

a busy cat

danych pokazuje wyraźnie, że metoda SAX libxml2 (co już próbowałeś) jest najlepszą opcją jeśli chodzi o wykorzystanie pamięci szczytowej.

Z drugiej strony wdrożenie XMLParser jest dość łatwe. Poniżej znajduje się przykładowy fragment, którego próbowałem użyć.

W swojej deklaracji klasy, wdrażania Protokołu XMLParserDelegate

class ViewController: UIViewController,XMLParserDelegate { 

override func viewDidLoad() { 
    super.viewDidLoad() 

skorzystać z poniższego sposobu zainicjowania parser:

func xmlParser() 
{ 
    posts = [] 
    parser = XMLParser(contentsOf:(NSURL(string:"http://images.apple.com/main/rss/hotnews/hotnews.rss"))! as URL)! 
    parser.delegate = self 
    parser.parse() 
} 

Po inicjalizacji delegata poniżej metod delegata dostanie nazwie jak i kiedy potrzebne:

func parserDidStartDocument(_ parser: XMLParser) { 
    print("started") 
} 

func parserDidEndDocument(_ parser: XMLParser) { 
    print("ended") 
} 

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

func parser(_ parser: XMLParser, foundCharacters string: String) { 
    print("foundCharacters") 
} 

Jest jeszcze jeden delegat również metody, do których możesz zadzwonić zgodnie z własnymi wymaganiami.

+0

Przepraszam, ale wyjaśniłem na swoim pytaniu, że już wypróbowałem to z konstruktor XMLParser (contentsOf: URL), ale nie działa. Najpierw pobiera cały plik XML ze strony internetowej, a następnie analizuje go. – ElegyD