Pull Parser works on file in assets folder, but not same file in internal storage

1.2k views Asked by At

I'm having trouble parsing an xml file from internal storage. I have a xml file that looks like this:

<?xml version="1.0" encoding="utf-8"?>
<entries>
    <entry>
        <name>Name 1</name>
        <submenu>Submenu 1</submenu>
        <color>Color 1</color>
    </entry>
    <entry>
        <name>Name 2</name>
        <submenu>Submenu 2</submenu>
        <color>Color 2</color>
    </entry>
    <entry>
        <name>Name 3</name>
        <submenu>Submenu 3</submenu>
        <color>Color 3</color>
    </entry>
</entries>

I have a copy of this xml file in my assets folder called test.xml and I have a copy on my webserver called test.xml. When I try to parse the file from my assets folder, everything works great. When I download the file from the internet and try to parse it from internal storage, it doesn't work. Here is my activity code which has all the functionality in it:

package com.example.testdl;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;


import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.Xml;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

ProgressDialog mProgressDialog;
Button button;
int x = 0;

String URL = "http://www.classicknightstudio.com/marthasvillans/test.xml";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTitle("Test1");
    setContentView(R.layout.activity_main);

    button = (Button) findViewById(R.id.myButton);
    button.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View arg0) {

            if (x == 0) {

                // Execute DownloadFile AsyncTask
                new DownloadFile().execute(URL);
                x = x + 1;

            } else {

                logNames();

            }
        }

    });

}

// DownloadFile AsyncTask
private class DownloadFile extends AsyncTask<String, Integer, String> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();

        mProgressDialog = new ProgressDialog(MainActivity.this);
        mProgressDialog.setTitle("Downloads");
        mProgressDialog.setMessage("Downloading, Please Wait!");
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setMax(100);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.show();
    }

    @Override
    protected String doInBackground(String... sUrl) {
        try {
            URL url = new URL(sUrl[0]);
            URLConnection connection = url.openConnection();
            connection.connect();

            // Detect the file length
            int fileLength = connection.getContentLength();

            // Download the file
            InputStream input = new     BufferedInputStream(url.openStream());

            FileOutputStream output = openFileOutput("test.xml",
                    Context.MODE_PRIVATE);
            output.write("test.xml".getBytes());

            byte data[] = new byte[1024];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                total += count;
                // Publish the progress
                publishProgress((int) (total * 100 / fileLength));
                output.write(data, 0, count);
            }

            // Close connection
            output.flush();
            output.close();
            input.close();
        } catch (Exception e) {
            // Error Log
            Log.e("Error", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);

        mProgressDialog.setProgress(progress[0]);
    }
}


private class XmlParser {

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

    private List<Entry> readFeed(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        List<Entry> entries = new ArrayList<Entry>();

        parser.require(XmlPullParser.START_TAG, ns, "entries");
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();

            if (name.equals("entry")) {
                entries.add(readEntry(parser));
            } else {
                skip(parser);
            }
        }
        return entries;
    }

    private Entry readEntry(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        parser.require(XmlPullParser.START_TAG, ns, "entry");
        String titleName = null;
        String submenu = null;
        String color = null;
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();
            if (name.equals("name")) {
                titleName = readTitleName(parser);
            } else if (name.equals("submenu")) {
                submenu = readSubmenu(parser);
            } else if (name.equals("color")) {
                color = readColor(parser);
            } else {
                skip(parser);
            }
        }
        return new Entry(titleName, submenu, color);
    }

    private String readTitleName(XmlPullParser parser) throws IOException,
            XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "name");
        String titleName = readText(parser);
        parser.require(XmlPullParser.END_TAG, ns, "name");
        return titleName;
    }

    private String readSubmenu(XmlPullParser parser) throws IOException,
            XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "submenu");
        String submenu = readText(parser);
        parser.require(XmlPullParser.END_TAG, ns, "submenu");
        return submenu;
    }

    private String readColor(XmlPullParser parser) throws IOException,
            XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "color");
        String color = readText(parser);
        parser.require(XmlPullParser.END_TAG, ns, "color");
        return color;
    }

    private String readText(XmlPullParser parser) throws IOException,
            XmlPullParserException {
        String result = "";
        if (parser.next() == XmlPullParser.TEXT) {
            result = parser.getText();
            parser.nextTag();
        }
        return result;
    }

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

}

public static class Entry {
    public final String titleName;
    public final String submenu;
    public final String color;

    public Entry(String titleName, String submenu, String color) {
        this.titleName = titleName;
        this.submenu = submenu;
        this.color = color;
    }

    public String getTitleName() {
        return titleName;
    }

    public String getSubmenu() {
        return submenu;
    }

    public String getColor() {
        return color;
    }
}

