Praca z XML do liczenia obiektów. Parsowanie danych XML

XML Extensible Markup Language to zestaw reguł kodowania dokumentów w formie czytelnej maszynowo. XML to popularny format wymiany danych w Internecie. Witryny, które często aktualizują swoją treść, takie jak witryny z wiadomościami lub blogi, często udostępniają kanał XML, dzięki czemu programy zewnętrzne są świadome zmian w treści. Wysyłanie i analizowanie danych XML jest częstym zadaniem aplikacji podłączonych do sieci. W tej lekcji wyjaśniono, jak analizować dokumenty XML i wykorzystywać zawarte w nich dane.

Wybór parsera

Analiza kanału

Pierwszym krokiem w analizie pliku danych jest podjęcie decyzji, które pola danych Cię interesują. Parser wyodrębnia podane pola i ignoruje wszystko inne.

Oto fragment kanału, który będzie eksplorowany w przykładowej aplikacji. Każdy post na StackOverflow.com pojawia się w kanale jako tag wejściowy, który zawiera kilka podtagów:

najnowsze pytania oznaczone tagiem Android — Przepełnienie stosu ... ... http://stackoverflow.com/q/9439999 0 Gdzie jest mój plik danych? klif2310 http://stackoverflow.com/users/1128925 2012-02-25T00:30:54Z 2012-02-25T00:30:54Z

Mam aplikację, która wymaga pliku danych...

... ...

Przykładowa aplikacja pobiera dane ze znacznika wpisu oraz jego tagów podrzędnych tytuł, łącze i podsumowanie.

Tworzenie instancji parsera

Następnym krokiem jest utworzenie instancji parsera i rozpoczęcie procesu analizowania. Ten fragment kodu inicjuje analizator składni, aby nie obsługiwał przestrzeni nazw i używał dostarczonego strumienia wejściowego jako danych wejściowych. Proces parsowania rozpoczyna się od wywołania metody nextTag() i wywołania metody readFeed(), która pobiera i przetwarza dane, którymi interesuje się aplikacja:

Klasa publiczna StackOverflowXmlParser ( // Nie używamy przestrzeni nazw private static final String ns = null; public List parse(InputStream in) zgłasza XmlPullParserException, IOException ( try ( XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES , false); parser.setInput(in, null); parser.nextTag(); zwróć readFeed(parser) w końcu (in.close(); ) ...)

Odejmij kanał

Metoda readFeed() wykonuje faktyczną pracę związaną z przetwarzaniem kanału. Elementy oznaczone znacznikiem „entry” stanowią punkt wyjścia do rekurencyjnego przetwarzania kanału. Jeśli następny tag nie jest tagiem wejściowym, zostanie pominięty. Po rekurencyjnym przetworzeniu całego „kanału” funkcja readFeed() zwraca listę zawierającą wpisy (w tym zagnieżdżone elementy danych) pobrane z kanału. Ta lista jest następnie zwracana przez parser.

Lista prywatna readFeed(parser XmlPullParser) zgłasza wyjątek XmlPullParserException, IOException ( Wpisy listy = new ArrayList (); parser.require(XmlPullParser.START_TAG, ns, "feed"); while (parser.next() != XmlPullParser.END_TAG) ( if (parser.getEventType() != XmlPullParser.START_TAG) (kontynuuj; ) String name = parser.getName(); // Rozpoczyna się od wyszukania znacznika wejściowego if (name.equals("entry")) ( wpisy.add( readEntry(parser)); else (skip(parser); ) ) zwraca wpisy;

Analiza XML

Aby przeanalizować kanał XML, wykonaj następujące czynności:

Ten fragment pokazuje, jak parser analizuje wpis, tytuł, łącze i podsumowanie.

Publiczna klasa statyczna Wpis (publiczny końcowy tytuł ciągu znaków; publiczny końcowy link do ciągu znaków; publiczne końcowe podsumowanie ciągu znaków; prywatny wpis (tytuł ciągu znaków, podsumowanie ciągu znaków, łącze ciągu znaków) ( this.title = tytuł; this.summary = podsumowanie; this.link = link ; ) ) // Analizuje zawartość wpisu. Jeśli napotka tytuł, podsumowanie lub znacznik łącza, przekazuje je // do odpowiednich metod „odczytu” w celu przetworzenia. W przeciwnym razie pomiń tag. prywatny wpis readEntry(parser XmlPullParser) zgłasza wyjątek XmlPullParserException, IOException ( parser.require(XmlPullParser.START_TAG, ns, "entry"); Ciąg tytuł = null; Podsumowanie ciągu = null; Łańcuch linku = null; podczas gdy (parser.next() ! = XmlPullParser.END_TAG) ( if (parser.getEventType() != XmlPullParser.START_TAG) ( kontynuuj; ) String name = parser.getName(); if (name.equals("title")) ( title = readTitle(parser) ; ) else if (nazwa.equals("podsumowanie")) ( podsumowanie = readSummary(parser); ) else if (nazwa.equals("link")) ( link = readLink(parser); ) else ( pomiń(parser) ; ) ) return new Entry(title, streszczenie, link) // Przetwarza tagi tytułowe w pliku danych. prywatny String readTitle(parser XmlPullParser) zgłasza wyjątek IOException, XmlPullParserException ( parser.require(XmlPullParser.START_TAG, ns, "title"); String title = readText(parser); parser.require(XmlPullParser.END_TAG, ns, "title"); return title; ) // Przetwarza tagi linków w kanale. prywatny String readLink(parser XmlPullParser) zgłasza wyjątek IOException, XmlPullParserException ( String link = ""; parser.require(XmlPullParser.START_TAG, ns, "link"); Tag string = parser.getName(); String relType = parser.getAttributeValue(null , "rel"); if (tag.equals("link")) ( if (relType.equals("alternatywny"))( link = parser.getAttributeValue(null, "href"); parser.nextTag(); ) ) parser.require(XmlPullParser.END_TAG, ns, "link return link"); // Przetwarza tagi podsumowujące w pliku danych. prywatny String readSummary(parser XmlPullParser) zgłasza wyjątek IOException, XmlPullParserException ( parser.require(XmlPullParser.START_TAG, ns, "podsumowanie"); Podsumowanie ciągu = readText(parser); parser.require(XmlPullParser.END_TAG, ns, "podsumowanie"); return streszczenie; ) // Dla tytułu tagu i podsumowania wyodrębnia ich wartości tekstowe. prywatny String readText(parser XmlPullParser) zgłasza wyjątek IOException, XmlPullParserException ( String wynik = ""; if (parser.next() == XmlPullParser.TEXT) ( wynik = parser.getText(); parser.nextTag(); ) zwraca wynik; ) ... )

Pomijanie przedmiotów, których nie potrzebujesz

W jednym z opisanych powyżej etapów analizowania XML parser pomija tagi, które nas nie interesują. Poniżej znajduje się kod parsera dla metody skip():

Prywatne pominięcie pustki (parser XmlPullParser) zgłasza wyjątek XmlPullParserException, IOException ( if (parser.getEventType() != XmlPullParser.START_TAG) ( rzut nowym wyjątkiem IllegalStateException(); ) int głębokość = 1; while (głębia != 0) ( przełącznik (parser. next()) ( case XmlPullParser.END_TAG: głębokość--; przerwa; case XmlPullParser.START_TAG: głębokość++; przerwa; ) ) )

Oto jak to działa:

  • Metoda zgłasza wyjątek, jeśli bieżącym zdarzeniem nie jest START_TAG .
  • Zużywa START_TAG i wszystkie zdarzenia aż do END_TAG.
  • Aby mieć pewność, że zatrzyma się na właściwym END_TAG, a nie na pierwszym tagu po oryginalnym START_TAG, śledzi głębokość zagnieżdżenia.

Zatem, jeśli bieżący element ma elementy zagnieżdżone, wartość głębi nie będzie wynosić 0, dopóki parser nie przetworzy wszystkich zdarzeń pomiędzy oryginalnym START_TAG i odpowiadającym mu END_TAG. Rozważmy na przykład, jak przechodzi analizator element posiadający 2 elementy zagnieżdżone, I :

  • Przy pierwszym przejściu przez pętlę while kolejny znacznik napotkany przez analizator to jest START_TAG dla
  • Przy drugim przejściu przez pętlę while następnym znacznikiem napotkanym przez analizator jest END_TAG
  • Przy trzecim przejściu przez pętlę while kolejnym znacznikiem napotkanym przez analizator jest START_TAG . Wartość głębokości zostaje zwiększona do 2.
  • Przy czwartym przejściu przez pętlę while kolejnym znacznikiem napotkanym przez analizator jest END_TAG. Wartość głębokości zostaje zmniejszona do 1.
  • Podczas piątego i ostatniego przejścia przez pętlę while kolejnym znacznikiem napotkanym przez analizator jest END_TAG. Wartość głębokości zostaje zmniejszona do 0, co wskazuje na to element został pomyślnie pominięty.

Przetwarzanie danych XML

Przykładowa aplikacja odbiera i analizuje źródło danych XML w AsyncTask. Przetwarzanie odbywa się poza głównym wątkiem interfejsu użytkownika. Po zakończeniu przetwarzania aplikacja aktualizuje interfejs użytkownika w głównym działaniu (NetworkActivity).

W poniższym fragmencie metoda LoadPage() wykonuje następujące czynności:

  • Inicjuje zmienną łańcuchową z adresem URL wskazującym źródło danych XML.
  • Jeśli pozwalają na to ustawienia użytkownika i połączenie sieciowe, wywołuje funkcję new DownloadXmlTask().execute(url) . Spowoduje to utworzenie nowego obiektu DownloadXmlTask ​​(podklasa AsyncTask) i wykonanie jego metody wykonywania(), która pobiera i analizuje potok oraz zwraca wynik w postaci ciągu znaków, który zostanie wyświetlony w interfejsie użytkownika.
klasa publiczna NetworkActivity rozszerza działanie (publiczny statyczny końcowy ciąg WIFI = „Wi-Fi”; publiczny statyczny końcowy ciąg ANY = „Any”; prywatny statyczny końcowy ciąg URL = „http://stackoverflow.com/feeds/tag?tagnames=android&sort =newest"; // Czy jest połączenie Wi-Fi. private static boolean wifiConnected = false; // Czy istnieje połączenie mobilne. private static boolean mobileConnected = false; // Czy wyświetlacz powinien być odświeżany. public static boolean OdświeżDisplay = true; public static String sPref = null ... // Używa AsyncTask do pobrania kanału XML z stackoverflow.com public void loadingPage() ( if((sPref.equals(ANY)) && (wifiConnected || mobileConnected ) ) ( new DownloadXmlTask().execute(URL); ) else if ((sPref.equals(WIFI)) && (wifiConnected)) ( new DownloadXmlTask().execute(URL); ) else ( // pokaż błąd) )
  • doInBackground() wykonuje metodę loadingXmlFromNetwork(). Przekazuje adres URL kanału jako parametr. Metoda LoadXmlFromNetwork() odbiera i przetwarza kanał. Po zakończeniu przetwarzania przekazuje wynikowy ciąg znaków.
  • onPostExecute() pobiera zwrócony ciąg znaków i wyświetla go w interfejsie użytkownika.
// Implementacja AsyncTask używanego do pobierania kanału XML ze stackoverflow.com. prywatna klasa DownloadXmlTask ​​rozszerza AsyncTask ( @Override chroniony String doInBackground(String... urls) ( try ( return loadingXmlFromNetwork(urls); ) catch (IOException e) ( return getResources().getString(R.string.connection_error); ) catch (XmlPullParserException e) ( return getResources().getString(R.string.xml_error); ) @Override chronione void onPostExecute(String wynik) ( setContentView(R.layout.main); // Wyświetla ciąg HTML w interfejsie użytkownika poprzez WebView WebView myWebView = ( WebView) findViewById(R.id.webview); myWebView.loadData(result, "text/html", null) )

Poniżej znajduje się metoda loadingXmlFromNetwork() wywoływana z DownloadXmlTask. Wykonuje następujące czynności:

  1. Tworzy instancję StackOverflowXmlParser. Tworzy także zmienne dla obiektów List Entry oraz tytułu, adresu URL i podsumowania, aby przechowywać wartości wyodrębnione z źródła XML dla tych pól.
  2. Wywołuje funkcję downloadUrl(), która pobiera kanał i zwraca go jako strumień wejściowy.
  3. Używa StackOverflowXmlParser do analizowania strumienia wejściowego. StackOverflowXmlParser wypełnia wpisy listy danymi ze źródła danych.
  4. Przetwarza wpisy List i łączy dane kanału ze znacznikami HTML.
  5. Zwraca ciąg HTML wyświetlany w interfejsie użytkownika głównego działania, AsyncTask, w metodzie onPostExecute().
// Przesyła plik XML ze strony stackoverflow.com, analizuje go i łączy z // znacznikami HTML. Zwraca ciąg HTML. private String loadingXmlFromNetwork(String urlString) zgłasza wyjątek XmlPullParserException, IOException ( Strumień wejściowy = null; // Utwórz instancję parsera StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser(); List wpisy = null; Tytuł ciągu = null; Adres URL łańcucha = null; Podsumowanie ciągu = null; Kalendarz RightNow = Calendar.getInstance(); Formater DateFormat = nowy SimpleDateFormat("MMM dd godz:mmaa"); // Sprawdza, czy użytkownik ustawił preferencję dołączania tekstu podsumowania SharedPreferencessharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); boolean pref = SharedPrefs.getBoolean("summaryPref", false); StringBuilder htmlString = nowy StringBuilder(); htmlString.append("

" + getResources().getString(R.string.page_title) + "

"); htmlString.append(" " + getResources().getString(R.string.updated) + " " + formatter.format(rightNow.getTime()) + ""); try ( stream = downloadUrl(urlString); wpisy = stackOverflowXmlParser.parse(stream); // Upewnia się, że strumień wejściowy zostanie zamknięty po // zakończeniu korzystania przez aplikację. ) w końcu ( if (stream != null) ( stream.close(); ) // StackOverflowXmlParser zwraca listę (zwaną „wpisami”) obiektów Entry. // Każdy obiekt Entry reprezentuje pojedynczy wpis w źródle XML. // Ta sekcja przetwarza listę wpisów w celu połączenia ich. wpis ze znacznikami HTML. // Każdy wpis jest wyświetlany w interfejsie użytkownika jako link, który opcjonalnie zawiera // podsumowanie tekstowe dla (Wpis wpisu: wpisy) ( htmlString.append("

" + wpis.tytuł + "

"); // Jeśli użytkownik ustawi preferencję dołączania tekstu podsumowania, // doda go do wyświetlacza. if (pref) ( htmlString.append(entry.summary); ) ) return htmlString.toString(); ) // Biorąc pod uwagę ciąg znaków reprezentujący adres URL, ustanawia połączenie i // pobiera strumień wejściowy prywatny strumień wejściowy inputStream(String urlString) zgłasza wyjątek IOException ( URL url = nowy adres URL (urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection() ; conn.setReadTimeout(10000 /* milisekund */); conn.setConnectTimeout(15000 /* milisekund */); conn.setRequestMethod("GET"); // Uruchamia zapytanie conn.connect( ); );

Niektóre przykłady w tym samouczku obejmują ciąg XML. Zamiast powtarzać to w każdym przykładzie, umieść tę linię w pliku dołączanym w każdym przykładzie. Linię tę pokazano w poniższym przykładzie. Ponadto możesz utworzyć dokument XML i przeczytać go za pomocą funkcji simplexml_load_file().

Przykład nr 1 Plik example.php z ciągiem XML

$xmlstr =<<


PHP: pojawienie się parsera


SM. Koder
Onliwia Aktorka


Pan. Koder
El Aktor


Jest to więc język. To wciąż język programowania. Lub
czy to jest język skryptowy? Wszystko zostało ujawnione w tym dokumencie,
jak z horroru.




7
5


XML;
?>

SimpleXML jest bardzo łatwy w użyciu! Spróbuj pobrać ciąg lub liczbę z podstawowego dokumentu XML.

Przykład nr 2 Odzyskiwanie części dokumentu

dołącz „przykład.php”;

echo $filmy -> film [ 0 ] -> fabuła ;
?>

Jest to więc język. To wciąż język programowania. A może jest to język skryptowy? Wszystko zostaje ujawnione w tym dokumencie przypominającym horror.

W PHP można uzyskać dostęp do elementu w dokumencie XML, który zawiera niedozwolone znaki (takie jak łącznik) w nazwie, umieszczając nazwę elementu w nawiasach klamrowych i apostrofach.

Przykład nr 3 Pobieranie ciągu znaków

dołącz „przykład.php”;

echo $filmy -> film ->( "wielkie-linie" )-> linia ;
?>

Wynik uruchomienia tego przykładu:

PHP rozwiązuje wszystkie moje problemy z siecią

Przykład nr 4 Dostęp do nieunikalnych elementów w SimpleXML

Jeśli w tym samym elemencie nadrzędnym znajduje się wiele wystąpień elementów podrzędnych, należy zastosować standardowe metody iteracji.

dołącz „przykład.php”;

$filmy = nowy element SimpleXMLE ($xmlstr );

/* Dla każdego węzła , nazwę wyświetlimy osobno . */
foreach ($filmy -> film -> postacie -> znak jako $znak ) (
echo $znak -> imię, "gra", $znak -> aktor, PHP_EOL;
}

?>

Wynik uruchomienia tego przykładu:

SM. Coder gra Onlivię Actorę Mr. Coder gra El Actora

Komentarz:

Nieruchomości ( $filmy->film w poprzednim przykładzie) nie są tablicami. Jest to obiekt iterowalny w postaci tablicy.

Przykład #5 Używanie atrybutów

Do tej pory udało nam się odzyskać jedynie nazwy i wartości elementów. SimpleXML może również uzyskać dostęp do atrybutów elementów. Dostęp do atrybutu elementu można uzyskać w taki sam sposób, jak do elementów tablicy ( szyk).

dołącz „przykład.php”;

$filmy = nowy element SimpleXMLE ($xmlstr );

/* Węzeł dostępu pierwszy film.
* Wyświetlimy również skalę ocen. */
foreach ($filmy -> film [ 0 ] -> ocena jako $ocena ) (
switch((string) $ocena [ "typ" ]) ( // Pobieranie atrybutów elementu według indeksu
przypadek „kciuki”:
echo $ocena , "kciuk w górę" ;
przerwa;
przypadek „gwiazd”:
echo $ocena, "gwiazdki" ;
przerwa;
}
}
?>

Wynik uruchomienia tego przykładu:

7 kciuków w górę5 gwiazdek

Przykład #6 Porównywanie elementów i atrybutów z tekstem

Aby porównać element lub atrybut z ciągiem znaków lub przekazać go do funkcji jako tekst, należy rzucić go na ciąg znaków za pomocą (strunowy). W przeciwnym razie PHP potraktuje element jako obiekt.

dołącz „przykład.php”;

$filmy = nowy element SimpleXMLE ($xmlstr );

if ((string) $filmy -> film -> tytuł == „PHP: pojawienie się parsera”) {
wydrukować "Mój ulubiony film.";
}

echo htmlentities ((string) $filmy -> film -> tytuł );
?>

Wynik uruchomienia tego przykładu:

Mój ulubiony film.PHP: Pojawienie się parsera

Przykład #7 Porównanie dwóch elementów

Dwa elementy SimpleXMLElementy są uważane za różne, nawet jeśli wskazują na ten sam obiekt od PHP 5.2.0.

dołącz „przykład.php”;

$filmy1 = nowy element SimpleXMLE ($xmlstr );
$filmy2 = nowy element SimpleXMLE ($xmlstr );
var_dump($filmy1 == $filmy2); // false od PHP 5.2.0
?>

Wynik uruchomienia tego przykładu:

Przykład nr 8 Korzystanie z XPath

SimpleXML zawiera wbudowaną obsługę XPath. Przeszukaj wszystkie elementy :

dołącz „przykład.php”;

$filmy = nowy element SimpleXMLE ($xmlstr );

foreach ($filmy -> xpath („//znak”) jako $znak ) (
echo $znak -> imię, "gra", $znak -> aktor, PHP_EOL;
}
?>

"// " służy jako symbol wieloznaczny. Aby określić ścieżkę bezwzględną, pomiń jeden z ukośników.

Wynik uruchomienia tego przykładu:

SM. Coder gra Onlivię Actorę Mr. Coder gra El Actor

Przykład #9 Ustawianie wartości

Dane w SimpleXML nie muszą być niezmienne. Obiekt pozwala na manipulację wszystkimi elementami.

dołącz „przykład.php”;
$filmy = nowy element SimpleXMLE ($xmlstr );

$filmy -> film [ 0 ]-> znaki -> znak [ 0 ] -> imię = "Miss Coder" ;

echo $filmy -> asXML();
?>

Wynik uruchomienia tego przykładu:

PHP: pojawienie się parsera Pani Koder Onliwia Aktorka Pan. Koder El Aktor 7 5

Przykład #10 Dodawanie elementów i atrybutów

Od wersji PHP 5.1.3 SimpleXML umożliwia łatwe dodawanie elementów i atrybutów podrzędnych.

dołącz „przykład.php”;
$filmy = nowy element SimpleXMLE ($xmlstr );

$znak = $filmy -> film [ 0 ]-> znaki -> addChild ( "znak");
$character -> addChild („imię”, „Pan Parser” );
$znak -> addChild („aktor”, „John Doe”);

$ocena = $filmy -> film [0]-> addChild („ocena”, „PG”);
$rating -> addAttribute („typ”, „mpaa”);

echo $filmy -> asXML();
?>

Wynik uruchomienia tego przykładu:

PHP: pojawienie się parsera SM. Koder Onliwia Aktorka Pan. Koder El Aktor Pan. Parsernieznany z nazwiska Jest to więc język. To wciąż język programowania. A może jest to język skryptowy? Wszystko zostaje ujawnione w tym dokumencie przypominającym horror. PHP rozwiązuje wszystkie moje problemy z siecią 7 5 PG

Przykład #11 Interakcja z DOM

PHP może konwertować węzły XML z formatu SimpleXML do formatu DOM i odwrotnie. Ten przykład pokazuje, jak zmienić element DOM w SimpleXML.

$dom = nowy dokument DOMDocu;
$dom -> obciążenieXML ( "nonsens" );
if (! $dom ) (
Echo „Błąd podczas analizowania dokumentu”;
Wyjście;
}

$książki = simplexml_import_dom($dom);

echo $książki -> książka [0]-> tytuł;
?>

Wynik uruchomienia tego przykładu:

4 lata temu

Często proponuje się popularną „sztuczkę”, polegającą na konwersji obiektu SimpleXML na tablicę poprzez uruchomienie go za pomocą funkcji json_encode(), a następnie json_decode(). Chciałbym wyjaśnić, dlaczego jest to zły pomysł.

Najprościej, ponieważ celem SimpleXML jest być łatwiejszym w użyciu i potężniejszym niż zwykła tablica. Możesz na przykład pisaćbar -> baz [ "bing" ] ?> i oznacza to samo cobar [ 0 ]-> baz [ 0 ][ "bing" ] ?> , niezależnie od tego, ile elementów barowych lub bazowych znajduje się w kodzie XML; i jeśli napiszeszbar [ 0 ]-> baz [ 0 ] ?> otrzymujesz całą zawartość łańcucha tego węzła - łącznie z sekcjami CDATA - niezależnie od tego, czy ma on również elementy podrzędne lub atrybuty. Masz także dostęp do informacji o przestrzeni nazw, możliwość wprowadzania prostych zmian w kodzie XML, a nawet możliwość „importowania” do obiektu DOM, co pozwala na znacznie potężniejszą manipulację. Wszystko to traci się, zamieniając obiekt w tablicę, zamiast czytać przykłady na tej stronie ze zrozumieniem.

Dodatkowo, ponieważ nie jest przeznaczony do tego celu, konwersja do JSON i z powrotem spowoduje w niektórych sytuacjach utratę informacji. Na przykład wszelkie elementy lub atrybuty w przestrzeni nazw zostaną po prostu odrzucone, a wszelka treść tekstowa zostanie odrzucona, jeśli element ma również dzieci lub atrybuty. Czasami nie będzie to miało znaczenia, ale jeśli przyzwyczaisz się do konwertowania wszystkiego na tablice, w końcu cię to użądli.

Oczywiście można napisać mądrzejszą konwersję, która nie będzie miała tych ograniczeń, ale w tym momencie nie uzyskasz żadnej wartości z SimpleXML i powinieneś po prostu użyć funkcji XML Parser niższego poziomu lub klasy XMLReader, aby stworzyć swoją strukturę Nadal nie będziesz mieć dodatkowej, wygodnej funkcjonalności SimpleXML, ale to twoja strata.

2 lata temu

Jeśli ciąg XML zawiera wartości logiczne zakodowane za pomocą „0” i „1”, napotkasz problemy, gdy rzutujesz element bezpośrednio na wartość bool:

$xmlstr =<<

1
0

XML;
$wartości = nowy SimpleXMLElement($xmlstr);
$truevalue = (bool)$values->truevalue; // PRAWDA
$falsevalue = (bool)$values->falsevalue; // też prawda!!!

Zamiast tego musisz najpierw rzucić na string lub int:

$truevalue = (bool)(int)$values->truevalue; // PRAWDA
$falsevalue = (bool)(int)$values->falsevalue; // FAŁSZ

9 lat temu

Jeśli w odpowiedzi chcesz wyprowadzić prawidłowy plik XML, nie zapomnij ustawić typu zawartości nagłówka na xml oraz wyświetlić wynik asXML():

$xml = simplexml_load_file("...");
...
... rzeczy XML
...

//wypisz xml w swojej odpowiedzi:
header("Typ zawartości: tekst/xml");
echo $xml -> asXML();
?>

9 lat temu

Z pliku README:

SimpleXML ma być łatwym sposobem dostępu do danych XML.

Obiekty SimpleXML kierują się czterema podstawowymi zasadami:

1) właściwości oznaczają iteratory elementów
2) indeksy numeryczne oznaczają elementy
3) wskaźniki nieliczbowe oznaczają atrybuty
4) konwersja ciągów umożliwia dostęp do danych TEKSTOWYCH

Podczas iteracji właściwości rozszerzenie zawsze wykonuje iterację
wszystkie węzły o tej nazwie elementu. Zatem metoda Children() musi być
wywoływany do iteracji po podwęzłach. Ale wykonaj także następujące czynności:
foreach ($obj->nazwa_węzła jako $elem) (
// zrób coś z $elem
}
zawsze skutkuje iteracją elementów „nazwa_węzła”. Więc nie dalej
sprawdzenie jest konieczne, aby rozróżnić liczbę węzłów tego typu.

Gdy dostęp do danych TEXT elementu jest uzyskiwany poprzez właściwość
wówczas wynik nie uwzględnia danych TEKSTOWYCH podelementów.

Znane problemy
============

Ze względu na problemy z silnikiem dostęp do niego jest obecnie niemożliwy
podelement o indeksie 0: $object->property.

8 lat temu

Używanie rzeczy takich jak: is_object($xml->module->admin) do sprawdzania, czy rzeczywiście istnieje węzeł o nazwie „admin”, nie wydaje się działać zgodnie z oczekiwaniami, ponieważ simplexml zawsze zwraca obiekt - w tym przypadku pusty - nawet jeśli dany węzeł nie istnieje.
Dla mnie, stara, dobra funkcja pusty() wydaje się działać dobrze w takich przypadkach.

8 lat temu

Szybka wskazówka dotycząca zapytań xpath i domyślnych przestrzeni nazw. Wygląda na to, że system XML stojący za SimpleXML działa tak samo, jak sądzę, że system XML wykorzystuje .NET: gdy trzeba zaadresować coś w domyślnej przestrzeni nazw, trzeba będzie zadeklarować przestrzeń nazw za pomocą rejestruXPathNamespace, a następnie użyć jej przedrostka zaadresuj inaczej w domyślnym żywym elemencie przestrzeni nazw.

$ciąg =<<

Czterdzieści Co?
Joe
Jane

Wiem, że to jest odpowiedź, ale jakie jest pytanie?


XML;

$xml = simplexml_load_string ($string);
$xml -> zarejestrujXPathNamespace("def" , „http://www.w3.org/2005/Atom”);

$nodes = $xml -> xpath („//def:document/def:title” );

?>

9 lat temu

Chociaż SimpleXMLElement twierdzi, że jest iterowalny, nie wydaje się, aby poprawnie implementował standardowe funkcje interfejsu Iteratora, takie jak::next i::reset. Dlatego też, chociaż funkcja foreach() działa, funkcje takie jak next(), current() czy each() wydają się nie działać tak, jak można by się spodziewać — wskaźnik nigdy się nie porusza lub jest resetowany.

6 lat temu

Jeśli kodowanie dokumentu XML nie jest zgodne z UTF-8, deklaracja kodowania musi pojawić się bezpośrednio po wersji="..." i przed standalone="...". Jest to wymóg standardu XML.

Jeśli kodowanie dokumentu XML różni się od UTF-8. Deklaracja kodowania powinna następować bezpośrednio po wersji = „…” i przed samodzielnym = „…”. To wymaganie jest standardowym XML.


OK

Język rosyjski. Język rosyjski
Błąd krytyczny: nieprzechwycony wyjątek „Wyjątek” z komunikatem „Nie można przeanalizować ciągu znaków jako XML” w...


publikacja tego artykułu jest dozwolona wyłącznie z linkiem do strony internetowej autora artykułu

W tym artykule pokażę przykład, jak analizować duży plik XML. Jeśli Twój serwer (hosting) nie zabrania wydłużania czasu działania skryptu, możesz przeanalizować plik XML ważący co najmniej gigabajty; ja osobiście analizowałem tylko pliki z ozonu o wadze 450 megabajtów.

Podczas analizowania dużych plików XML pojawiają się dwa problemy:
1. Za mało pamięci.
2. Nie ma wystarczającej ilości czasu na uruchomienie skryptu.

Drugi problem z czasem można rozwiązać, jeśli serwer tego nie zabrania.
Ale problem z pamięcią jest trudny do rozwiązania, nawet jeśli mówimy o własnym serwerze, to przenoszenie plików o wielkości 500 megabajtów nie jest zbyt łatwe i po prostu nie można zwiększyć pamięci w hostingu i VDS.

PHP posiada kilka wbudowanych opcji przetwarzania XML - SimpleXML, DOM, SAX.
Wszystkie te opcje są szczegółowo opisane w wielu artykułach z przykładami, ale wszystkie przykłady demonstrują pracę z pełnym dokumentem XML.

Oto jeden przykład pobierania obiektu z pliku XML

Teraz możesz przetworzyć ten obiekt, ALE...
Jak widać, cały plik XML jest wczytywany do pamięci, a następnie wszystko jest przetwarzane w obiekt.
Oznacza to, że wszystkie dane trafiają do pamięci i jeśli nie ma wystarczającej ilości przydzielonej pamięci, skrypt zatrzymuje się.

Ta opcja nie nadaje się do przetwarzania dużych plików; musisz czytać plik linia po linii i przetwarzać te dane jedna po drugiej.
W tym przypadku kontrola ważności odbywa się również w trakcie przetwarzania danych, dlatego trzeba mieć możliwość wycofania danych, np. usunięcia wszystkich danych wprowadzonych do bazy w przypadku nieprawidłowego pliku XML lub przeprowadzenia dwóch przebiegów przez plik, najpierw przeczytaj pod kątem ważności, a następnie przeczytaj pod kątem przetwarzania danych.

Oto teoretyczny przykład analizowania dużego pliku XML.
Skrypt ten odczytuje z pliku jeden znak na raz, zbiera te dane w bloki i wysyła je do parsera XML.
Takie podejście całkowicie rozwiązuje problem pamięci i nie powoduje obciążenia, ale z czasem pogarsza problem. Jak spróbować rozwiązać problem z biegiem czasu, przeczytaj poniżej.

Funkcja webi_xml (plik $)
{

########
### funkcja danych

{
wydrukuj $dane ;
}
############################################



{
wydrukuj $nazwa ;
print_r($attrs);
}


## Funkcja znacznika zamykającego
funkcja endElement ($parser, $name)
{
wydrukuj $nazwa ;
}
############################################

($xml_parser, „dane”);

// otwórz plik
$fp = fopen($plik, "r");

$perviy_vxod = 1 ; $dane = "" ;



{

$simvol = fgetc ($fp); $dane.= $simvol;


if($simvol != ">" ) (kontynuuj;)


Echo "

przerwa;
}

$dane = "" ;
}
fclose($fp);

Webi_xml("1.xml");

?>

W tym przykładzie umieściłem wszystko w jednej funkcji webi_xml() i na samym dole widać jej wywołanie.
Sam skrypt składa się z trzech głównych funkcji:
1. Funkcja przechwytująca otwarcie znacznika startElement().
2. Funkcja przechwytująca zamykający tag endElement().
3. Oraz funkcja odbierania danych data() .

Załóżmy, że zawartość pliku 1.xml to przepis



< title >Prosty chleb
< ingredient amount = "3" unit = "стакан" >Mąka
< ingredient amount = "0.25" unit = "грамм" >Drożdże
< ingredient amount = "1.5" unit = "стакан" >Ciepła woda
< ingredient amount = "1" unit = "чайная ложка" >Sól
< instructions >
< step > Wszystkie składniki wymieszać i dokładnie zagnieść.
< step > Przykryć ściereczką i odstawić na godzinę w ciepłym pomieszczeniu..
< step > Ugniataj ponownie, ułożyć na blasze do pieczenia i wstawić do piekarnika.
< step > Odwiedź witrynę witryny


Wszystko zaczynamy od wywołania ogólnej funkcji webi_xml („1.xml” );
Następnie parser uruchamia tę funkcję i wszystkie nazwy znaczników są konwertowane na wielkie litery, tak aby wszystkie znaczniki miały tę samą wielkość liter.

$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, prawda);

Teraz wskazujemy, które funkcje będą działać, aby wychwycić otwarcie tagu, zamknięcie i przetwarzanie danych

xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, „dane”);

Następnie następuje otwarcie określonego pliku, iteracja po pliku, jeden znak na raz, a każdy znak jest dodawany do zmiennej łańcuchowej, aż znak zostanie znaleziony > .
Jeśli jest to pierwszy dostęp do pliku, to po drodze wszystko, co niepotrzebne na początku pliku, zostanie usunięte, wszystko, co będzie wcześniej , jest to znacznik, od którego powinien zaczynać się kod XML.
Po raz pierwszy zmienna łańcuchowa będzie zawierać ciąg znaków

I wyślij go do dezasemblera
xml_parse ($xml_parser, $data, feof ($fp));
Po przetworzeniu danych zmienna łańcuchowa jest resetowana, gromadzenie danych w łańcuch rozpoczyna się od nowa i ciąg jest tworzony po raz drugi

Na trzecim
</b><br>na czwartym <br><b>Prosty chleb

Należy pamiętać, że zmienna łańcuchowa jest zawsze tworzona na podstawie wypełnionego znacznika > i nie ma potrzeby wysyłania włamywaczowi np. otwartej i zamkniętej zawieszki z danymi
Prosty chleb
Ważne, aby ten handler otrzymał cały nieprzerwany tag, przynajmniej jeden tag otwarty, a w kolejnym kroku tag zamknięty, lub od razu otrzymał 1000 linii pliku, nie ma to znaczenia, najważniejsze, że tag nie pęka np

le>Zwykły chleb
W ten sposób niemożliwe jest przesłanie danych do modułu obsługi, ponieważ tag jest rozdarty.
Możesz wymyślić własną metodę przesyłania danych do handlera, np. zebrać 1 megabajt danych i wysłać go do handlera, aby zwiększyć prędkość, tylko upewnij się, że tagi są zawsze wypełnione, a dane mogą zostać podarte
Prosty</b><br><b>chleb

W ten sposób, w dowolnych częściach, możesz wysłać duży plik do procesora.

Przyjrzyjmy się teraz, w jaki sposób te dane są przetwarzane i jak je uzyskać.

Zacznijmy od funkcji otwierania tagów element startowy ($parser, $nazwa, $attrs)
Załóżmy, że przetwarzanie osiągnęło linię
< ingredient amount = "3" unit = "стакан" >Mąka
Następnie wewnątrz funkcji zmienna $name będzie równa składnik czyli nazwa otwartego tagu (nie doszło jeszcze do zamknięcia tagu).
Również w tym przypadku dostępna będzie tablica atrybutów tego tagu $attrs, która będzie zawierać dane ilość = „3” i jednostka = „szkło”.

Następnie dane otwartego tagu zostały przetworzone przez funkcję dane ($parser, $data)
Zmienna $data będzie zawierać wszystko co znajduje się pomiędzy tagiem otwierającym i zamykającym, w naszym przypadku jest to tekst Muka

I przetwarzanie naszego ciągu przez funkcję kończy się endElement ($parser, $nazwa)
Jest to nazwa zamkniętego tagu, w naszym przypadku $name będzie równe składnik

A potem wszystko znowu zatoczyło koło.

Powyższy przykład ilustruje jedynie zasadę przetwarzania XML, jednak dla rzeczywistego zastosowania wymaga on modyfikacji.
Zazwyczaj trzeba analizować duży plik XML, aby wprowadzić dane do bazy danych, a aby prawidłowo przetworzyć dane, trzeba wiedzieć, do którego otwartego tagu należą dane, na jakim poziomie zagnieżdżenia tagów i które tagi są otwarte w powyższej hierarchii. Dzięki tym informacjom możesz bez problemu poprawnie przetworzyć plik.
Aby to zrobić należy wprowadzić kilka zmiennych globalnych, które będą zbierać informacje o otwartych tagach, zagnieżdżeniu i danych.
Oto przykład, którego możesz użyć

Funkcja webi_xml (plik $)
{
globalny $webi_głębokość ; // licznik do śledzenia głębokości zagnieżdżenia
$webi_głębia = 0 ;
globalny $webi_tag_open ; // będzie zawierać tablicę aktualnie otwartych tagów
$webi_tag_open = tablica();
globalny $webi_data_temp ; //ta tablica będzie zawierać dane jednego tagu

####################################################
### funkcja danych
dane funkcji ($parser, $data)
{
globalny $webi_głębokość ;
globalny $webi_tag_open ;
globalny $webi_data_temp ;
// dodaj dane do tablicy wskazujące zagnieżdżenie i aktualnie otwarty tag
$webi_data_temp [ $webi_głębia ][ $webi_tag_open [ $webi_głębia ]][ "dane" ].= $dane;
}
############################################

####################################################
### funkcja tagu otwierającego
funkcja startElement ($parser, $name, $attrs)
{
globalny $webi_głębokość ;
globalny $webi_tag_open ;
globalny $webi_data_temp ;

// jeśli poziom zagnieżdżenia nie jest już zerowy, oznacza to, że jeden tag jest już otwarty
// a dane z niego znajdują się już w tablicy, możesz je przetworzyć
jeśli ($webi_głębia)
{




" ;

wydrukuj "
" ;
print_r($webi_tag_open); // tablica otwartych tagów
wydrukuj "


" ;

// po przetworzeniu danych usuń je, aby zwolnić pamięć
unset($GLOBALS [ "webi_data_temp" ][ $webi_głębia ]);
}

// teraz otwierany jest następny tag i dalsze przetwarzanie nastąpi w następnym kroku
$webi_głębia++; //zwiększ zagnieżdżanie

$webi_tag_open [ $webi_głębia ]= $nazwa; // dodaj otwarty tag do tablicy informacyjnej
$webi_data_temp [ $webi_głębia ][ $nazwa ][ "atrybuty" ]= $attrs; // teraz dodaj atrybuty tagu

}
###############################################

#################################################
## Funkcja znacznika zamykającego
funkcja endElement ($parser, $nazwa) (
globalny $webi_głębokość ;
globalny $webi_tag_open ;
globalny $webi_data_temp ;

// Tutaj rozpoczyna się przetwarzanie danych, np. dodanie do bazy, zapis do pliku itp.
// $webi_tag_open zawiera łańcuch otwartych tagów według poziomu zagnieżdżenia
// na przykład $webi_tag_open[$webi_głębokość] zawiera nazwę otwartego tagu, którego informacje są aktualnie przetwarzane
// Poziom zagnieżdżenia tagu $webi_length
// $webi_data_temp[$webi_głębokość][$webi_tag_open[$webi_głębokość]]["attrs"] tablica atrybutów tagu
// $webi_data_temp[$webi_length][$webi_tag_open[$webi_głębi]]["data"] dane tagu

Wydrukuj „dane”. $webi_tag_open [ $webi_głębia ]. „--” .($webi_data_temp [ $webi_głębokość ][ $webi_tag_open [ $webi_głębia ]][ „dane” ]). "
" ;
print_r ($webi_data_temp [ $webi_głębia ][ $webi_tag_open [ $webi_głębia ]][ "attrs" ]);
wydrukuj "
" ;
print_r($webi_tag_open);
wydrukuj "


" ;

Unset($GLOBALS [ "webi_data_temp" ]); // po przetworzeniu danych usuwamy całą tablicę z danymi, gdyż tag został zamknięty
unset($GLOBALS [ "webi_tag_open" ][ $webi_głębia ]); // usuń informacje o tym otwartym tagu... od czasu jego zamknięcia

$webi_głębia --; // zmniejsz zagnieżdżanie
}
############################################

$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, prawda);

// wskazuje, które funkcje będą działać podczas otwierania i zamykania tagów
xml_set_element_handler($xml_parser, "startElement", "endElement");

// określ funkcję do pracy z danymi
xml_set_character_data_handler($xml_parser, „dane”);

// otwórz plik
$fp = fopen($plik, "r");

$perviy_vxod = 1 ; // flaga sprawdzająca pierwszy wpis w pliku
$dane = "" ; // tutaj zbieramy dane z pliku w częściach i wysyłamy je do parsera xml

// pętla aż do znalezienia końca pliku
while (! feof ($fp ) i $fp )
{
$simvol = fgetc ($fp); // odczytaj jeden znak z pliku
$dane.= $simvol; // dodaj ten znak do danych, które mają zostać wysłane

// jeśli znak nie jest znacznikiem końcowym, wróć na początek pętli i dodaj kolejny znak do danych, i tak dalej, aż zostanie znaleziony znacznik końcowy
if($simvol != ">" ) (kontynuuj;)
// jeśli odnaleziono tag zamykający, teraz wyślemy zebrane dane do przetworzenia

// sprawdzamy czy jest to pierwszy wpis w pliku, wtedy usuniemy wszystko co jest przed tagiem// ponieważ czasami możesz napotkać śmieci przed początkiem XML (niezdarni redaktorzy lub plik został odebrany przez skrypt z innego serwera)
if($perviy_vxod ) ( $data = strstr ($data , "

// teraz wrzuć dane do parsera xml
if (! xml_parse ($xml_parser, $data, feof ($fp))) (

// tutaj możesz przetwarzać i odbierać błędy ważności...
// gdy tylko wystąpi błąd, przetwarzanie zostaje zatrzymane
Echo "
Błąd XML: " .xml_error_string(xml_get_error_code($xml_parser));
echo „w linii” . xml_get_current_line_number ($xml_parser);
przerwa;
}

// po przeanalizowaniu usuń zebrane dane do następnego kroku cyklu.
$dane = "" ;
}
fclose($fp);
xml_parser_free($xml_parser);
// usuwanie zmiennych globalnych
unset($GLOBALS ["głębokość_webi"]);
unset($GLOBALS [ "webi_tag_open" ]);
unset($GLOBALS [ "webi_data_temp" ]);

Webi_xml("1.xml");

?>

Całemu przykładowi towarzyszą komentarze, teraz przetestuj i poeksperymentuj.
Należy pamiętać, że w funkcji pracy z danymi dane nie są po prostu wstawiane do tablicy, ale raczej dodawane za pomocą „ .=" ponieważ dane mogą nie dotrzeć w całości, a jeśli po prostu wykonasz zadanie, od czasu do czasu otrzymasz dane w kawałkach.

Cóż, to wszystko, teraz jest wystarczająco dużo pamięci do przetwarzania pliku dowolnego rozmiaru, ale czas działania skryptu można wydłużyć na kilka sposobów.
Wstaw funkcję na początku skryptu
set_time_limit(6000);
Lub
ini_set („max_czas_wykonania”, „6000”);

Lub dodaj tekst do pliku .htaccess
wartość_php max_execution_time 6000

Te przykłady wydłużą czas działania skryptu do 6000 sekund.
Możesz zwiększyć czas w ten sposób tylko wtedy, gdy tryb awaryjny jest wyłączony.

Jeśli masz dostęp do edycji php.ini, możesz wydłużyć czas za pomocą
maksymalny czas_wykonania = 6000

Na przykład na hostingu Masterhost w momencie pisania tego artykułu wydłużanie czasu skryptu jest zabronione pomimo wyłączenia trybu awaryjnego, ale jeśli jesteś profesjonalistą, możesz stworzyć własną wersję PHP na Masterhost, ale to nie jest tematem tego artykułu.