تجزیه داده‌های XML

«زبان نشانه‌گذاری گسترش‌پذیر» (XML) مجموعه‌ای از قوانین برای کدبندی اسناد به شکل ماشین‌خوانا است. XML قالب محبوبی برای هم‌رسانی داده‌ها در اینترنت است.

وب‌سایت‌هایی که محتوای خود را به‌طور مکرر به‌روز می‌کنند، مانند سایت‌های خبری یا وبلاگ‌ها، اغلب یک فید XML ارائه می‌دهند تا برنامه‌های خارجی بتوانند از تغییرات محتوا مطلع شوند. بارگذاری و تجزیه داده‌های XML یکی از کارهای رایج برای برنامه‌های متصل به شبکه است. این موضوع نحوه تجزیه اسناد XML و استفاده از داده‌های آن‌ها را توضیح می‌دهد.

برای کسب اطلاعات بیشتر درباره ایجاد محتوای مبتنی بر وب در برنامه Android، به محتوای مبتنی بر وب مراجعه کنید.

انتخاب تجزیه‌کننده

XmlPullParser را توصیه می‌کنیم که روشی کارآمد و قابل‌نگهداری برای تجزیه XML در Android است. ‫Android دو پیاده‌سازی از این میانجی دارد:

هرکدام را انتخاب کنید خوب است. مثال این بخش از ExpatPullParser و Xml.newPullParser() استفاده می‌کند.

تجزیه‌وتحلیل فید

اولین گام در تجزیه فید این است که تصمیم بگیرید به کدام فیلدها علاقه دارید. تجزیه‌کننده داده‌های آن فیلدها را استخراج می‌کند و بقیه را نادیده می‌گیرد.

گزیده زیر را از فید تجزیه‌شده در برنامه نمونه ببینید. هر پست به StackOverflow.com در فید به‌عنوان یک برچسب entry ظاهر می‌شود که شامل چندین برچسب تودرتو است:

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

برنامه نمونه داده‌های برچسب entry و برچسب‌های تودرتوی آن title، link، و summary را استخراج می‌کند.

نمونه‌سازی تجزیه‌کننده

مرحله بعدی در تجزیه فید، نمونه‌سازی تجزیه‌کننده و شروع فرایند تجزیه است. این قطعه کد تجزیه‌کننده‌ای را مقداردهی اولیه می‌کند تا فضاهای نام را پردازش نکند و از InputStream ارائه‌شده به‌عنوان ورودی استفاده کند. فرایند تجزیه را با فراخوانی nextTag() شروع می‌کند و روش readFeed() را فراخوانی می‌کند که داده‌های موردنظر برنامه را استخراج و پردازش می‌کند:

کاتلین

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

جاوا

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

خواندن فید

روش readFeed() کار واقعی پردازش فید را انجام می‌دهد. این برنامه به‌دنبال عناصر برچسب‌گذاری‌شده با «entry» می‌گردد تا به‌عنوان نقطه شروعی برای پردازش بازگشتی فید استفاده کند. اگر برچسبی برچسب entry نباشد، از آن صرف‌نظر می‌کند. وقتی کل فید به‌صورت بازگشتی پردازش شد، readFeed() یک List برمی‌گرداند که حاوی ورودی‌هایی (ازجمله اعضای داده‌های تودرتو) است که از فید استخراج کرده است. سپس این List توسط تجزیه‌کننده برگردانده می‌شود.

کاتلین

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

جاوا

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