protected void logNames() {

    File file = getBaseContext().getFileStreamPath("test.xml");

    if (file.exists()) {

        Log.d("TEST1", "file exists");

        try {

            // InputStream in = getApplicationContext().getAssets().open("test.xml");

            FileInputStream in = openFileInput("test.xml");

            XmlParser myParse = new XmlParser();

            try {

                @SuppressWarnings("unchecked")
                ArrayList<Entry> TheParse = (ArrayList<Entry>) myParse
                        .parse(in);

                for (int i = 0; i < TheParse.size(); i = i + 1) {
                    Log.d("TEST1", "" + TheParse.get(i).getTitleName());
                }

            } catch (XmlPullParserException e) {

                e.printStackTrace();
            }

        } catch (IOException e) {

            e.printStackTrace();
        }

    }

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

}

The activity is set up that when you press the button the first time, it downloads the file from the web. Then when you press the button a second time, it logs the name of each entry in the logcat.

In the logNames method, the code that i posted is using the FileInputStream in = openFileInput("test.xml");

Commented out above it is a stream that gets the file from the assets folder. I understand that to read the file from the assets folder, I don't need all the downloading stuff. So, if i comment out the:

FileInputStream in = openFileInput("test.xml");

in the logNames method and uncomment the:

InputStream in = getApplicationContext().getAssets().open("test.xml");

Then everything works fine and the names are displayed in the log cat. However, if i use the code as shown (attempting to download the xml from the web, save it to external storage, and then parse it), I get the following error:

08-12 04:38:56.899: W/System.err(17622): org.xmlpull.v1.XmlPullParserException:    Unexpected token (position:TEXT test.xml@1:9 in java.io.InputStreamReader@4278f850) 
08-12 04:38:56.909: W/System.err(17622):    at      org.kxml2.io.KXmlParser.next(KXmlParser.java:426)
08-12 04:38:56.909: W/System.err(17622):    at org.kxml2.io.KXmlParser.next(KXmlParser.java:310)
08-12 04:38:56.909: W/System.err(17622):    at org.kxml2.io.KXmlParser.nextTag(KXmlParser.java:2029)
08-12 04:38:56.909: W/System.err(17622):    at com.example.testdl.MainActivity$XmlParser.parse(MainActivity.java:143)
08-12 04:38:56.909: W/System.err(17622):    at com.example.testdl.MainActivity.logNames(MainActivity.java:292)
08-12 04:38:56.909: W/System.err(17622):    at com.example.testdl.MainActivity$1.onClick(MainActivity.java:59)
08-12 04:38:56.909: W/System.err(17622):    at android.view.View.performClick(View.java:4203)
08-12 04:38:56.909: W/System.err(17622):    at android.view.View$PerformClick.run(View.java:17189)
08-12 04:38:56.909: W/System.err(17622):    at android.os.Handler.handleCallback(Handler.java:615)
08-12 04:38:56.909: W/System.err(17622):    at android.os.Handler.dispatchMessage(Handler.java:92)
08-12 04:38:56.909: W/System.err(17622):    at android.os.Looper.loop(Looper.java:137)
08-12 04:38:56.909: W/System.err(17622):    at android.app.ActivityThread.main(ActivityThread.java:4950)
08-12 04:38:56.909: W/System.err(17622):    at java.lang.reflect.Method.invokeNative(Native Method)
08-12 04:38:56.909: W/System.err(17622):    at java.lang.reflect.Method.invoke(Method.java:511)
08-12 04:38:56.909: W/System.err(17622):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1004)
08-12 04:38:56.909: W/System.err(17622):    at c     com.android.internal.os.ZygoteInit.main(ZygoteInit.java:771)
08-12 04:38:56.919: W/System.err(17622):    at dalvik.system.NativeStart.main(Native    Method)
08-12 04:38:58.210: D/TEST1(17622): file exists
08-12 04:38:58.210: W/System.err(17622): org.xmlpull.v1.XmlPullParserException:    Unexpected token (position:TEXT test.xml@1:9 in java.io.InputStreamReader@4279b7c8) 
08-12 04:38:58.210: W/System.err(17622):    at org.kxml2.io.KXmlParser.next(KXmlParser.java:426)
08-12 04:38:58.210: W/System.err(17622):    at org.kxml2.io.KXmlParser.next(KXmlParser.java:310)
08-12 04:38:58.210: W/System.err(17622):    at org.kxml2.io.KXmlParser.nextTag(KXmlParser.java:2029)
08-12 04:38:58.210: W/System.err(17622):    at com.example.testdl.MainActivity$XmlParser.parse(MainActivity.java:143)
08-12 04:38:58.220: W/System.err(17622):    at com.example.testdl.MainActivity.logNames(MainActivity.java:292)
08-12 04:38:58.220: W/System.err(17622):    at com.example.testdl.MainActivity$1.onClick(MainActivity.java:59)
08-12 04:38:58.220: W/System.err(17622):    at android.view.View.performClick(View.java:4203)
08-12 04:38:58.220: W/System.err(17622):    at and android.view.View$PerformClick.run(View.java:17189)
08-12 04:38:58.220: W/System.err(17622):    at android.os.Handler.handleCallback(Handler.java:615)
08-12 04:38:58.220: W/System.err(17622):    at android.os.Handler.dispatchMessage(Handler.java:92)
08-12 04:38:58.220: W/System.err(17622):    at android.os.Looper.loop(Looper.java:137)
08-12 04:38:58.220: W/System.err(17622):    at android.app.ActivityThread.main(ActivityThread.java:4950)
08-12 04:38:58.220: W/System.err(17622):    at java.lang.reflect.Method.invokeNative(Native Method)
08-12 04:38:58.220: W/System.err(17622):    at java.lang.reflect.Method.invoke(Method.java:511)
08-12 04:38:58.220: W/System.err(17622):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1004)
08-12 04:38:58.220: W/System.err(17622):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:771)
08-12 04:38:58.220: W/System.err(17622):    at dalvik.system.NativeStart.main(Native Method)

