Daniel Mendel
Daniel Mendel

Reputation: 506

Can I use the "Serializer"?

I have been looking around teh internetz for a quick and simple way to parse custom XML files which hold data elements and attributes as simple as this:

<book>
    <book type="sci-fi">
    <book cost="25">
</book>

And add to that I'll have a book class with these elements inside it. And simple as that I could create an XML: file reading a book class and convert it to XML. And also able to read an XML and convert into a new book class.

So I found this thing

Which includes these lines:

Serializer serializer = new Persister();
Example example = new Example("Example message", 123);
File result = new File("example.xml");

serializer.write(example, result);

Can I use these functions in Android apps? The Serializer and persister? Or is it called some how different?

Upvotes: 1

Views: 286

Answers (2)

Zoe - Save the data dump
Zoe - Save the data dump

Reputation: 28229

You can use an XML parser that comes with Android. Reference

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

This returns the parser, and returns the data. InputStream is the stream you read from.

Further, this is a copied example from the android documentation, with added explanations:

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

    parser.require(XmlPullParser.START_TAG, ns, "feed");//We read the parser inputted, but we try to find a specific tag. This can be the base tag, or if you have multiple of the same type, get the first.
    while (parser.next() != XmlPullParser.END_TAG) {//As long as the parser doesn't detect an end tag
        if (parser.getEventType() != XmlPullParser.START_TAG) {//and it isn't a start tag
            continue;
        }
        String name = parser.getName();//we parse the tag found by the reader
        //Then there are if-statements to determine what type of tag it is. This example uses a specific type to look for, as this looks for <entry>
        if (name.equals("entry")) {
            entries.add(readEntry(parser));//If it is that tag, add it to the list of content
        } else {
            skip(parser);//else skip it
        }
    }
    return entries;
}

Further, this can be applied to creating classes:

First, the entry class

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

Then the parsing:

// 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");//finds the children of the tag "entry"
    String title = null;
    String summary = null;
    String link = null;
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            continue;
        }
        //and using if-statements, we find the different types of tags, and distribute the content to the appropriate strings
        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);
        }
    }
    //And finally, we create a new entry
    return new Entry(title, summary, link);
}

//The following methods are the methods used to parse the different tags. 
//They are used as separate methods to make it more clear what goes where, 
//instead of pushing all these into the same method
// 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;
}
  ...
}

And this can be modified rather easily to fit your needs: change the tag names to your tags, and modify the example class to whatever you want. I am not sure if you can get the price for an instance from attributes, but if you add sub-tags, you may get the same result. Example:

<book>
    <type>sci-fi</type>
    <cost>
        <currency>USD</currency>
        <price>25</price>
    </cost>
</book>

With cost you can also apply 25 directly.

Or you can continue to use the attribute system, but you will need to add some more code. And further, the current XML file you have never closes the tags(<book type="sci-fi">), and you should not repeat the book-tag inside a book tag. Not because it is discouraged, but because you end up with a hard situation trying to find out what book tag the parser has found.


Further, to write an XML file, there are many different ways. For saving it locally, you can do it much more easily by using Serializable:

public class Something implements Serializable{
...
}

And then use Object[In/Out]putStream to read or write:

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("o.ser")));

    //Save the object
    oos.writeObject(instance);
    // close the writing.
    oos.close();

And to load:

ObjectInputStream ois= new ObjectInputStream(new FileInputStream(new File("o.ser")));

instance = (Something) ois.readObject();//or Something instance = (Something)...
ois.close;

But if you really want to save to a .xml file, here is one way:

(this uses internal storage, so this will be removed when the app is. The concept for external writing(public access areas) is the same, but a different writing method)

public static String getString(String basetag, List<String> tags/*Raw name, e.g. book*/, List<String> value){
    if(basetag == null || basetag.equals("") || basetag.equals(" ")){
        throw new IllegalArgumentException("There has to be a base tag.");
    }
    String retval = "<" + basetag + ">\n";
    if(tags == null || value == null){
        throw new IllegalArgumentException("There have to be tags and values!");
    }
    if(tags.size() != value.size()){
        throw new IllegalArgumentException("There has to be as many tags as there are values");
    }
    assert tags.size() != 0;
    assert value.size() != 0;

    for(int i = 0; i < tags.size(); i++){
        String s = "<%s>%s</%s>\n";
        retval += String.format(Locale.ENGLISH, s, tags.get(i), value.get(i), tags.get(i));
    }
    return retval;
}

public static void save(String filename, String data, Context c){

    if(data != null && filename != null) {
        try {
            //Create the file if not exists
            File file = new File(c.getFilesDir() + "/", filename);
            if (file.getParentFile().mkdirs())
                file.createNewFile();
            //Access file and write
            FileOutputStream fos = c.openFileOutput(filename, Context.MODE_PRIVATE);
            fos.write(data.getBytes());

            fos.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }else{
        throw new NullPointerException("Filename and/or data is null");
    }
}

private static List load(String fn, Context c){
    String collected = null;
    FileInputStream fis = null;
    try{

        fis = c.openFileInput(fn);
        return parse(fis);

    }catch(Exception e){
        e.printStackTrace();
        return null;
    }finally{
        try {
            fis.close();
        } catch(NullPointerException npe){
            return null;
        } catch (Exception e) {
            // TODO Auto-generated catch block

            e.printStackTrace();
        }
    }
}

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

To end of this post, I really suggest you read the android documentation on this subject as it contains a lot of valuable information that may be more up to date than this answer(if it is a long time since the answer has been updated)

Upvotes: 0

enjoy-writing
enjoy-writing

Reputation: 520

I think you want to save one object into a local file.

Yes, in android you can use ObjectOutputStream class to write Object(writeObject method), and use ObjectOutputStream to save into a file. The corresponding read function class is ObjectInputStream. But,the foremost thing is that your class object to save must implement Serializable interface

Upvotes: 1

Related Questions