Xml解析 - XmlPullParser解析示例分析

Xml解析 - XmlPullParser解析示例分析

 

XmlPullParser是一种事件驱动的XML文件解析方式。由于Android系统已经集成了该部分,且Android framework中有关XML文件的解析的部分,基本都是使用这种方式去处理的;所以,我们有必要学习、了解XmlPullParser的解析过程及使用方法,以便我们阅读Android源码或在工作中使用。

XmlPullParser以事件驱动解析流程,我们平时常用的事件有:

  • XmlPullParser.START_DOCUMENT:XML解析器目前在文件的最开始处,此事还没有读到任何数据
  • XmlPullParser.END_DOCUMENT:逻辑意义上的文件末尾;表明当前XML解析器处理的文件流已经到末端
  • XmlPullParser.START_TAG:XML解析器解析到了一个开始标签
  • XmlPullParser.END_TAG:XML解析器解析到了一个结束标签
  • XmlPullParser.TEXT:XML解析器解析到了字符数据

 

另外还有其他的XmlPullParser.CDSECT、XmlPullParser.ENTITY_REF等事件,因这些事件较少涉及到,这里就不做分析了。

我们通过XmlPullParser解析器读取到每个事件后,都可以根据事件的类型来进行相应的处理。如,遇到XmlPullParser.START_TAG,我们就能获取到当前TAG的名字,然后根据我们自己设定的TAG的含义,去进行后续的解析、读取工作。

XmlPullParser使用中有时会涉及到当前解析的标签在整个XML结构中的所处的高度问题,我们通常调用getDepth()函数来获取当前TAG所处的高度值。下面我们借助一个示例,来看下XmlPullParser是如何定义TAG的高度的。

示例一

我们要解析的XML内容如下:

<?xml version="1.0" encoding="utf-8"?>
<root>
    
    <A>
        <B>
            <C>
                
            </C>
            <D />
        </B>
    </A>
    
    <E>
        <F />
    </E>
    
    <G />
    
</root>

 

接着使用XmlPullParser写一段XML解析代码,通过打印来看每个节点的高度值是怎样的:

public class XmlPullMain {