the check for if the file exists in the logNames method passes and it looks like the file is indeed saved to internal storage. Any help is appreciated.

2

There are 2 answers

0
a2345sooted On BEST ANSWER

solved. It must have been something wrong in my downloader class. After rewriting it, I have it working, ... for multiple files too ! Here's the new downloader:

  private class GetXMLTask extends AsyncTask<String, Integer, Boolean> {

    private static final int DOWNLOAD_BUFFER_SIZE = 4096;
    int dlCount = 1;

    private Context context;

    int noOfURLs;

    public GetXMLTask(Context context) {
        this.context = context;
    }

    @Override
    protected Boolean doInBackground(String... urls) {
        noOfURLs = urls.length;


        for (String url : urls) {
            downloadFile(url);

        }

        return true;
    }

    private Boolean downloadFile(String urlString) {


        int count = 0;
        String basePath = "http://www.mysite.com/myFolder/";

        URL url;
        InputStream inputStream = null;
        BufferedOutputStream outputStream = null;

        File outFile;
        FileOutputStream fileStream;

        try {
            url = new URL(basePath + urlString);
            URLConnection connection = url.openConnection();
            int lenghtOfFile = connection.getContentLength();

            inputStream = new BufferedInputStream(url.openStream());


            outFile = new File(context.getFilesDir() + "/" + urlString);
            fileStream = new FileOutputStream(outFile);

            outputStream = new BufferedOutputStream(fileStream, DOWNLOAD_BUFFER_SIZE);

            byte data[] = new byte[1024];
            long total = 0;

            while ((count = inputStream.read(data)) != -1) {
                total += count;
                /*publishing progress update on UI thread.
                Invokes onProgressUpdate()*/
                publishProgress((int)((total*100)/lenghtOfFile));

                // writing data to byte array stream
                outputStream.write(data, 0, count);
            }
            outputStream.flush();



        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileUtils.close(inputStream);
            FileUtils.close(outputStream);
        }
        dlCount++;
        return true;
    }

    protected void onProgressUpdate(Integer... progress) {
        progressDialog.setProgress(progress[0]);

            progressDialog.setMessage("Downloading File " + dlCount + "/" + noOfURLs);

   }

   @Override
protected void onPostExecute(Boolean result) {

    super.onPostExecute(result);

    logNames();
    progressDialog.dismiss();
}   

}

To invoke it in the activity, I use:

/*Creating and executing background task*/
    GetXMLTask task = new GetXMLTask(getApplicationContext());
    task.execute(new String[] { URL3 });

    progressDialog = new ProgressDialog(this);
    progressDialog.setTitle(PROTOCOL_NAME);
    progressDialog.setMessage("Loading...");
    progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    progressDialog.setIndeterminate(false);
    progressDialog.setMax(100);
    progressDialog.setCancelable(true);
    progressDialog.show();

URL3 is a string I set. You could add however many strings you wanted there into that array. logNames() is a method to be called after finishing downloaded. Could be any method there. Hope this helps someone :)

6
Niranj Patel On
  1. If you have already InputStream on this line InputStream input = new BufferedInputStream(url.openStream()); then why are you convet into file and get back InputStream from file??
  2. Your file path is wrong, it should be full path like this. FileInputStream in = openFileInput("/mnt/sdcard/../test.xml");
  3. Check your file is sucessfully write or not?

Solution:

  1. No need to save file, you can directly use InputStream for parsing.
  2. Correct you file path..