Android SAX API: XmlResourceParser及其扩展应用

来源:互联网 发布:淘宝商品描述不符处罚 编辑:程序博客网 时间:2024/05/18 03:39

 

XmlResourceParser继承了2个接口:AttributeSet和XmlPullParser。其中XmlPullParser定义了AndroidSAX框架。跟Java 的SAX API相比,XmlPullParser令人难以置信地简单。

一、使用XmlResourceParser读取资源束中的xml

资源束是应用程序编译后的应用程序包的别称。如果我们有一个xml文件是放在应用程序包内并随编译后的包一起发布的,那么使用XmlResourceParser读取xml非常简单。

首先在res目录下新建目录xml。在xml目录中新建xml文件,例如tqqk.xml:

<?xml version="1.0" encoding="utf-8"?>

<tqqk>

  <item name="" id="78"/>

  <item name="多云" id="80"/>

  <item name="" id="79"/>

  <item name="小(阵)雨" id="4"/>

  <item name="中雨" id="5"/>

  <item name="大雨" id="802"/>

  <item name="大到暴雨" id="7"/>

  <item name="雷阵雨" id="181"/>

  <item name="雨夹雪" id="84"/>

  <item name="小雪" id="10"/>

  <item name="中雪" id="11"/>

  <item name="大到暴雪" id="83"/>

  <item name="冰雹" id="13"/>

  <item name="" id="14"/>

  <item name="多云转晴" id="85"/>

  <item name="阴湿" id="182"/>

  <item name="闷热" id="202"/>

</tqqk>

这是一个天气情况列表,每个item元素有两个属性:name和id。也就是说我们给每种天气定义一个名字和id。

由于我们的这个xml文档中并没有定义DTD或者Schema,Eclipse会提示一个警告,不用理会它。

现在我们需要用 XmlResourceParser 来读取xml文件并转换为KVP(keyvalue pairs)对象。

新建一个类 KVPsFromXml:

public class KVPsFromXml {

  private Context ctx;

 

  publicKVPsFromXml(Context c) {

    ctx = c;

  }

 

  public Map<String,String> TqqkFromXml(String filename) {

    Map<String, String> map = new HashMap<String,String>();

    // 获得处理androidxml文件的XmlResourceParser对象

    XmlResourceParser xml = ctx.getResources().getXml(

        getResIDFromXmlFile(filename));

    try {

      // 切换到下一个状态,并获得当前状态的类型

      int eventType = xml.next();

      while (true) {

        // 文档开始状态

        if (eventType ==XmlPullParser.START_DOCUMENT) {

        }

        // 标签开始状态

        else if (eventType ==XmlPullParser.START_TAG) {

          // 将标签名称和当前标签的深度(根节点的depth1,第2层节点的depth2,类推)

          switch(xml.getDepth()){

          case 1:

            break;

          case 2:

            //   item name id 属性,并放入 map

            String key=xml.getAttributeValue(null, "name");

            String value=xml.getAttributeValue(null, "id");

            map.put(key, value);

            break;

          }

          

          // 获得当前标签的属性个数

//          int count =xml.getAttributeCount();

         // 将所有属性的名称和属性值添加到StringBuffer对象中

//          for (int i = 0; i <count; i++) {

//            sb.append(xml.getAttributeName(i)

//                +"xml.getAttributeValue(i)");

//          }

        }

        // 标签结束状态

        else if (eventType ==XmlPullParser.END_TAG) {

        }

        // 读取标签内容状态

        else if (eventType ==XmlPullParser.TEXT) {

        }

        // 文档结束状态

        else if (eventType ==XmlPullParser.END_DOCUMENT) {

          // 文档分析结束后,退出while循环

          break;

        }

        // 切换到下一个状态,并获得当前状态的类型

        eventType= xml.next();

 

      }

    } catch (Exception e) {

      e.printStackTrace();

    }

    return map;

  }

 

  public intgetResIDFromXmlFile(String file) {

    int ret = 0;

    @SuppressWarnings("rawtypes")

    Class c = null;

 

    try {

      c = Class.forName("ydtf.ydqx.R$xml");

      Field field = c.getDeclaredField(file);

      if (field != null)

        ret= field.getInt(c);

    } catch (Exception ex) {

      ex.printStackTrace();

    }

    return ret;

  }

}