	public static void main(String[] args) {

		try {
//			InputStream xmlStream = new FileInputStream(new File("res/res.xml"));
//			XmlPullMain parserTool = new XmlPullMain();
//			parserTool.parserXmlByPull(xmlStream, "UTF-8");
			 InputStream xmlStream = new FileInputStream(new
			 File("res/test.xml"));
			 XmlPullMain parserTool = new XmlPullMain();
			 parserTool.testDepth(xmlStream, "UTF-8");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}

public void testDepth(InputStream xmlStream, String xmlEncode) {
		XmlPullParserFactory factory = null;
		XmlPullParser parser = null;
		try {
			factory = XmlPullParserFactory.newInstance();
			factory.setNamespaceAware(true);
			parser = factory.newPullParser();
			parser.setInput(xmlStream, xmlEncode);

			int event = parser.getEventType();
			System.out.println("start current depth: " + parser.getDepth());
			while (event != XmlPullParser.END_DOCUMENT) {
				switch (event) {
				case XmlPullParser.START_DOCUMENT:
					System.out.println("START_DOCUMENT depth: " + parser.getDepth());
					break;
				case XmlPullParser.START_TAG:
					String node = parser.getName();
					if (node.equals("root")) {
						System.out.println("root START_TAG depeth: " + parser.getDepth());
					} else if (node.equals("A")) {
						System.out.println("A START_TAG depeth: " + parser.getDepth());
					} else if (node.equals("B")) {
						System.out.println("B START_TAG depeth: " + parser.getDepth());
					} else if (node.equals("C")) {
						System.out.println("C START_TAG depeth: " + parser.getDepth());
					} else if (node.equals("D")) {
						System.out.println("D START_TAG depeth: " + parser.getDepth());
					} else if (node.equals("E")) {
						System.out.println("E START_TAG depeth: " + parser.getDepth());
					} else if (node.equals("F")) {
						System.out.println("F START_TAG depeth: " + parser.getDepth());
					} else if (node.equals("G")) {
						System.out.println("G START_TAG depeth: " + parser.getDepth());
					}
					break;
				case XmlPullParser.TEXT:
					System.out.println("--TEXT depth: " + parser.getDepth());
					break;
				case XmlPullParser.END_TAG:
					String nodeEnd = parser.getName();
					if (nodeEnd.equals("root")) {
						System.out.println("root END_TAG depeth: " + parser.getDepth());
					} else if (nodeEnd.equals("A")) {
						System.out.println("A END_TAG depeth: " + parser.getDepth());
					} else if (nodeEnd.equals("B")) {
						System.out.println("B END_TAG depeth: " + parser.getDepth());
					} else if (nodeEnd.equals("C")) {
						System.out.println("C END_TAG depeth: " + parser.getDepth());
					} else if (nodeEnd.equals("D")) {
						System.out.println("D END_TAG depeth: " + parser.getDepth());
					} else if (nodeEnd.equals("E")) {
						System.out.println("E END_TAG depeth: " + parser.getDepth());
					} else if (nodeEnd.equals("F")) {
						System.out.println("F END_TAG depeth: " + parser.getDepth());
					} else if (nodeEnd.equals("G")) {
						System.out.println("G END_TAG depeth: " + parser.getDepth());
					}
					break;
				case XmlPullParser.CDSECT:
					break;
				default:
					break;
				}
				event = parser.next();
			}
		} catch (Exception e) {

		}
	}

}

代码的执行结果如下:

start current depth: 0
START_DOCUMENT depth: 0
root START_TAG depeth: 1
--TEXT depth: 1
A START_TAG depeth: 2
--TEXT depth: 2
B START_TAG depeth: 3
--TEXT depth: 3
C START_TAG depeth: 4
--TEXT depth: 4
C END_TAG depeth: 4
--TEXT depth: 3
D START_TAG depeth: 4
D END_TAG depeth: 4
--TEXT depth: 3
B END_TAG depeth: 3
--TEXT depth: 2
A END_TAG depeth: 2
--TEXT depth: 1
E START_TAG depeth: 2
--TEXT depth: 2
F START_TAG depeth: 3
F END_TAG depeth: 3
--TEXT depth: 2
E END_TAG depeth: 2
--TEXT depth: 1
G START_TAG depeth: 2
G END_TAG depeth: 2
--TEXT depth: 1
root END_TAG depeth: 1

根据输出结果,我们可以在Xml文件中标记处各个节点的高度值:

<?xml version="1.0" encoding="utf-8"?>
<!-- 0 -->
<root><!-- 1 -->
    
    <A><!-- 2 -->
        <B><!-- 3 -->
            <C><!-- 4 -->
                
            </C><!-- 4 -->
            <D /><!-- 4 -->
        </B><!-- 3 -->
    </A><!-- 2 -->
    
    <E><!-- 2 -->
        <F /><!-- 3 -->
    </E><!-- 2 -->
    
    <G /><!-- 2 -->
    
</root><!-- 1 -->

 

注释标签中的整数值即为当前节点所处的高度,从结果可以看到:XmlPullParser对节点高度的处理是基于它所处的层次结构得出的。如果我们的XML文件写的层次清晰的话, 就很容易看出其中各个节点所处的高度情况。

另外,从输出结果,我们还看到:

  • <a> ... </a>这种形式的标签,解析时会读取到两个TEXT事件:一个在<a> START_TAG之后产生,一个在</a> END_TAG之后产生
  • <b />这种形式的标签只会在<b> END_TAG之后读取到一个TEXT事件,<b> START_TAG之后不会产生TEXT事件

这种TEXT事件有时候对我们是没有意义的,基于这种原因,我们在使用XmlPullParser解析Xml文件时,要根据自己的需要来处理得到的事件。

XmlPullParser的基本用法就跟上面示例所展示的那样:

  • 首先获取到需要解析的XML文件对应的InputStream流
  • 实例化XmlPullParserFactory工厂对象
  • 再借助工厂对象获取XmlPullParser类型的解析器实例

然后再进行解析即可。

XmlPullParser的使用模式较为固定,这里不再做过多说明;其他的问题可以看它的源码来得到答案。

示例二

下面再给出一个模仿Androidmainfest.xml配置文件解析过程的示例。

我们要解析的Xml文件内容:

<?xml version="1.0" encoding="utf-8"?>
<!-- 0 -->
<manifest package="com.example.androidtest" ><!-- 1 -->

    <uses-sdk
        minSdkVersion="19"
        targetSdkVersion="21">kitkat4.4</uses-sdk><!-- 2 -->

    <application
        allowBackup="true"
        icon="ic_launcher" ><!-- 2 -->
        
        <activity
            name="MainActivity"
            label="app_name" ><!-- 3 -->
            
            <intent-filter><!-- 4 -->
                <action name="android.intent.action.MAIN" /><!-- 5 -->

                <category name="android.intent.category.LAUNCHER" /><!-- 5 -->
            </intent-filter><!-- 4 -->
            
        </activity><!-- 3 -->
        
        <activity
            name="DefaultActivity"
            label="def_name" ><!-- 3 -->
            
            <intent-filter><!-- 4 -->
                <category name="android.intent.category.LAUNCHER" /><!-- 5 -->
            </intent-filter><!-- 4 -->
            
            <intent-filter><!-- 4 -->
                <category name="android.intent.category.HOME" /><!-- 5 -->
            </intent-filter><!-- 4 -->
            
        </activity><!-- 3 -->
        
    </application><!-- 2 -->
    
    <application
        allowBackup="false"
        icon="ic_launcher"><!-- 2 -->
        
        <activity
            name="TestActivity"
            label="test_name" ><!-- 3 -->
            
            <intent-filter><!-- 4 -->
                <category name="android.intent.category.LAUNCHER" /><!-- 5 -->
            </intent-filter><!-- 4 -->
            
        </activity><!-- 3 -->
        
    </application><!-- 2 -->

</manifest><!-- 1 -->
<!-- 0 -->

这里只是模仿了Android配置文件中的形式。再看具体的解析代码:

public class XmlPullMain {

