在 Android 中使用 SAX 解析 XML
如何在 Android 中使用 SAX 解析 XML。
引言
Android 应用程序可以使用来自 Web 的远程 XML 数据,或本地文件系统中的 XML 数据。Java 语言提供了多种处理 XML 数据的实用工具,包括 SAX 和 DOM 解析器。在本教程中,我们将使用更高效的 SAX 解析器。我们将处理存储在 Internet 位置的 XML 文件,并使用检索到的数据来构建 Android 应用的用户界面元素。您应该能够根据自己 Android 项目的需求来调整代码。
准备 XML 数据
如果您已有正在处理的 XML 数据,可以通过对 Java 代码进行一些修改来将其用于本教程。如果您想为项目创建数据源,请使用 Liquid XML Studio 来生成您的 XML 代码。如果您有 XSD(XML Schema Definition),可以自动从 XSD 构建 XML,或者可以从表或树创建 XML。或者,使用代码编辑器手动构建 XML 元素和属性。获得 XML 数据后,请确保您也使用该软件进行验证,并在尝试在 Android 应用中解析文件之前解决任何错误。
本教程将展示如何从 Web 位置加载 XML 文件,但您也可以从本地文件系统加载。如果您想使用通过 Internet 加载的数据,请将您的 XML 文件上传到 Web 服务器,并记下其存储位置。
本教程中的 Java 代码期望 XML 数据具有此结构
<?xml version="1.0" encoding="utf-8"?>
<appdata>
<brand name="Lovely Products">
<product>Hat</product>
<product>Gloves</product>
</brand>
<brand name="Great Things">
<product>Table</product>
<product>Chair</product>
<product>Bed</product>
</brand>
</appdata>
您可以使用此作为参考来理解这些步骤,从而调整 Java 代码以适应您自己的 XML。
创建或打开 Android 项目
如果您已有正在处理的项目,请打开它。否则,请创建一个新项目。如果您打算使用通过 Internet 加载的 XML 数据,请在项目的 manifest 文件中添加以下行
<uses-permission android:name="android.permission.INTERNET" />
创建解析类
在项目中创建一个新类。该类将解析导入的 XML 数据。使用以下大纲,并根据您选择的类名进行调整
public class DataHandler extends DefaultHandler {
//class declaration goes here
}
该类扩展了 DefaultHandler
,它提供了 SAX 解析工具。在此类声明行上方,添加以下 import
语句
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.widget.TextView;
创建类实例变量和构造函数方法
将以下代码添加到您的类声明中
//list for imported product data
private ArrayList<TextView> theViews;
//string to track each entry
private String currBrand = "";
//flag to keep track of XML processing
private boolean isProduct = false;
//context for user interface
private Context theContext;
//constructor
public DataHandler(Context cont) {
super();
theViews = new ArrayList<TextView>();
theContext = cont;
}
构造函数方法仅调用父类的方法,实例化用于保存从 XML 数据源读取的产品的列表,并实例化传递的上下文对象,以便我们可以创建用户界面元素。
添加标准的 SAX 方法
在您的 DefaultHandler
类内部,标准的 SAX 方法将解析 XML 数据。当程序遇到文档的开始和结束、元素的开始和结束标签以及元素内容时,会自动调用这些方法。在构造函数方法之后添加以下方法大纲
//start of the XML document
public void startDocument () { Log.i("DataHandler", "Start of XML document"); }
//end of the XML document
public void endDocument () { Log.i("DataHandler", "End of XML document"); }
//opening element tag
public void startElement (String uri, String name, String qName, Attributes atts)
{
//handle the start of an element
}
//closing element tag
public void endElement (String uri, String name, String qName)
{
//handle the end of an element
}
//element content
public void characters (char ch[], int start, int length)
{
//process the element content
}
当程序遇到文档的开始或结束时,我们不需要它执行任何操作,因此只需输出状态更新到 Android 日志以进行测试。接下来我们将完成其他三个方法。
处理每个元素的开始
startElement
方法可以访问其开始标签中的元素名称,以及它拥有的任何属性。我们将为数据中的每个品牌元素创建一个 string
,并将品牌名称与该品牌的每个产品项一起列出。将以下代码添加到您的 startElement
方法中
//find out if the element is a brand
if(qName.equals("brand"))
{
//set product tag to false
isProduct = false;
//create View item for brand display
TextView brandView = new TextView(theContext);
brandView.setTextColor(Color.rgb(73, 136, 83));
//add the attribute value to the displayed text
String viewText = "Items from " + atts.getValue("name") + ":";
brandView.setText(viewText);
//add the new view to the list
theViews.add(brandView);
}
//the element is a product
else if(qName.equals("product"))
isProduct = true;
当程序遇到品牌元素时,我们将为该品牌要显示的数据创建一个新的 View
项,其中包含名称和一些信息性文本。当您使用 SAX 处理 XML 数据时,程序会以线性方式遍历数据,因此标志可以帮助检测在每个方法执行时您的应用程序处于文档的哪个点。
处理每个元素的结束
将以下代码添加到您的 endElement
方法中
if(qName.equals("brand"))
{
//create a View item for the products
TextView productView = new TextView(theContext);
productView.setTextColor(Color.rgb(192, 199, 95));
//display the compiled items
productView.setText(currBrand);
//add to the list
theViews.add(productView);
//reset the variable for future items
currBrand = "";
}
当 endElement
方法遇到品牌元素的结束标签时,我们将构建的 string
添加到一个新的 View
中,然后将其添加到列表中,并将 string
重置为空,为下一个品牌元素做准备。
处理每个元素的内容
characters
方法处理 XML 元素内容。此方法必须考虑 XML 数据中可能出现的各种类型的空白。在此应用程序中,我们仅忽略空白。将以下代码添加到您的 characters
方法中
//string to store the character content
String currText = "";
//loop through the character array
for (int i=start; i<start+length; i++)
{
switch (ch[i]) {
case '\\':
break;
case '"':
break;
case '\n':
break;
case '\r':
break;
case '\t':
break;
default:
currText += ch[i];
break;
}
}
//prepare for the next item
if(isProduct && currText.length()>0)
currBrand += currText+"\n";
该方法接收一个包含元素内容的字符数组,因此代码在一个循环结构中遍历此数组。我们将每个字符添加到 string
中,最终得到完整的元素内容,然后将其添加到当前项文本中。
将数据提供给应用程序上下文
您的应用程序需要访问已解析的数据,例如在用户界面中显示它,因此在 characters
方法之后,添加一个 public
方法供其他类调用
public ArrayList<TextView> getData()
{
//take care of SAX, input and parsing errors
try
{
//set the parsing driver
System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");
//create a parser
SAXParserFactory parseFactory = SAXParserFactory.newInstance();
SAXParser xmlParser = parseFactory.newSAXParser();
//get an XML reader
XMLReader xmlIn = xmlParser.getXMLReader();
//instruct the app to use this object as the handler
xmlIn.setContentHandler(this);
//provide the name and location of the XML file **ALTER THIS FOR YOUR FILE**
URL xmlURL = new URL("http://mydomain.com/mydata.xml");
//open the connection and get an input stream
URLConnection xmlConn = xmlURL.openConnection();
InputStreamReader xmlStream = new InputStreamReader(xmlConn.getInputStream());
//build a buffered reader
BufferedReader xmlBuff = new BufferedReader(xmlStream);
//parse the data
xmlIn.parse(new InputSource(xmlBuff));
}
catch(SAXException se) { Log.e("AndroidTestsActivity",
"SAX Error " + se.getMessage()); }
catch(IOException ie) { Log.e("AndroidTestsActivity",
"Input Error " + ie.getMessage()); }
catch(Exception oe) { Log.e("AndroidTestsActivity",
"Unspecified Error " + oe.getMessage()); }
//return the parsed product list
return theViews;
}
此代码必须包含在 try
块中,并为每种可能的异常类型添加 catch
块。该代码创建所需的 SAX 类的对象实例,通过 Internet 打开到 XML 文件的连接,最后指示应用程序解析数据。确保更改 URL 代码以匹配您自己的 XML 文件的名称和位置。解析完成后,此方法将产品数据列表返回到应用程序中的另一个类。
调用解析函数并显示数据
要从您的主应用程序 Activity
类调用 DefaultHandler
类,请将此代码添加到 onCreate
方法中
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//get a reference to the layout
LayoutInflater inflater = getLayoutInflater();
LinearLayout mainLayout = (LinearLayout) inflater.inflate(R.layout.main,null);
try
{
//create an instance of the DefaultHandler class
//**ALTER THIS FOR YOUR CLASS NAME**
DataHandler handler = new DataHandler(getApplicationContext());
//get the string list by calling the public method
ArrayList<TextView> newViews = handler.getData();
//convert to an array
Object[] products = newViews.toArray();
//loop through the items, creating a View item for each
for(int i=0; i<products.length; i++)
{
//add the next View in the list
mainLayout.addView((TextView)products[i]);
}
}
catch(Exception pce) { Log.e("AndroidTestsActivity", "PCE "+pce.getMessage()); }
setContentView(mainLayout);
}
此代码使用 XML 中的数据在应用程序用户界面中显示一系列 TextView
项。首先,我们获取对主布局的引用,然后创建一个 DefaultHandler
类的实例,传递应用程序上下文 - 确保更改代码以反映您自己的类名,而不是“DataHandler
”。然后,我们在创建的 DefaultHandler
对象上调用 getData
方法,以获取和解析数据,并将其作为 TextView
项的列表检索。将列表转换为数组后,我们将每个 TextView
项添加到布局中。try
块处理任何解析异常。最后,Activity
设置其主布局。
为这段代码添加以下 import
语句
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;
这是在 Android 模拟器上运行的最终显示

此应用程序仅在基本界面中显示解析的数据项。对于您自己的项目,您可以使用更复杂的显示方法,例如使项目具有交互性。无论您解析数据的目的是什么,都应该能够通过一些调整和添加来使用此代码以满足您的需求。
历史
- 2012 年 2 月 23 日:初始版本