5Answers
  • 3
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

Writing XML on Android

Given an instance of org.w3c.dom.Document, how do I save its contents to a file/stream?

EDIT: As CommonsWare pointed out, there's no such possibility using classes from Android SDK prior to Android 2.2 (API 8). Can you recommend then a third-party library for saving Document contents to a file/stream?

You can write xml like all others text files. For parsing Document to string I used:

public static String getStringFromNode(Node root) throws IOException {

        StringBuilder result = new StringBuilder();

        if (root.getNodeType() == 3)
            result.append(root.getNodeValue());
        else {
            if (root.getNodeType() != 9) {
                StringBuffer attrs = new StringBuffer();
                for (int k = 0; k < root.getAttributes().getLength(); ++k) {
                    attrs.append(" ").append(
                            root.getAttributes().item(k).getNodeName()).append(
                            "=\"").append(
                            root.getAttributes().item(k).getNodeValue())
                            .append("\" ");
                }
                result.append("<").append(root.getNodeName()).append(" ")
                        .append(attrs).append(">");
            } else {
                result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            }

            NodeList nodes = root.getChildNodes();
            for (int i = 0, j = nodes.getLength(); i < j; i++) {
                Node node = nodes.item(i);
                result.append(getStringFromNode(node));
            }

            if (root.getNodeType() != 9) {
                result.append("</").append(root.getNodeName()).append(">");
            }
        }
        return result.toString();
    }

But there is one more simple way to do this: http://www.ibm.com/developerworks/opensource/library/x-android/index.html#list11

private String writeXml(List<Message> messages){
    XmlSerializer serializer = Xml.newSerializer();
    StringWriter writer = new StringWriter();
    try {
        serializer.setOutput(writer);
        serializer.startDocument("UTF-8", true);
        serializer.startTag("", "messages");
        serializer.attribute("", "number", String.valueOf(messages.size()));
        for (Message msg: messages){
            serializer.startTag("", "message");
            serializer.attribute("", "date", msg.getDate());
            serializer.startTag("", "title");
            serializer.text(msg.getTitle());
            serializer.endTag("", "title");
            serializer.startTag("", "url");
            serializer.text(msg.getLink().toExternalForm());
            serializer.endTag("", "url");
            serializer.startTag("", "body");
            serializer.text(msg.getDescription());
            serializer.endTag("", "body");
            serializer.endTag("", "message");
        }
        serializer.endTag("", "messages");
        serializer.endDocument();
        return writer.toString();
    } catch (Exception e) {
        throw new RuntimeException(e);
    } 
}
  • 39
Reply Report
      • 2
    • getStringFromNode() function works perfectly for basic XML content (not tested for complex XML), thanks a lot. That helps a lot for Android versions < 2.2 when TransformerFactory cannot be used.
      • 2
    • Thanks for this answer. I know I could traverse the Document by myself but I'd rather not do this. The code you posted doesn't look production-ready/reliable :/

There is a very lightweight framework for reading and writing XML from annotated Java objects. It is fully compatible with Android.

http://simple.sourceforge.net

  • 20
Reply Report
      • 2
    • I tried this framework and it's awesome on android. It does use reflection but this probably won't be an issue for the majority of cases.
      • 1
    • It does use reflection, however reflection is performed only once to scan the XML schema based on the annotated classes. All reflection is cached to improve performance. It musch faster than similar frameworks such as JAXB and XStream, and on many scenarios is even faster than native Java XML serialization.
    • Awesome lib, this is what I use now. I use it in conjunction with ORMLite actually, and together I have just a few classes, all annotated with these two libs, and I can deserialize from XML and save to SQLite with just a few lines of code. Pretty awesome :)

Since API level 8 you can use:

javax.xml.transform.TransformerFactory factory = new javax.xml.transform.TransformerFactory();
javax.xml.transform.Transformer transformer = factory.newTransformer();