	public static void main(String[] args) {

		try {
			InputStream xmlStream = new FileInputStream(new File("res/res.xml"));
			XmlPullMain parserTool = new XmlPullMain();
			parserTool.parserXmlByPull(xmlStream, "UTF-8");
//			 InputStream xmlStream = new FileInputStream(new
//			 File("res/test.xml"));
//			 XmlPullMain parserTool = new XmlPullMain();
//			 parserTool.testDepth(xmlStream, "UTF-8");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}

	public void parserXmlByPull(InputStream xmlStream, String xmlEncode) {
		XmlPullParserFactory factory = null;
		XmlPullParser parser = null;
		try {
			factory = XmlPullParserFactory.newInstance();
			factory.setNamespaceAware(true);
			parser = factory.newPullParser();
			parser.setInput(xmlStream, xmlEncode);

			int event = parser.getEventType();

			while (event != XmlPullParser.END_DOCUMENT) {

				switch (event) {
				case XmlPullParser.START_DOCUMENT:
					System.out.println("First we get START_DOCUMENT event");
					break;

				case XmlPullParser.START_TAG:
					String node = parser.getName();
					if (node.equals("manifest")) {
						System.out.println("now we parser manifest Tag");
						System.out.println("\tmainfest attribute: " + parser.getAttributeValue(0));
					} else if (node.equals("uses-sdk")) {
						System.out.println("now we parser uses-sdk Tag");
						int count = parser.getAttributeCount();
						for (int i = 0; i < count; i++)
							System.out.println("\tuses-sdk attribute: " + parser.getAttributeValue(i));
						System.out.println("\tuses-sdk plateform: " + parser.nextText());
					} else if (node.equals("application")) {
						System.out.println("now we parser Application Tag");
						parserApplicationInfo(parser);
					}
					break;

				default:
					break;
				}

				event = parser.next();
			}

		} catch (XmlPullParserException | IOException e) {
			e.printStackTrace();
		}

	}

	private void parserApplicationInfo(XmlPullParser parser) throws XmlPullParserException, IOException {
		int count = parser.getAttributeCount();
		for (int i = 0; i < count; i++)
			System.out.println("\twe parser Application attribute: " + parser.getAttributeValue(i));
		int event = 0;
		int depth = parser.getDepth();
		while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
				&& (parser.getDepth() > depth || event != XmlPullParser.END_TAG)) {//只解析当前的标签内部的内容

			if (event == XmlPullParser.TEXT || event == XmlPullParser.END_TAG) {
				continue;
			}

			switch (event) {
			case XmlPullParser.START_TAG:
				String node = parser.getName();
				if (node.equals("activity")) {
					parserActivityInfo(parser);
				}
				break;
			default:
				break;
			}
		}
	}

	private void parserActivityInfo(XmlPullParser parser) throws XmlPullParserException, IOException {
		int count = parser.getAttributeCount();
		for (int i = 0; i < count; i++)
			System.out.println("\t\twe parser Activity -> attribute: " + parser.getAttributeValue(i));

		int event = 0;
		int depth = parser.getDepth();
		while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
				&& (parser.getDepth() > depth || event != XmlPullParser.END_TAG)) {//只解析当前的标签内部的内容
			if (event == XmlPullParser.TEXT || event == XmlPullParser.END_TAG) {
				continue;
			}
			switch (event) {
			case XmlPullParser.START_TAG:
				String node = parser.getName();
				if (node.equals("intent-filter")) {
					parserIntentInfo(parser);
				}
				break;
			default:
				break;
			}
		}
	}

	private void parserIntentInfo(XmlPullParser parser) throws XmlPullParserException, IOException {
		int event = 0;
		int depth = parser.getDepth();
		while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
				&& (parser.getDepth() > depth || event != XmlPullParser.END_TAG)) {//只解析当前的标签内部的内容
			if (event == XmlPullParser.TEXT || event == XmlPullParser.END_TAG) {
				continue;
			}

			switch (event) {
			case XmlPullParser.START_TAG:
				String node = parser.getName();
				if (node.equals("action")) {
					System.out.println("\t\t\twe parser intent-filter action-> " + parser.getAttributeValue(0));
				} else if (node.equals("category")) {
					System.out.println("\t\t\twe parser intent-filter category-> " + parser.getAttributeValue(0));
				}
				break;
			default:
				break;
			}
		}
	}
}

输出的结果如下:

First we get START_DOCUMENT event
now we parser manifest Tag
	mainfest attribute: com.example.androidtest
now we parser uses-sdk Tag
	uses-sdk attribute: 19
	uses-sdk attribute: 21
	uses-sdk plateform: kitkat4.4
now we parser Application Tag
	we parser Application attribute: true
	we parser Application attribute: ic_launcher
		we parser Activity -> attribute: MainActivity
		we parser Activity -> attribute: app_name
			we parser intent-filter action-> android.intent.action.MAIN
			we parser intent-filter category-> android.intent.category.LAUNCHER
		we parser Activity -> attribute: DefaultActivity
		we parser Activity -> attribute: def_name
			we parser intent-filter category-> android.intent.category.LAUNCHER
			we parser intent-filter category-> android.intent.category.HOME
now we parser Application Tag
	we parser Application attribute: false
	we parser Application attribute: ic_launcher
		we parser Activity -> attribute: TestActivity
		we parser Activity -> attribute: test_name
			we parser intent-filter category-> android.intent.category.LAUNCHER

Android源码中对XML文件的解析工作基本都是按照这种模式去解析的。这里介绍XmlPullParser也是为了让我们能更好地阅读Fucking Source Code。

 

结束。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值