مراحل تجزیه فید XML از قرار زیر است:

  1. همان‌طور که در تجزیه‌وتحلیل فید توضیح داده شده است، برچسب‌هایی را که می‌خواهید در برنامه‌تان گنجانده شود شناسایی کنید. این نمونه داده‌های برچسب entry و برچسب‌های تودرتوی آن را استخراج می‌کند: title، link، و summary.
  2. روش‌های زیر را ایجاد کنید:

    • یک روش «خواندن» برای هر برچسبی که می‌خواهید اضافه کنید، مانند readEntry() و readTitle(). تجزیه‌کننده برچسب‌های را از جریان ورودی می‌خواند. وقتی با برچسبی با نام، در این مثال، entry، title، link یا summary مواجه می‌شود، روش مناسب برای آن برچسب را فراخوانی می‌کند. درغیراین‌صورت، برچسب را رد می‌کند.
    • روش‌های استخراج داده برای هر نوع برچسب متفاوت و پیش بردن تجزیه‌کننده به برچسب بعدی. در این مثال، روش‌های مربوطه به‌صورت زیر است:
      • برای برچسب‌های title و summary، تجزیه‌کننده readText() را فراخوانی می‌کند. این روش داده‌های این برچسب‌ها را با فراخوانی parser.getText() استخراج می‌کند.
      • برای برچسب link، تجزیه‌کننده ابتدا با تعیین اینکه آیا پیوند از نوعی است که به آن علاقه دارد یا نه، داده‌های پیوندها را استخراج می‌کند. سپس از parser.getAttributeValue() برای استخراج مقدار پیوند استفاده می‌کند.
      • برای برچسب entry، تجزیه‌کننده readEntry() را فراخوانی می‌کند. این روش برچسب‌های تودرتوی ورودی را تجزیه می‌کند و یک شیء Entry با اعضای داده title، link، و summary برمی‌گرداند.
    • روش کمکی skip() که بازگشتی است. برای بحث بیشتر درباره این موضوع، به رد شدن از برچسب‌هایی که برایتان مهم نیست مراجعه کنید.

این قطعه کد نشان می‌دهد که تجزیه‌کننده چگونه ورودی‌ها، عنوان‌ها، پیوندها، و خلاصه‌ها را تجزیه می‌کند.

کاتلین

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

جاوا

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

برچسب‌هایی را که برایتان مهم نیست رد کنید

تجزیه‌کننده باید برچسب‌هایی را که به آن‌ها علاقه‌ای ندارد رد کند. در اینجا روش skip() تجزیه‌کننده آمده است:

کاتلین

@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++
        }
    }
}

جاوا

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

نحوه کار به این صورت است:

  • اگر رویداد کنونی START_TAG نباشد، استثنایی ایجاد می‌کند.
  • این START_TAG و همه رویدادها را تا و ازجمله END_TAG منطبق مصرف می‌کند.
  • این ویژگی عمق تودرتو را ردیابی می‌کند تا مطمئن شود که در END_TAG صحیح متوقف می‌شود و نه در اولین برچسبی که پس‌از START_TAG اصلی با آن مواجه می‌شود.

بنابراین، اگر عنصر فعلی دارای عناصر تودرتو باشد، مقدار depth تا زمانی که تجزیه‌کننده همه رویدادهای بین START_TAG اصلی و END_TAG منطبق با آن را مصرف نکند، ۰ نخواهد بود. برای مثال، درنظر بگیرید که تجزیه‌کننده چگونه از عنصر <author>، که دارای ۲ عنصر تودرتو، <name> و <uri> است، رد می‌شود:

  • اولین‌باری که تجزیه‌کننده از حلقه while عبور می‌کند، برچسب بعدی که پس‌از <author> با آن مواجه می‌شود START_TAG برای <name> است. مقدار depth به ۲ افزایش می‌یابد.
  • ‫The second time through the while loop, the next tag the parser encounters is the END_TAG </name>. مقدار برای depth به ۱ کاهش می‌یابد.
  • در سومین باری که تجزیه‌کننده از حلقه while عبور می‌کند، برچسب بعدی که با آن مواجه می‌شود START_TAG <uri> است. مقدار برای depth به ۲ افزایش می‌یابد.
  • ‫در چهارمین باری که حلقه while اجرا می‌شود، برچسب بعدی که تجزیه‌کننده با آن مواجه می‌شود END_TAG </uri> است. مقدار depth به ۱ کاهش می‌یابد.
  • در پنجمین و آخرین باری که تجزیه‌کننده در حلقه while قرار می‌گیرد، برچسب بعدی که با آن مواجه می‌شود END_TAG </author> است. مقدار depth به ۰ کاهش می‌یابد که نشان می‌دهد عنصر <author> با موفقیت رد شده است.

