XML-Daten parsen

Extensible Markup Language (XML) ist ein Regelsatz für die Codierung von Dokumenten in maschinenlesbares Format. XML ist ein beliebtes Format für den Austausch von Daten im Internet.

Websites, auf denen die Inhalte häufig aktualisiert werden, z. B. Nachrichtenwebsites oder Blogs, stellen häufig einen XML-Feed bereit, damit externe Programme über den Content informiert bleiben können. Änderungen. Das Hochladen und Parsen von XML-Daten ist eine gängige Aufgabe bei Apps. In diesem Thema wird erläutert, wie XML-Dokumente geparst und deren Daten verwendet werden.

Weitere Informationen zum Erstellen webbasierter Inhalte in deiner Android-App findest du unter Webbasierte Inhalte:

Parser auswählen

Wir empfehlen XmlPullParser, eine effiziente und eine verwaltbare Methode zum Parsen von XML unter Android. Android hat zwei Implementierungen dieser Schnittstelle:

Beide Optionen sind in Ordnung. Die Im Beispiel in diesem Abschnitt werden ExpatPullParser und Xml.newPullParser().

Feed analysieren

Der erste Schritt beim Parsen eines Feeds besteht darin, zu entscheiden, für welche Felder Sie sich interessieren. Der Parser extrahiert Daten für diese Felder und ignoriert den Rest.

Hier sehen Sie einen Auszug aus einem geparsten Feed in der Beispiel-App. Jedes Beitrag auf StackOverflow.com wird in der Feed als entry-Tag, das mehrere verschachtelte Tags enthält:

<?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>

Beispielanwendung extrahiert Daten für das entry-Tag und seine verschachtelten Tags title, link und summary.

Parser instanziieren

Der nächste Schritt beim Parsen eines Feeds einen Parser instanziieren und den Parsing-Prozess starten. Dieses Snippet initialisiert einen Parser, damit er keine Namespaces verarbeitet und den bereitgestellten InputStream als Eingabe verwendet. Er startet den Parsing-Prozess mit einem Aufruf an nextTag() und ruft den readFeed()-Methode, die die Daten der App extrahiert und verarbeitet Interesse an:

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 lesen

Die Methode readFeed() übernimmt die Verarbeitung des Feed. Es wird nach Elementen mit dem Tag „entry“ gesucht. als Ausgangspunkt für rekursive der Verarbeitung des Feeds. Wenn ein Tag kein entry-Tag ist, wird es übersprungen. Sobald alle Feed rekursiv verarbeitet wird, gibt readFeed() eine List mit den Einträgen (einschließlich verschachtelter Datenmitglieder), die aus dem Feed extrahiert wurden. Diese List wird dann vom parser.

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 parsen

Die Schritte zum Parsen eines XML-Feeds sind folgende:

  1. Identifizieren Sie wie unter Feed analysieren beschrieben die Tags, die Sie in Ihre App einbinden möchten. Dieses Im Beispiel werden Daten für das entry-Tag und seine verschachtelten Tags extrahiert: title, link und summary.
  2. Erstellen Sie die folgenden Methoden:

    • Ein „Lesen“ für die Tags, die Sie einbinden möchten, readEntry() und readTitle(). Der Parser liest Tags aus dem Eingabestream. Wenn in diesem Beispiel ein Tag namens entry, title, link oder summary wird die entsprechende Methode für dieses Tag. Andernfalls wird das Tag übersprungen.
    • Methoden zum Extrahieren von Daten für jeden Tag-Typ und zum Fortfahren der mit dem nächsten Tag verbinden. In diesem Beispiel sind folgende Methoden relevant: <ph type="x-smartling-placeholder">
        </ph>
      • Für die Tags title und summary ruft der Parser auf readText() Diese Methode extrahiert Daten für diese Tags, indem sie parser.getText()
      • Beim link-Tag extrahiert der Parser zuerst Daten für Links, indem er bestimmt, ob es sich um den Typ des Links handelt die für sie interessant sind. Dann wird parser.getAttributeValue() verwendet, um um den Linkwert zu extrahieren.
      • Für das entry-Tag ruft der Parser readEntry() auf. Diese Methode parst die verschachtelten Tags des Eintrags und gibt ein Entry-Objekt zurück mit den Datenelementen title, link und summary.
    • Eine skip()-Hilfsmethode, die rekursiv ist. Weitere Informationen zu diesem Thema finden Sie unter Unwichtige Tags überspringen.

Dieses Snippet zeigt, wie der Parser Einträge, Titel, Links und Zusammenfassungen parst.

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;
}
  ...
}

Unwichtige Tags überspringen

Der Parser muss Tags überspringen, die für ihn nicht relevant sind. Hier ist die skip()-Methode des Parsers:

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;
        }
    }
 }