该类的构造函数需要传递一个Context参数,即把使用这个类的Activity引用传递给它。因为Activity有一个很便利的方法getResource可以访问并加载R 对象中的类(资源)。由于 XmlPullParser接口定义了Android SAX的XMLPULL V1 API(请参考http://www.xmlpull.org),我们可以使用SAX解析中的4个事件:

START_TAG,TEXT,END_TAG,END_DOCUMENT(这跟Java SAX中的4个事件是对应的)。因此在接下来的while循环中,我们针对4个事件进行了分别的处理,从而读取xml中的各个元素及其属性,并组装成KVP(键值对)放入Map中。

ctx.getResources().getXml().getResIDFromXmlFile())方法可以获得一个 XmlResourceParser 对象。但是getResIDFromXmlFile 方法要求提供一个int型的资源id(即R.java中定义的各种16进制数)为参数。

由于res文件夹中的各种资源被映射入R.java的类及字段——具体说,res目录下的子目录映射为R.java中的内部类,子目录中的文件被映射为内部类的字段。因此,xml目录下的tqqk.xml会被映射为R.java中的xml类的tqqk字段。用java表述则是“包名.R.$xml.tqqk”。通过java.reflect包,我们可以得到这个xml文件的资源id。

接下来,我们在Activity中取得xml解析的结果--Map对象:

public void onCreate(BundlesavedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.ydqxlogin);

    // read thexml file:tqqk.xml

    KVPsFromXml xml=new KVPsFromXml(this);

    tqqk_map=xml.TqqkFromXml("tqqk");

    Log.i("tqqk_map",tqqk_map+"");

 }

我们可以在LogCat中看到打印出来的结果:

—·çƒ­=202, 大到暴雨=7, 阴湿=182, 大到暴雪=83, 冰雹=13, 中雨=5, 雷阵雨=181, 中雪=11, 雨夹雪=84, 大雨=802, 阴=79, 雾=14, 多云=80, 小雪=10, 晴=78, 小(阵)雨=4, 多云转晴=85}

由于name使用了中文,所以出现了乱码。如果我们用adblogcat命令的话,则可以显示中文:

{大雨=802, 冰雹=13, 雷阵雨=181, 阴湿=182, 晴=78, 阴=79, 闷热=202,多云=80, 雨夹雪=84, 小(阵)雨=4, 中雨=5, 小雪=10, 大到暴雨=7, 中雪=11, 雾=14, 多云转晴=85, 大到暴雪=83}

二、直接从资源束之外读取xml

有时候,xml并不总是随资源束一起编译,比如说从网络流中获取的xml。

那么我们可以直接使用XmlPullParser接口。

修改KVPsFromXml类的 TqqkFromXml 方法代码:

public Map<String,String> TqqkFromXml(InputStream in,String encode){

    Map<String, String> map = new HashMap<String,String>();

    try{

           XmlPullParserFactoryfactory = XmlPullParserFactory.newInstance();

           factory.setNamespaceAware(true);

           XmlPullParserxpp = factory.newPullParser();

           xpp.setInput(in,encode);

        // 切换到下一个状态,并获得当前状态的类型

           int eventType =xpp.getEventType();

        while (true) {

          // 文档开始状态

          if (eventType ==XmlPullParser.START_DOCUMENT) {

          }

          // 标签开始状态

          else if (eventType ==XmlPullParser.START_TAG) {

            // 将标签名称和当前标签的深度(根节点的depth1,第2层节点的depth2,类推)

            switch(xpp.getDepth()){

            case 1:

              break;

            case 2:

              //   item name id 属性,并放入 map

              Stringkey=xpp.getAttributeValue(null, "name");

              Stringvalue=xpp.getAttributeValue(null, "id");

              map.put(key,value);

              break;

            }

            

            // 获得当前标签的属性个数

//            int count = xml.getAttributeCount();

            // 将所有属性的名称和属性值添加到StringBuffer对象中

//            for (int i = 0; i < count; i++) {

//              sb.append(xml.getAttributeName(i)

//                  + "xml.getAttributeValue(i)");

//            }

          }

          // 标签结束状态

          else if (eventType ==XmlPullParser.END_TAG) {

          }

          // 读取标签内容状态

          else if (eventType ==XmlPullParser.TEXT) {

          }

          // 文档结束状态

          else if (eventType ==XmlPullParser.END_DOCUMENT) {

            // 文档分析结束后,退出while循环

            break;

          }

          // 切换到下一个状态,并获得当前状态的类型

          eventType = xpp.next();

        }

       }catch(Exception e){

         e.printStackTrace();

       }

    return map;

 }