مصرف داده‌های XML

برنامه نمونه، فید XML را به‌صورت ناهمگام واکشی و تجزیه می‌کند. این کار پردازش را از رشته میانای کاربر اصلی خارج می‌کند. وقتی پردازش کامل شد، برنامه واسط کاربر را در فعالیت اصلی خود، NetworkActivity، به‌روز می‌کند.

در گزیده زیر، روش loadPage() کارهای زیر را انجام می‌دهد:

  • متغیر رشته‌ای را با نشانی وب برای فید XML مقداردهی اولیه می‌کند.
  • اگر تنظیمات کاربر و اتصال شبکه اجازه دهد، روش downloadXml(url) را فراخوانی می‌کند. این روش فید را بارگیری و تجزیه می‌کند و نتیجه رشته‌ای را برای نمایش در واسط کاربر برمی‌گرداند.

کاتلین

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

جاوا

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 روش‌های زیر را در Kotlin فراخوانی می‌کند:

  • ‫lifecycleScope.launch(Dispatchers.IO)، که از روال‌های مشترک Kotlin برای راه‌اندازی روش loadXmlFromNetwork() در رشته IO استفاده می‌کند. این نشانی وب فید را به‌عنوان پارامتر عبور می‌دهد. روش loadXmlFromNetwork() فید را واکشی و پردازش می‌کند. وقتی کارش تمام می‌شود، رشته نتیجه را برمی‌گرداند.
  • ‫withContext(Dispatchers.Main) که از روال‌های مشترک Kotlin برای بازگشت به رشته اصلی استفاده می‌کند، رشته برگردانده‌شده را می‌گیرد و آن را در واسط کاربر نمایش می‌دهد.

در زبان برنامه‌نویسی Java، فرایند به‌صورت زیر است:

  • Executor روش loadXmlFromNetwork() را در رشته پس‌زمینه اجرا می‌کند. این نشانی وب فید را به‌عنوان پارامتر عبور می‌دهد. روش loadXmlFromNetwork() فید را واکشی و پردازش می‌کند. وقتی کارش تمام می‌شود، رشته نتیجه را برمی‌گرداند.
  • ‫Handler با post تماس می‌گیرد تا به رشته اصلی برگردد، رشته برگردانده‌شده را می‌گیرد، و آن را در میانای کاربر نمایش می‌دهد.

کاتلین

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

جاوا

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

روش loadXmlFromNetwork() که از downloadXml فراخوانده می‌شود در قطعه کد بعدی نشان داده شده است. این ویژگی کارهای زیر را انجام می‌دهد:

  1. StackOverflowXmlParser را نمونه‌سازی می‌کند. همچنین متغیرهایی برای List از اشیاء Entry (entries) و برای title، url، و summary ایجاد می‌کند تا مقادیر استخراج‌شده از فید XML را برای آن فیلدها نگه دارد.
  2. downloadUrl() را فراخوانی می‌کند که فید را واکشی می‌کند و آن را به‌عنوان InputStream برمی‌گرداند.
  3. از StackOverflowXmlParser برای تجزیه InputStream استفاده می‌کند. StackOverflowXmlParser List از entries را با داده‌های فید پر می‌کند.
  4. entries List را پردازش می‌کند و داده‌های فید را با نشان‌گذاری HTML ترکیب می‌کند.
  5. رشته HTML را که در واسط کاربر فعالیت اصلی نمایش داده می‌شود برمی‌گرداند.

کاتلین

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

جاوا

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