So funktioniert's:

  • Es wird eine Ausnahme ausgelöst, wenn das aktuelle Ereignis kein START_TAG
  • Er verarbeitet die START_TAG und alle Ereignisse bis einschließlich den übereinstimmenden END_TAG.
  • Die Verschachtelungstiefe wird erfasst, um sicherzustellen, dass sie am richtigen END_TAG und nicht bei dem ersten Tag nach dem ursprünglichen START_TAG.

Wenn also das aktuelle Element verschachtelte Elemente enthält, wird der Wert von depth ist erst 0, wenn der Parser alle Ereignisse zwischen die ursprüngliche START_TAG und die zugehörige END_TAG. Für Stellen Sie sich vor, wie der Parser das Element <author> überspringt, mit 2 verschachtelten Elementen, <name> und <uri>:

  • Beim ersten Mal durch die while-Schleife wird das nächste Tag vom Parser gesendet. Treffer nach <author> sind die START_TAG für <name>. Der Wert für depth erhöht sich in 2.
  • Beim zweiten Mal durch die while-Schleife wird das nächste Tag vom Parser END_TAG </name>. Der Wert für depth wird auf 1 verringert.
  • Beim dritten Mal durch die while-Schleife wird der Parser als Nächstes START_TAG <uri>. Der Wert in depth-Schritten auf 2.
  • Beim vierten Mal über die while-Schleife wird das nächste Tag vom Parser END_TAG </uri>. Der Wert für depth verringert sich auf 1.
  • Beim fünften und letzten Mal über die while-Schleife, das nächste Mal Tag, auf den der Parser stößt, ist das END_TAG </author> Der Wert für depth verringert sich auf 0, was bedeutet, dass das <author>-Element erfolgreich war übersprungen.

XML-Daten verarbeiten

Die Beispielanwendung ruft den XML-Feed asynchron ab und parst ihn. Dadurch wird die Verarbeitung aus dem Haupt-UI-Thread entfernt. Wann? abgeschlossen ist, aktualisiert die App die UI in der Hauptaktivität, NetworkActivity

Im folgenden Auszug führt die Methode loadPage() Folgendes aus:

  • Initialisiert eine Stringvariable mit der URL für den XML-Feed.
  • Ruft die Methode downloadXml(url) auf, wenn die Einstellungen des Nutzers und das Netzwerk Verbindung zulassen. Diese Methode lädt den Feed herunter, parst ihn und gibt ein Zeichenfolgenergebnis zurück, die auf der Benutzeroberfläche angezeigt werden.

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.
        }
    }

Die Methode downloadXml ruft die folgenden Methoden in Kotlin auf:

  • lifecycleScope.launch(Dispatchers.IO), die Kotlin-Coroutinen verwendet, um starte die Methode loadXmlFromNetwork() im E/A-Thread. Die Feed-URL wird als . Die Methode loadXmlFromNetwork() ruft ab und verarbeitet sie. den Feed. Nach Abschluss des Vorgangs wird ein Ergebnisstring zurückgegeben.
  • withContext(Dispatchers.Main), die Kotlin-Koroutinen verwendet, um zum Hauptthread zurückzukehren, verwendet den zurückgegebener String und zeigt ihn auf der Benutzeroberfläche an.

In der Programmiersprache Java läuft der Prozess so ab:

  • Ein Executor-Objekt wird ausgeführt die loadXmlFromNetwork()-Methode in einem Hintergrundthread. Die Feed-URL wird als . Die Methode loadXmlFromNetwork() ruft ab und verarbeitet sie. den Feed. Nach Abschluss des Vorgangs wird ein Ergebnisstring zurückgegeben.
  • Ein Handler ruft post an um zum Hauptthread zurückzukehren, zurückgegebener String und zeigt ihn auf der Benutzeroberfläche an.

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);
        });
    });
}

Die Methode loadXmlFromNetwork(), die aus downloadXml wird im nächsten Snippet angezeigt. Dabei werden folgende Schritte ausgeführt:

  1. Instanziiert eine StackOverflowXmlParser. Außerdem werden Variablen für ein List von Entry Objekten (entries) und für title, url und summary für den -Werte, die aus dem XML-Feed für diese Felder extrahiert wurden.
  2. downloadUrl() aufrufen, wodurch der Feed abgerufen und als InputStream.
  3. Verwendet StackOverflowXmlParser zum Parsen von InputStream. StackOverflowXmlParser füllt ein List von entries mit Daten aus dem Feed.
  4. Verarbeitet entries List und kombiniert die Feed-Daten mit HTML-Markup.
  5. Gibt einen HTML-String zurück, der in der Hauptaktivität angezeigt wird UI.

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();
}