javax.xml.transform.dom.DOMSource domSource = new javax.xml.transform.dom.DOMSource(rootNode);
javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(outputStream);

transformer(domSource, result);
  • 16
Reply Report

Here's a solution for API Level 4. It requires an external library, however, the library is not large and makes this a lot easier.

I used XOM 1.2.6 and its core packages only jar file.

Full activity code including imports:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import nu.xom.converters.DOMConverter;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;

public class XOMTestActivity extends Activity {
    private static final String TAG = "XOMTestActivity";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        try {
            DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

            //Used XOM project.xml file for testing
            InputStream rawStream = this.getResources().openRawResource(R.raw.project);

            Document document = docBuilder.parse(rawStream);

            //API Level 4 will not always return a valid Document for XOM
            //So, find the root level element manually
            NodeList nodeList = document.getChildNodes();
            Node elementNode = null;
            for(int i = 0 ; i < nodeList.getLength() ; i++) {
                Node n = nodeList.item(i);
                if(n instanceof Element) {
                    elementNode = n;
                    break;
                }
            }

            //assuming there was a root level element
            DocumentFragment docFragment = document.createDocumentFragment();
            docFragment.appendChild(elementNode);

            nu.xom.Nodes nodes = DOMConverter.convert(docFragment);
            nu.xom.Document xomDoc = new nu.xom.Document((nu.xom.Element) nodes.get(0));

            Log.d(TAG, "onCreate: " + xomDoc.toXML());

            String outFile =
                    Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "wc3-xom-doc.xml";

            Writer writer = new FileWriter(outFile);
            writer.write(xomDoc.toXML());
            writer.close();
        } catch(DOMException de) {
            Log.e(TAG, "onCreate: dom exception: " + de.code, de);
        } catch(Exception e) {
            Log.e(TAG, "onCreate: exception", e);
        }

    }
}

It's not terribly long. It would be quite a bit shorter for API level 7+ since you can skip all the work required to find the root element. Resulting apk is 162k so I don't feel XOM adds much weight to a project.

The magic is in DOMConverter.

  • 10
Reply Report

I realize Isaac was looking for a solution using API level 4, but for others who can use a minimum level 8, here is a nice solution based off of what radek-k posted:

StringOutputStream.java:

import java.io.OutputStream;

class StringOutputStream extends OutputStream
{
    private StringBuilder m_string;

    StringOutputStream()
    {
        m_string = new StringBuilder();
    }

    @Override
    public void write(int b) throws IOException
    {
        m_string.append( (char) b );
    }

    @Override
    public String toString()
    {
        return m_string.toString();
    }
}

XMLHelper.java:

import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;


public class XMLhelper
{
    private static String serializeDocument(Document doc)
    {
        String xml = null;
        try
        {
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            Properties outFormat = new Properties();
            outFormat.setProperty( OutputKeys.INDENT, "yes" );
            outFormat.setProperty( OutputKeys.METHOD, "xml" );
            outFormat.setProperty( OutputKeys.OMIT_XML_DECLARATION, "no" );
            outFormat.setProperty( OutputKeys.VERSION, "1.0" );
            outFormat.setProperty( OutputKeys.ENCODING, "UTF-8" );
            transformer.setOutputProperties( outFormat );

            DOMSource domSource = new DOMSource( doc.getDocumentElement() );
            OutputStream output = new StringOutputStream();
            StreamResult result = new StreamResult( output );
            transformer.transform( domSource, result );

            xml = output.toString();
            android.util.Log.i( "XMLHELPER", xml );
        }
        catch (TransformerConfigurationException e)
        {
            android.util.Log.d( "XMLHELPER", "Exception: " + e );
            e.printStackTrace();
        }
        catch (TransformerException e)
        {
            android.util.Log.d( "XMLHELPER", "Exception: " + e );
            e.printStackTrace();
        }

        return xml;
    }
}
  • 7
Reply Report