4G拨号上网 之 APN的XML文件解析

在做4G项目实现拨号上网的过程中,我们需要获取中国三大运营商的APN,全球APN的原始参数都是存放在XML中的,我们需要使用,那么就需要将存放APN的XML文档进行解析。

一、XML基础知识

1、概念

(1)XML指可扩展标记语言(eXtensible Markup Language)。XML 本身不会做任何事情,是被设计用来传输和存储数据的,不用于表现和展示数据。

(2)XML 和 HTML 之间的差异:

  • XML 不是 HTML 的替代;
  • XML 和 HTML 为不同的目的而设计:
    XML 被设计用来传输和存储数据,其焦点是数据的内容。
    HTML 被设计用来显示数据,其焦点是数据的外观。
  • HTML 旨在显示信息,而 XML 旨在传输信息。
2、结构

XML 文档中的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。也就是从"根部"开始,然后扩展到"枝叶"。XML 文档必须包含根元素。该元素是所有其他元素的父元素。

以一个实例来进行说明:

<?xml version="1.0" encoding="UTF-8"?>
<letter>
<to>Seven</to>
<from>Tom</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</letter>

说明:
第一行是 XML 声明。它定义 XML 的版本(1.0)和所使用的编码(UTF-8 : 万国码, 可显示各种语言)。
第二行描述文档的根元素(像在说:“本文档是一封信”)。
接下来 4 行描述根的 4 个子元素(to, from, heading 以及 body)。
最后一行定义根元素的结尾。
在这个实例中,XML 文档里面包含了一张 Tom 写给 Seven 的信。

二、APN的xml文档

1、 APN包含哪些参数

一个典型的APN包含的参数有名称、MCC、MNC、接入点、类型。

下面以CMCC APN为例,它包含下面一些参数:

<apn carrier=“连接互联网” //名称
mcc=“460” //MCC
mnc=“07” //MNC
apn=“cmnet” //接入点
type=“default,supl,net” //类型
/>

APN还可以包含其他更多的参数,详情见 android APN解析

2、APN的存储位置
  • APN以XML的格式存储。
  • 文件名:Apns-conf.xml
  • 源码文件路径:
    MTK平台(通常):alps\mediatek\frameworks\base\telephony\etc
    高通平台(通常):android\vendor\qcom\proprietary\telephony-apps\etc

三、解析XML文档

本文使用库libxml2 解析

1、安装库libxml2
(1)官网地址:http://xmlsoft.org/sources/old/
(2)下载

在官网地址上复制下载链接,然后去linux下载

 wget "https://gitlab.gnome.org/GNOME/libxml2/-/archive/v2.9.14/libxml2-v2.9.14.tar.gz"

在这里插入图片描述

(3)解压缩
 tar -zxvf libxml2-v2.9.14.tar.gz

在这里插入图片描述

(4)安装

一般来说,在网上下载的库文件要安装要先使用命令 ./configure 生成 makefile ,但是,这个库文件,比较特殊,首先就使用 ./configure 命令会失败:
在这里插入图片描述
所以我们首先要做的应该是生成 configure 文件,打开解压缩过后的文件,会发现里面没有 makefile ,也没有 configure,但是此时我们可以发现有文件 autogen.sh ,这个脚本文件是我们生成 configure 的重要核心:
在这里插入图片描述
使用命令:

./autogen.sh

(这个过程可能时间会有些长)
然后就可以发现文件中已经生成了 configure 文件:
在这里插入图片描述
然后使用命令:

./configure

生成 makefile 文件:
在这里插入图片描述
之后,编译运行,但是此时又出现了问题,在编译时报错了:
在这里插入图片描述
经过查询后,我发现是因为我没有安装python-devel,这个软件包是 libxml2 的依赖包,如果不安装,libxml2 安装就会报错。这个软件包只是一个底层依赖包,所以安装 RPM 包即可。命令如下:

sudo apt-get -y --force-yes install python-dev

在这里插入图片描述
之后再进行make,就可以了

make
sudo make install

在这里插入图片描述
查看后发现库libxml2已经安装好了,此时的库是放在系统默认路径 下的,但是我想将其放在我自己的账号路径下,于是可以在 ./configure 的时候指定一下其路径:

./configure --prefix=/... ...        (... ...是要安装的目标路径)

在这里插入图片描述
重新编译安装,会发现库已经安装到我的指定路径下了:
在这里插入图片描述

(5)链接

因为一般系统默认找库的路径是 /lib 和 /usr/lib ,在自己修改了路径之后,使用的时候要指明库的路径:

gcc  -c  *.c   -I ../lib/include/libxml2 -L  ../lib/lib/  -lxml2

-I :后面是编译时所需要的库的头文件的路径
-L:后面是链接时所需要的库文件
具体链接库的步骤可移步:动态库、静态库

2、代码封装

解析文件的具体使用方法和函数在代码中都有详述:

(1)apn_xml.h
#ifndef   _APNS_XML_H_
#define   _APNS_XML_H_


#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <libxml2/libxml/parser.h>
#include <libxml2/libxml/xmlmemory.h>
#include <libxml/tree.h>

/******************************************************************
* 名称:             xml_parser
* 功能:             解析保存APN的XML文件
*
* 入口参数:         filename:    xml文件名
*
* 出口参数:         正确返回为0,错误返回为-1
*******************************************************************/
extern int xml_parser(char *filename);

#endif

(2)apn_xml.c
#include "apns_xml.h"

#define   XML_PRINT
#define   FILENAME    "./apns-full-conf.xml"

