Genişletilebilir Biçimlendirme Dili (XML), dokümanları kodlamaya ilişkin okunabilir bir biçimde olması gerekir. XML, internette veri paylaşımı için popüler bir biçimdir.
İçeriğini sık sık güncelleyen haber siteleri veya bloglar gibi web siteleri, harici programların içeriği takip edebilmesi için genellikle bir XML feed'i sağlar anlamına gelir. Ağa bağlı cihazlar için XML verilerini yükleme ve ayrıştırma, yaygın olarak kullanılan bir görevdir Bu konuda, XML dokümanlarının nasıl ayrıştırılacağı ve bu belgelerin nasıl kullanılacağı açıklanmaktadır.
Android uygulamanızda web tabanlı içerik oluşturma hakkında daha fazla bilgi edinmek için Web tabanlı içerik.
Ayrıştırıcı seçin
verimli ve etkili bir çözüm olan XmlPullParser
XML'de XML ayrıştırmasının kolay bir yoludur. Android'de iki
aşağıdaki adımları uygulayın:
KXmlParser
XmlPullParserFactory.newPullParser()
kullanılıyorExpatPullParser
kullanıyorXml.newPullParser()
Her iki seçenek de kullanılabilir. İlgili içeriği oluşturmak için kullanılan
bu bölümdeki bir örnekte ExpatPullParser
ve
Xml.newPullParser()
.
Feed'i analiz etme
Bir feed'i ayrıştırmanın ilk adımı, ilgilendiğiniz alanlarla ilgili karar vermektir. Ayrıştırıcı, bu alanlar için verileri ayıklar ve geri kalanını yok sayar.
Örnek uygulamada ayrıştırılmış bir feed'den alınan aşağıdaki alıntıya bakın. Her biri
StackOverflow.com'a gönderilen yayınlar
feed'inizi, iç içe yerleştirilmiş birkaç etiket içeren bir entry
etiketi olarak tanımlar:
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" ..."> <title type="text">newest questions tagged android - Stack Overflow</title> ... <entry> ... </entry> <entry> <id>http://stackoverflow.com/q/9439999</id> <re:rank scheme="http://stackoverflow.com">0</re:rank> <title type="text">Where is my data file?</title> <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="android"/> <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="file"/> <author> <name>cliff2310</name> <uri>http://stackoverflow.com/users/1128925</uri> </author> <link rel="alternate" href="http://stackoverflow.com/questions/9439999/where-is-my-data-file" /> <published>2012-02-25T00:30:54Z</published> <updated>2012-02-25T00:30:54Z</updated> <summary type="html"> <p>I have an Application that requires a data file...</p> </summary> </entry> <entry> ... </entry> ... </feed>
Örnek uygulama
entry
etiketi ve iç içe geçmiş etiketleri için verileri ayıklar
title
, link
ve summary
.
Ayrıştırıcıyı örneklendirme
Bir feed'i ayrıştırmada bir sonraki adım,
ayrıştırıcı örneklendirilir ve ayrıştırma işlemine başlar. Bu snippet
ad alanlarını işlememesi ve sağlanan InputStream
öğesini giriş olarak kullanması için bir ayrıştırıcı başlatır. Ayrıştırma sürecini başlatmak için bir
nextTag()
ve
Uygulamanın verilerini ayıklayan ve işleyen readFeed()
yöntemi
ilgi alanları:
Kotlin
// We don't use namespaces. private val ns: String? = null class StackOverflowXmlParser { @Throws(XmlPullParserException::class, IOException::class) fun parse(inputStream: InputStream): List<*> { inputStream.use { inputStream -> val parser: XmlPullParser = Xml.newPullParser() parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setInput(inputStream, null) parser.nextTag() return readFeed(parser) } } ... }
Java
public class StackOverflowXmlParser { // We don't use namespaces. private static final String ns = null; public List parse(InputStream in) throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(in, null); parser.nextTag(); return readFeed(parser); } finally { in.close(); } } ... }
Feed'i oku
readFeed()
yöntemi,
feed'i seçin. "Giriş" etiketli öğeleri arar. yinelemeli bir başlangıç noktası olarak
bir şablondur. Bir etiket entry
etiketi değilse bu etiketi atlar. Tüm
feed yinelemeli olarak işlenir, readFeed()
bir
List
içeren tüm bu alt bölümlere (iç içe yerleştirilmiş veri üyeleri dahil)
özet akışından alındı. Bu List
, daha sonra
ayrıştırıcıdır.
Kotlin
@Throws(XmlPullParserException::class, IOException::class) private fun readFeed(parser: XmlPullParser): List<Entry> { val entries = mutableListOf<Entry>() parser.require(XmlPullParser.START_TAG, ns, "feed") while (parser.next() != XmlPullParser.END_TAG) { if (parser.eventType != XmlPullParser.START_TAG) { continue } // Starts by looking for the entry tag. if (parser.name == "entry") { entries.add(readEntry(parser)) } else { skip(parser) } } return entries }
Java
private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException { List entries = new ArrayList(); parser.require(XmlPullParser.START_TAG, ns, "feed"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } String name = parser.getName(); // Starts by looking for the entry tag. if (name.equals("entry")) { entries.add(readEntry(parser)); } else { skip(parser); } } return entries; }
XML ayrıştırma
Bir XML feed'ini ayrıştırma adımları aşağıda açıklanmıştır:
- Feed'i analiz etme bölümünde açıklandığı gibi uygulamanıza dahil etmek istediğiniz etiketleri tanımlayın. Bu
örnek,
entry
etiketi ve iç içe yerleştirilmiş etiketlerine ilişkin verileri ayıklar:title
,link
vesummary
. - Aşağıdaki yöntemleri oluşturun:
- Bir "okuma" yöntemini kullanabilirsiniz (örneğin,
readEntry()
vereadTitle()
. Ayrıştırıcı şu verileri okur: etiketleri arasındaki ilişkiyi belirtir. Bu örnekte,entry
,title
,link
veyasummary
için uygun yöntemi çağırır bu etiket için geçerli olur. Aksi takdirde, etiketi atlar. - Her bir farklı etiket türü için veri ayıklama ve
ayrıştırıcıyı sonraki etikete ekleyin. Bu örnekte, alakalı yöntemler aşağıdaki gibidir:
- Ayrıştırıcı,
title
vesummary
etiketleri içinreadText()
. Bu yöntem, şunu çağırarak bu etiketlere ilişkin verileri ayıklar:parser.getText()
link
etiketi için ayrıştırıcı, öncelikle bağlantının farklı türde belirli bir sayfadır. Daha sonraparser.getAttributeValue()
kullanır. bağlantının değerini çıkarın.- Ayrıştırıcı,
entry
etiketi içinreadEntry()
yöntemini çağırır. Bu yöntem, girişin iç içe yerleştirilmiş etiketlerini ayrıştırır ve birEntry
döndürür.title
,link
ve veri üyelerini içeren nesnesummary
.
- Ayrıştırıcı,
- Yinelemeli bir yardımcı
skip()
yöntemi. Bu konuyla ilgili daha fazla bilgi için Önem verdiğiniz etiketleri atlama bölümüne bakın.
- Bir "okuma" yöntemini kullanabilirsiniz (örneğin,
Bu snippet, ayrıştırıcının girişleri, başlıkları, bağlantıları ve özetleri nasıl ayrıştırdığını gösterir.
Kotlin
data class Entry(val title: String?, val summary: String?, val link: String?) // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off // to their respective "read" methods for processing. Otherwise, skips the tag. @Throws(XmlPullParserException::class, IOException::class) private fun readEntry(parser: XmlPullParser): Entry { parser.require(XmlPullParser.START_TAG, ns, "entry") var title: String? = null var summary: String? = null var link: String? = null while (parser.next() != XmlPullParser.END_TAG) { if (parser.eventType != XmlPullParser.START_TAG) { continue } when (parser.name) { "title" -> title = readTitle(parser) "summary" -> summary = readSummary(parser) "link" -> link = readLink(parser) else -> skip(parser) } } return Entry(title, summary, link) } // Processes title tags in the feed. @Throws(IOException::class, XmlPullParserException::class) private fun readTitle(parser: XmlPullParser): String { parser.require(XmlPullParser.START_TAG, ns, "title") val title = readText(parser) parser.require(XmlPullParser.END_TAG, ns, "title") return title } // Processes link tags in the feed. @Throws(IOException::class, XmlPullParserException::class) private fun readLink(parser: XmlPullParser): String { var link = "" parser.require(XmlPullParser.START_TAG, ns, "link") val tag = parser.name val relType = parser.getAttributeValue(null, "rel") if (tag == "link") { if (relType == "alternate") { link = parser.getAttributeValue(null, "href") parser.nextTag() } } parser.require(XmlPullParser.END_TAG, ns, "link") return link } // Processes summary tags in the feed. @Throws(IOException::class, XmlPullParserException::class) private fun readSummary(parser: XmlPullParser): String { parser.require(XmlPullParser.START_TAG, ns, "summary") val summary = readText(parser) parser.require(XmlPullParser.END_TAG, ns, "summary") return summary } // For the tags title and summary, extracts their text values. @Throws(IOException::class, XmlPullParserException::class) private fun readText(parser: XmlPullParser): String { var result = "" if (parser.next() == XmlPullParser.TEXT) { result = parser.text parser.nextTag() } return result } ...
Java
public static class Entry { public final String title; public final String link; public final String summary; private Entry(String title, String summary, String link) { this.title = title; this.summary = summary; this.link = link; } } // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off // to their respective "read" methods for processing. Otherwise, skips the tag. private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, ns, "entry"); String title = null; String summary = null; String link = null; while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } String name = parser.getName(); if (name.equals("title")) { title = readTitle(parser); } else if (name.equals("summary")) { summary = readSummary(parser); } else if (name.equals("link")) { link = readLink(parser); } else { skip(parser); } } return new Entry(title, summary, link); } // Processes title tags in the feed. private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, ns, "title"); String title = readText(parser); parser.require(XmlPullParser.END_TAG, ns, "title"); return title; } // Processes link tags in the feed. private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException { String link = ""; parser.require(XmlPullParser.START_TAG, ns, "link"); String tag = parser.getName(); String relType = parser.getAttributeValue(null, "rel"); if (tag.equals("link")) { if (relType.equals("alternate")){ link = parser.getAttributeValue(null, "href"); parser.nextTag(); } } parser.require(XmlPullParser.END_TAG, ns, "link"); return link; } // Processes summary tags in the feed. private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, ns, "summary"); String summary = readText(parser); parser.require(XmlPullParser.END_TAG, ns, "summary"); return summary; } // For the tags title and summary, extracts their text values. private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { String result = ""; if (parser.next() == XmlPullParser.TEXT) { result = parser.getText(); parser.nextTag(); } return result; } ... }
İlgilenmediğiniz etiketleri atlayın
Ayrıştırıcının ilgilenmediği etiketleri atlaması gerekiyor. Ayrıştırıcının skip()
yöntemi şöyledir:
Kotlin
@Throws(XmlPullParserException::class, IOException::class) private fun skip(parser: XmlPullParser) { if (parser.eventType != XmlPullParser.START_TAG) { throw IllegalStateException() } var depth = 1 while (depth != 0) { when (parser.next()) { XmlPullParser.END_TAG -> depth-- XmlPullParser.START_TAG -> depth++ } } }
Java
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { if (parser.getEventType() != XmlPullParser.START_TAG) { throw new IllegalStateException(); } int depth = 1; while (depth != 0) { switch (parser.next()) { case XmlPullParser.END_TAG: depth--; break; case XmlPullParser.START_TAG: depth++; break; } } }
Bu şöyle işler:
- Geçerli etkinlik gerçek bir etkinlik değilse
START_TAG
START_TAG
veri türünün yanı sıra, şu tarihe kadar ve dahil tüm etkinlikleri kullanır: eşleşenEND_TAG
.- İç içe yerleştirme derinliğini takip ederek doğru
END_TAG
noktasında değil, doğru olduğunda durduğundan emin olmak için orijinalSTART_TAG
etiketinden sonra karşılaştığı ilk etikettir.
Dolayısıyla, geçerli öğede iç içe yerleştirilmiş öğeler varsa
depth
, ayrıştırıcı tüm etkinlikleri tüketene kadar 0 olmaz
orijinal START_TAG
ve eşleşen END_TAG
. Örneğin,
ayrıştırıcının <author>
öğesini nasıl atladığını düşünün
İç içe yerleştirilmiş 2 öğesi olan <name>
ve
<uri>
:
while
döngüsünde ilk kez, ayrıştırıcının bir sonraki etiketi<author>
sonrasıSTART_TAG
karşılaşma oranı<name>
.depth
değeri şu şekilde artar: 2.while
döngüsünden ikincisinde, ayrıştırıcı bir sonraki etiketleEND_TAG
</name>
. Değer (depth
) 1'e iner.while
döngüsünden üçüncü kez yararlanıldığında ayrıştırıcının bir sonraki etiketiSTART_TAG
<uri>
. Değerdepth
değeri 2'ye artar.while
döngüsünden dördüncü kez, ayrıştırıcının sonraki etiketiEND_TAG
</uri>
. Değeridepth
, 1'e düşer.while
döngüsünün beşinci ve son seferi, sonraki sefer ayrıştırıcının karşılaştığı etiketEND_TAG
</author>
.depth
değeri şu değere düşer: 0 (<author>
öğesinin başarıyla tamamlandığını gösterir) atlandı.
XML verilerini kullan
Örnek uygulama, XML feed'ini eşzamansız olarak getirir ve ayrıştırır. Bu, işlemeyi ana kullanıcı arayüzü iş parçacığından kaldırır. Zaman
Uygulama, kullanıcı arayüzünü ana etkinliğinde günceller.
NetworkActivity
Aşağıdaki alıntıda loadPage()
yöntemi aşağıdakileri yapar:
- XML feed'inin URL'siyle bir dize değişkenini başlatır.
- Kullanıcının ayarları ve ağ ise
downloadXml(url)
yöntemini çağırır buna izin vermiş olursunuz. Bu yöntem feed'i indirip ayrıştırır ve kullanıcı arayüzünde görüntülenir.
Kotlin
class NetworkActivity : Activity() { companion object { const val WIFI = "Wi-Fi" const val ANY = "Any" const val SO_URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest" // Whether there is a Wi-Fi connection. private var wifiConnected = false // Whether there is a mobile connection. private var mobileConnected = false // Whether the display should be refreshed. var refreshDisplay = true // The user's current network preference setting. var sPref: String? = null } ... // Asynchronously downloads the XML feed from stackoverflow.com. fun loadPage() { if (sPref.equals(ANY) && (wifiConnected || mobileConnected)) { downloadXml(SO_URL) } else if (sPref.equals(WIFI) && wifiConnected) { downloadXml(SO_URL) } else { // Show error. } } ... }
Java
public class NetworkActivity extends Activity { public static final String WIFI = "Wi-Fi"; public static final String ANY = "Any"; private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest"; // Whether there is a Wi-Fi connection. private static boolean wifiConnected = false; // Whether there is a mobile connection. private static boolean mobileConnected = false; // Whether the display should be refreshed. public static boolean refreshDisplay = true; public static String sPref = null; ... // Asynchronously downloads the XML feed from stackoverflow.com. public void loadPage() { if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) { downloadXml(URL); } else if ((sPref.equals(WIFI)) && (wifiConnected)) { downloadXml(URL); } else { // Show error. } }
downloadXml
yöntemi, Kotlin'de aşağıdaki yöntemleri çağırır:
lifecycleScope.launch(Dispatchers.IO)
(kotlin eş yordamlarını kullanarak) KS iş parçacığındaloadXmlFromNetwork()
yöntemini başlatın. Feed URL'sini parametresinden sonra bir değer girin.loadXmlFromNetwork()
yöntemini getirme ve işleme feed'i seçin. Tamamlandığında, bir sonuç dizesi geri verir.- Ana iş parçacığına dönmek için Kotlin eş yordamlarını kullanan
withContext(Dispatchers.Main)
, dizeyi döndürür ve kullanıcı arayüzünde görüntüler.
Java programlama dilinde süreç aşağıdaki gibidir:
Executor
tarafından yürütülür bir arka plan ileti dizisindeloadXmlFromNetwork()
yöntemini kullanır. Feed URL'sini parametresinden sonra bir değer girin.loadXmlFromNetwork()
yöntemini getirme ve işleme feed'i seçin. Tamamlandığında, bir sonuç dizesi geri verir.Handler
post
numaralı telefonu arar ana ileti dizisine dönmek için dizeyi döndürür ve kullanıcı arayüzünde görüntüler.
Kotlin
// Implementation of Kotlin coroutines used to download XML feed from stackoverflow.com. private fun downloadXml(vararg urls: String) { var result: String? = null lifecycleScope.launch(Dispatchers.IO) { result = try { loadXmlFromNetwork(urls[0]) } catch (e: IOException) { resources.getString(R.string.connection_error) } catch (e: XmlPullParserException) { resources.getString(R.string.xml_error) } withContext(Dispatchers.Main) { setContentView(R.layout.main) // Displays the HTML string in the UI via a WebView. findViewById<WebView>(R.id.webview)?.apply { loadData(result?: "", "text/html", null) } } } }
Java
// Implementation of Executor and Handler used to download XML feed asynchronously from stackoverflow.com. private void downloadXml(String... urls) { ExecutorService executor = Executors.newSingleThreadExecutor(); Handler handler = new Handler(Looper.getMainLooper()); executor.execute(() -> { String result; try { result = loadXmlFromNetwork(urls[0]); } catch (IOException e) { result = getResources().getString(R.string.connection_error); } catch (XmlPullParserException e) { result = getResources().getString(R.string.xml_error); } String finalResult = result; handler.post(() -> { setContentView(R.layout.main); // Displays the HTML string in the UI via a WebView. WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.loadData(finalResult, "text/html", null); }); }); }
Çağrılacak loadXmlFromNetwork()
yöntemi
Sonraki snippet'te downloadXml
gösteriliyor. Aşağıdaki işlemler gerçekleşir:
StackOverflowXmlParser
oluşturur. Aynı zamandaEntry
nesnedenList
tanesi (entries
) ve içintitle
,url
vesummary
, özelliklerini koruyacak bu alanlar için XML feed'inden ayıklanmış değerler.downloadUrl()
öğesini çağırarak feed'i getirir veInputStream
.InputStream
öğesini ayrıştırmak içinStackOverflowXmlParser
kullanır.StackOverflowXmlParser
, bir Feed'den alınan verilerle birlikteList
/entries
.entries
List
işlemini işler ve feed verilerini HTML işaretlemesiyle birleştirir.- Ana etkinlikte görüntülenen bir HTML dizesini döndürür Kullanıcı arayüzü.
Kotlin
// Uploads XML from stackoverflow.com, parses it, and combines it with // HTML markup. Returns HTML string. @Throws(XmlPullParserException::class, IOException::class) private fun loadXmlFromNetwork(urlString: String): String { // Checks whether the user set the preference to include summary text. val pref: Boolean = PreferenceManager.getDefaultSharedPreferences(this)?.run { getBoolean("summaryPref", false) } ?: false val entries: List<Entry> = downloadUrl(urlString)?.use { stream -> // Instantiates the parser. StackOverflowXmlParser().parse(stream) } ?: emptyList() return StringBuilder().apply { append("<h3>${resources.getString(R.string.page_title)}</h3>") append("<em>${resources.getString(R.string.updated)} ") append("${formatter.format(rightNow.time)}</em>") // StackOverflowXmlParser returns a List (called "entries") of Entry objects. // Each Entry object represents a single post in the XML feed. // This section processes the entries list to combine each entry with HTML markup. // Each entry is displayed in the UI as a link that optionally includes // a text summary. entries.forEach { entry -> append("<p><a href='") append(entry.link) append("'>" + entry.title + "</a></p>") // If the user set the preference to include summary text, // adds it to the display. if (pref) { append(entry.summary) } } }.toString() } // Given a string representation of a URL, sets up a connection and gets // an input stream. @Throws(IOException::class) private fun downloadUrl(urlString: String): InputStream? { val url = URL(urlString) return (url.openConnection() as? HttpURLConnection)?.run { readTimeout = 10000 connectTimeout = 15000 requestMethod = "GET" doInput = true // Starts the query. connect() inputStream } }
Java
// Uploads XML from stackoverflow.com, parses it, and combines it with // HTML markup. Returns HTML string. private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException { InputStream stream = null; // Instantiates the parser. StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser(); List<Entry> entries = null; String title = null; String url = null; String summary = null; Calendar rightNow = Calendar.getInstance(); DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa"); // Checks whether the user set the preference to include summary text. SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); boolean pref = sharedPrefs.getBoolean("summaryPref", false); StringBuilder htmlString = new StringBuilder(); htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>"); htmlString.append("<em>" + getResources().getString(R.string.updated) + " " + formatter.format(rightNow.getTime()) + "</em>"); try { stream = downloadUrl(urlString); entries = stackOverflowXmlParser.parse(stream); // Makes sure that the InputStream is closed after the app is // finished using it. } finally { if (stream != null) { stream.close(); } } // StackOverflowXmlParser returns a List (called "entries") of Entry objects. // Each Entry object represents a single post in the XML feed. // This section processes the entries list to combine each entry with HTML markup. // Each entry is displayed in the UI as a link that optionally includes // a text summary. for (Entry entry : entries) { htmlString.append("<p><a href='"); htmlString.append(entry.link); htmlString.append("'>" + entry.title + "</a></p>"); // If the user set the preference to include summary text, // adds it to the display. if (pref) { htmlString.append(entry.summary); } } return htmlString.toString(); } // Given a string representation of a URL, sets up a connection and gets // an input stream. private InputStream downloadUrl(String urlString) throws IOException { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000 /* milliseconds */); conn.setConnectTimeout(15000 /* milliseconds */); conn.setRequestMethod("GET"); conn.setDoInput(true); // Starts the query. conn.connect(); return conn.getInputStream(); }