修改Activity调用代码:

    String sXml = "<tqqk>" + "<item name=/"/" id=/"78/"/>"

+ "<item name=/"多云/" id=/"80/"/>"

+ "<item name=/"/" id=/"79/"/>"

+ "<item name=/"小(阵)雨/" id=/"4/"/>"

+ "<item name=/"中雨/" id=/"5/"/>"

+ "<item name=/"大雨/" id=/"802/"/>"

+ "<item name=/"大到暴雨/" id=/"7/"/>"

+ "<item name=/"雷阵雨/" id=/"181/"/>"

+ "<item name=/"雨夹雪/" id=/"84/"/>"

+ "<item name=/"小雪/" id=/"10/"/>"

+ "<item name=/"中雪/" id=/"11/"/>"

+ "<item name=/"大到暴雪/" id=/"83/"/>"

+ "<item name=/"冰雹/" id=/"13/"/>"

+ "<item name=/"/" id=/"14/"/>"

+ "<item name=/"多云转晴/" id=/"85/"/>"

+ "<item name=/"阴湿/" id=/"182/"/>"

+ "<item name=/"闷热/" id=/"202/"/>" + "</tqqk>";

    ByteArrayInputStream stream = newByteArrayInputStream(sXml.getBytes());

 

    KVPsFromXml xml = new KVPsFromXml(this);

    tqqk_map =xml.TqqkFromXml(stream,null);

   Log.i("tqqk_map", tqqk_map + "");

现在我们构建了一个字符流传递给 TqqkFromXml 方法(第二个参数字符编码设定为null,因为java内部字符编码未发生任何改变),它仍然可以为我们读取xml的内容:

{大雨=802, 冰雹=13, 雷阵雨=181, 阴湿=182, 晴=78, 阴=79, 闷热=202,多云=80, 雨夹雪=84, 小(阵)雨=4, 中雨=5, 小雪=10, 大到暴雨=7, 中雪=11, 雾=14, 多云转晴=85, 大到暴雪=83}

当然,这里我偷了个懒,没有使用网络流读取xml,但结果不会有任何区别。

 

三、AttributeSet

XmlResourceParser还继承了AttributeSet接口。一个AttributeSet接口对象代表了一个Xml元素,它可以把该元素的所有属性用统一的方法进行访问。正如以下代码所示:

publicVector<Map<Object,Object>> getAttributeSet(String name){

    Vector<Map<Object,Object>>vector=new Vector<Map<Object,Object>>();

    // 获得处理androidxml文件的XmlResourceParser对象

    XmlResourceParser parser = ctx.getResources().getXml(

getResIDFromXmlFile(name));

    int state = 0;

      do {

//        AttributeSet as;

          try {

              state= parser.next();

              if (state ==XmlPullParser.START_TAG && parser.getName().equals("item")) {

                  AttributeSet as=Xml.asAttributeSet(parser);

                  intn=as.getAttributeCount();

                  Map<Object,Object> map=newHashMap<Object,Object>();

              while(n>0){

        n--;

        map.put(as.getAttributeName(n),as.getAttributeValue(n));

              }

              if(map!=null) vector.add(map);

              }

          } catch(XmlPullParserException e1) {

              e1.printStackTrace();

          } catch (IOException e1) {

              e1.printStackTrace();

          }  

      } while(state !=XmlPullParser.END_DOCUMENT);

      return vector;

 }

如你所见,AttributeSet接口实际上是一个抽象的对象,它并没有定义实例变量或字段,它只定义了一系列的访问Xml元素属性的方法,因此我们无法直接保存AttributeSet对象到集合中。最终我们把它复制到Map 对象并放入集合中(因为我们的xml文件中有多个元素)。

接下来我们打印这些xml元素:

KVPsFromXml xml = new KVPsFromXml(this);

    

Vector<Map<Object,Object>> attrs=xml.getAttributeSet("tqqk");

for(Map<Object,Object>as : attrs){      

  Log.i("map",as.toString());

}

当然,Map 距离 Object 已经不远了,要将Map映射为对象只需要使用java的反射机制。

 

原创粉丝点击