#ifdef    XML_PRINT
#define   xml_print(format,args...)  printf(format,##args)
#else
#define   xml_print(format,args...)  do{}  whlie(1)
#endif

/*
 * 1、读入文件,返回文档指针doc
 * 2、得到根节点
 * 3、轮询所有节点,找到所需的节点(根元素为apn),取出其内容
 * 4、取出带有mcc和mnc属性的节点
 * 5、将mcc和mnc属性取出来
 *
 *
 */

int xml_parser(char *filename)
{
        int                 rv = 0;

        /*  xmlDoc  xmlDocPtr:文档对象的结构体及其指针   */
        xmlDocPtr           doc;           //定义解析文件指针
        /*  xmlNode xmlNodePtr:节点对象的结构体及其指针  */
        xmlNodePtr          curNode;       //定义节点指针
        xmlChar             *temp;            //临时字符串变量
        xmlChar             *mccptr;
        xmlChar             *mncptr;

        /*
         * 函数:xmlDocPtr xmlReadFile (const char * filename,const char * encoding, int options);
         * 函数说明:  Filename:   目标文件的路径或URL
         *             encoding:   目标文件的编码方式,可以为NULL
         *             options:   枚举变量中的值,可以为0
         *             XML_PARSE_RECOVER    = 1<<0,   //尝试修复错误语法
         *             XML_PARSE_NOERROR    = 1<<5,   //不输出错误日志
         *             XML_PARSE_NOWARNING  = 1<<6,   //不输出警告日志
         *             XML_PARSE_PEDANTIC   = 1<<7,   //输出详细的错误日志
         *
         * 返回值:成功:XML文档的树结构    失败:NULL
         *
         */
        doc = xmlReadFile(FILENAME,"UTF-8",XML_PARSE_RECOVER);
        if(NULL == doc)
        {
                xml_print("Read the xmlFile failed:%s\n",strerror(errno));
                return -1;
        }


        /*
         * 函数: xmlNodePtr    xmlDocGetRootElement(xmlDocPtr doc)
         * 函数说明:获得根节点
         * 参数说明:doc:XML文档句柄。
         * 返回值:成功:XML文档的根节点    失败:NULL
         *
         */
        curNode = xmlDocGetRootElement(doc);
        if(NULL == curNode)
        {
                xml_print("Get the root failed:%s\n",strerror(errno));

                //释放内存中的XML文档
                xmlFreeDoc(doc);
                return -1;
        }

        /*
         * 函数:int xmlStrcmp(const xmlChar *str1,const xmlChar *str2);
         * 函数说明:判断两个字符串是否相同
         * 参数说明:str1:待判断的字符串   str2:正确的字符串
         * 返回值:相同:0   不同:不为0
         *
         */
        rv = xmlStrcmp(curNode->name, (const xmlChar* ) "apns");
        if(rv != 0)
        {
                xml_print("The root is not apns.\n");
                xmlFreeDoc(doc);
                return -1;
        }

        //找到根节点的首个儿子节点
        curNode = curNode->xmlChildrenNode;
        while(curNode != NULL)
        {
                //获得节点内容后取出
                if( ! xmlStrcmp(curNode->name,(const xmlChar *) "apn") )
                {

                        /*
                         *  函数:xmlChar* xmlNodeGetContent(xmlNodePtr cur);
                         *  函数说明:获得节点的内容
                         *  参数说明:cur:节点的指针
                         *  返回值:成功:节点的文本内容(需要用xmlFree()函数释放发挥的资源    失败:NULL
                         *
                         */
                        temp = xmlNodeGetContent(curNode);
                        xml_print("APN: %s\n",temp);
                        xmlFree(temp);

                }

                //找到带有mcc和mnc属性的节点
                /*
                 * 函数:
                 * 函数说明:判断节点cur是否具有属性
                 * 参数说明:
                 * 返回值:
                 *
                 */
                if( (xmlHasProp( curNode,BAD_CAST "mcc")) && (xmlHasProp( curNode,BAD_CAST "mnc") ) )
                {
                        xmlNodePtr propNodePtr = curNode;
                        mccptr = xmlGetProp(propNodePtr,BAD_CAST "mcc");
                        mncptr = xmlGetProp(propNodePtr,BAD_CAST "MNC");
                        xml_print("The apn is:%s,mcc is:%s,mnc is:%s\n",temp,mccptr,mncptr);
                        xmlFree(mccptr);
                        xmlFree(mncptr);
                }

                curNode = curNode ->next;
        }
        xmlFreeDoc(doc);
        return 0;

}

小tips:
可以看到我的.c文件中有这一段代码:

#define   XML_PRINT

#ifdef    XML_PRINT
#define   xml_print(format,args...)  printf(format,##args)
#else
#define   xml_print(format,args...)  do{}  whlie(1)
#endif

说明:args… 表示可变参数列表,args在预处理过程中,会被实际的参数集所替换。“##”的作用是对token进行连接,上面中format,args都可以看作是token,如果token为空,“##”则不进行连接,所以允许省略可变参数。

其实这是一个很好用的小技巧,在做项目的时候,代码中调试阶段需要打印出来的语句,往往到最后阶段是没有必要的打印的,这时候要一个一个地修改删除 printf 是很麻烦的一件事,那我们再最开始写代码的时候就可以利用宏定义一段上面的代码,如果不想要打印,那么将 #define XML_PRINT 这一句注释掉就好。

还有在写解析代码的过程中,还有很多没有使用到的库函数,具体的函数说明可以移步下面两篇文章进行进一步学习:
libxml2库函数详解
Libxml2常用概述及常用函数

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值