Android-使用Dom對(duì)XML進(jìn)行增刪改查

0. Thanks

Android解析XML的三種方式
Android幾種解析XML方式的比較
android xml 解析 修改
android 對(duì)xml文件的pull解析窜护,生成xml 诗力,對(duì)xml文件的增刪

1. 概述

  • 平常我們一般是使用JSON與服務(wù)器做數(shù)據(jù)通信搬瑰,JSON的話蒋得,直接用GSON或者其他庫(kù)去解析很簡(jiǎn)單。但是,其他有些服務(wù)器會(huì)返回XML格式的文件,這時(shí)候就需要去讀取XML文件了烙肺。

  • XML的解析有三種方式,在Android中提供了三種解析XML的方式:DOM(Document Objrect Model),SAX(Simple API XML),以及Android推薦的Pull解析方式氧卧,他們也各有弊端桃笙,而這里來(lái)看看使用DOM的方式。

2. Dom解析

  • DOM解析器在解析XML文檔時(shí)沙绝,會(huì)把文檔中的所有元素搏明,按照其出現(xiàn)的層次關(guān)系,解析成一個(gè)個(gè)Node對(duì)象(節(jié)點(diǎn))闪檬。再形象點(diǎn)星著,就是一棵樹,多節(jié)點(diǎn)的樹粗悯,稱為Dom樹虚循。

  • Node對(duì)象提供了一系列常量來(lái)代表結(jié)點(diǎn)的類型,當(dāng)開發(fā)人員獲得某個(gè)Node類型后为黎,就可以把Node節(jié)點(diǎn)轉(zhuǎn)換成相應(yīng)節(jié)點(diǎn)對(duì)象(Node的子類對(duì)象)邮丰,以便于調(diào)用其特有的方法。

  • Node對(duì)象提供了相應(yīng)的方法去獲得它的父結(jié)點(diǎn)或子結(jié)點(diǎn)铭乾。編程人員通過(guò)這些方法就可以讀取整個(gè)XML文檔的內(nèi)容剪廉、或添加、修改炕檩、刪除XML文檔的內(nèi)容.

3. Dom解析代碼示例

  • 代碼如下:
/**
 * DOM解析
 *  把文檔中的所有元素斗蒋,按照其出現(xiàn)的層次關(guān)系,解析成一個(gè)個(gè)Node對(duì)象(節(jié)點(diǎn))笛质。
 *  缺點(diǎn)是消耗大量的內(nèi)存泉沾。
 * @param xmlFilePath   文件
 * @return  Document
 */
public static Document loadWithDom(String xmlFilePath) {
        try {
            File file = new File(xmlFilePath);
            if (!file.exists()) {
                throw new RuntimeException("not find file:" + xmlFilePath);
            }
            else {
                InputStream inputStream = new FileInputStream(file);
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                Document document = documentBuilder.parse(inputStream);
                try {
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return document;
            }
        } catch (ParserConfigurationException | IOException | SAXException e) {
            return null;
        }
    }
  • 上面的方法是同步的,最終返回的是一個(gè) Document 對(duì)象妇押。

4. 查找

  • 自定義一個(gè)稍微簡(jiǎn)單的XML:
<?xml version="1.0" encoding="utf-8" standalone='yes'?>
<packages key1="value1" key2="value2">
    <last-platform-version internal="22" external="22" fingerprint="honeybot/H1f/H1f:5.1.1/LMY47V/huiyu01091530:userdebug/test-keys"/>
    <database-version internal="3" external="3"/>
    <permission-trees/>
    <permissions>
        <item name="android.permission.SET_SCREEN_COMPATIBILITY" package="android" protection="2"/>
        <item name="android.permission.MEDIA_CONTENT_CONTROL" package="android" protection="18"/>
        <item name="android.permission.DELETE_PACKAGES" package="android" protection="18"/>
        <item name="com.android.voicemail.permission.ADD_VOICEMAIL" package="android" protection="1"/>
    </permissions>
    <package name="com.android.providers.downloads" codePath="/system/priv-app/DownloadProvider" nativeLibraryPath="/system/priv-app/DownloadProvider/lib" flags="1074282053" ft="15f9e785498" it="15f9e785498" ut="15f9e785498" version="22" sharedUserId="10002">
        <sigs count="1">
            <cert index="1"/>
        </sigs>
        <proper-signing-keyset identifier="2"/>
        <signing-keyset identifier="2"/>
    </package>
</packages>
  • 使用上面的代碼去解析跷究,Document如下:


    1.png
  • Documnet,it is the root of the document tree, and provides the primary access to the document's data. 就是整個(gè)xml的root敲霍,通過(guò)它可以獲取到xml的相關(guān)信息俊马。

  • xmlVersion,代表的是xml的版本

  • children肩杈,子節(jié)點(diǎn)柴我,是Element扩然,對(duì)應(yīng)上面的艘儒,是最外層的package

  • Element,是xml的最外層的結(jié)點(diǎn),由document.getDocumentElement() 得到 界睁。

  • Node觉增,結(jié)點(diǎn)。何為結(jié)點(diǎn)翻斟?其實(shí)就是一個(gè)<abc></abc>的一個(gè)結(jié)點(diǎn)信息抑片,存儲(chǔ)著一些,結(jié)點(diǎn)本身的屬性杨赤,和其結(jié)點(diǎn)下的子結(jié)點(diǎn)等等。

//得到最外層的節(jié)點(diǎn)
Element element = document.getDocumentElement();
//得到節(jié)點(diǎn)的屬性
NamedNodeMap namedNodeMap = element.getAttributes();
//便利屬性并log輸出
for (int i = 0; i < namedNodeMap.getLength(); i++) {
    Node node = namedNodeMap.item(i);
    node.getNodeName();//key
    node.getTextContent();//value
    node.getNodeType();//node type
}
//得到子節(jié)點(diǎn)列表
NodeList nodeList = element.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
    Node node = nodeList.item(i);
    //每個(gè)node下面截汪,也可能有node疾牲,和,node的屬性衙解,獲取都如上所示
}
  • Node阳柔,每個(gè)node里面的屬性,也是node蚓峦,每個(gè)node下的子節(jié)點(diǎn)node也是一個(gè)一個(gè)node
  //節(jié)點(diǎn)的屬性
   Node node = namedNodeMap.item(i);
   node.getNodeName();//key
   node.getTextContent();//value
   node.getNodeType();//node type
  //節(jié)點(diǎn)下的子節(jié)點(diǎn)
  NodeList nodeList = element.getChildNodes();
  for (int i = 0; i < nodeList.getLength(); i++) {
     Node node = nodeList.item(i);
     //每個(gè)node下面舌剂,也可能有node,和暑椰,node的屬性霍转,獲取都如上所示
  }
  • 4.1 實(shí)踐一下,查找

  • 在android系統(tǒng)里面一汽,安裝的每一個(gè)app避消,其信息都被存到一個(gè)xml里面:/data/system/packages.xml,可以通過(guò)root去查看里面的內(nèi)容召夹,大概如下(其實(shí)上面的例子就是從這個(gè)xml文件copy來(lái)的):

<?xml version="1.0" encoding="utf-8" standalone='yes'?>
<packages key1="value1" key2="value2">
    <last-platform-version internal="22" external="22" fingerprint="honeybot/H1f/H1f:5.1.1/LMY47V/huiyu01091530:userdebug/test-keys"/>
    <database-version internal="3" external="3"/>
    <permission-trees/>
    <permissions>
        //一堆的權(quán)限
        <item name="android.permission.SET_SCREEN_COMPATIBILITY" package="android" protection="2"/>
    </permissions>
    //一堆的app
    <package name="com.android.providers.downloads" codePath="/system/priv-app/DownloadProvider" nativeLibraryPath="/system/priv-app/DownloadProvider/lib" flags="1074282053" ft="15f9e785498" it="15f9e785498" ut="15f9e785498" version="22" sharedUserId="10002">
        <sigs count="1">
            <cert index="1"/>
        </sigs>
        <proper-signing-keyset identifier="2"/>
        <signing-keyset identifier="2"/>
    </package>
</packages>

而現(xiàn)在有個(gè)需求岩喷,查找是否有app:com.xx.xx,也就是查找xml中的package節(jié)點(diǎn)中的name屬性值有沒有此包名监憎。
我們先封裝一下代碼吧:

public class XmlUtils {

    /**
     * DOM解析
     *  把文檔中的所有元素纱意,按照其出現(xiàn)的層次關(guān)系,解析成一個(gè)個(gè)Node對(duì)象(節(jié)點(diǎn))鲸阔。
     *  缺點(diǎn)是消耗大量的內(nèi)存偷霉。
     * @param xmlFilePath   文件
     * @return  Document
     */
    public static Document loadWithDom(String xmlFilePath) {
        try {
            File file = new File(xmlFilePath);
            if (!file.exists()) {
                throw new RuntimeException("not find file:" + xmlFilePath);
            }
            else {
                InputStream inputStream = new FileInputStream(file);
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                Document document = documentBuilder.parse(inputStream);
                try {
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return document;
            }
        } catch (ParserConfigurationException | IOException | SAXException e) {
            return null;
        }
    }

    public static Observable<Document> loadWithDomRx(String xmlFilePath) {
        return Observable.just(loadWithDom(xmlFilePath));
    }
}

封裝好之后,就可以寫代碼隶债,如下:

//加載文件
XmlUtils.loadWithDomRx("/sdcard/Test.xml")
        .subscribe(document -> {
            //判斷是否加載到文件
            if (document!=null && document.getDocumentElement()!=null) {
                //判斷有無(wú)node
                NodeList nodeList = document.getDocumentElement().getChildNodes();
                if (nodeList != null) {
                    //遍歷node list
                    for (int i = 0; i < nodeList.getLength(); i++) {
                        Node node = nodeList.item(i);
                        //判斷是否是package節(jié)點(diǎn)
                        if (node.getNodeName() != null && node.getNodeName().equals("package")) {
                            //提取參數(shù)列表
                            NamedNodeMap namedNodeMap = node.getAttributes();
                            if (namedNodeMap != null && namedNodeMap.getLength()>0) {
                                //判斷參數(shù)中是否有com.xx.xx
                                Node n = namedNodeMap.item(0);
                                if (n.getNodeName()!=null && n.getNodeName().equals("name")) {
                                   if (n.getTextContent()!=null && n.getTextContent().equals("com.xx.xx")) {
                                        //進(jìn)行您的操作
                                   }
                                }
                            }
                        }
                    }
                }
            }
        });

注意腾它,要做好判空瞒滴。有可能很多node不存在參數(shù),或者沒有子節(jié)點(diǎn)妓忍。

5. 增刪改

當(dāng)加載xml到內(nèi)存中后世剖,你可以對(duì)document進(jìn)行修改

  • 增加
Element element = document.createElement("New Node");
element.setAttribute("key1","value1");
element.setAttribute("key2","value2");
node.appendChild(element);
  • 刪除
//注意的是旁瘫,你需要先找出這個(gè)node對(duì)象,因?yàn)閍pi沒有提供直接remove index 的node的方法惠况。
element.removeChild(node);
node1.removeChild(node2);
  • 修改
//找到具體的node稠屠,或者,elemnet权埠,修改:
node.setNodeValue("edit key");
node.setTextContent("edit value");

6. 保存

  • 在內(nèi)存中修改好的document對(duì)象攘蔽,直接保存為新的xml文件秩彤,代碼如下:
/**
 * 保存修改后的Doc
 *  http://blog.csdn.net/franksun1991/article/details/41869521
 * @param doc   doc
 * @param saveXmlFilePath   路徑
 * @return  是否成功
 */
public static boolean saveXmlWithDom(Document doc,String saveXmlFilePath) {
    if (doc==null || saveXmlFilePath==null || saveXmlFilePath.isEmpty())
        return false;
    try {
        //將內(nèi)存中的Dom保存到文件
        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer();
        //設(shè)置輸出的xml的格式事哭,utf-8
        transformer.setOutputProperty("encoding", "utf-8");
        transformer.setOutputProperty("version",doc.getXmlVersion());
        DOMSource source = new DOMSource(doc);
        //打開輸出流
        File file = new File(saveXmlFilePath);
        if (!file.exists())
            Log.i("XmlUtils","saveXmlWithDom,createNewFile:"+file.createNewFile());
        OutputStream outputStream = new FileOutputStream(file);
        //xml的存放位置
        StreamResult src = new StreamResult(outputStream);
        transformer.transform(source, src);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

7. 附上工具類

/**
 * <pre>
 *     author: Chestnut
 *     blog  : http://www.reibang.com/u/a0206b5f4526
 *     time  : 2018/1/10 17:14
 *     desc  :  XML解析工具類
 *     thanks To:
 *          1.  [Android解析XML的三種方式]     http://blog.csdn.net/d_shadow/article/details/55253586
 *          2.  [Android幾種解析XML方式的比較]       http://blog.csdn.net/isee361820238/article/details/52371342
 *          3.  [android xml 解析 修改]     http://blog.csdn.net/i_lovefish/article/details/39476051
 *          4.  [android 對(duì)xml文件的pull解析降盹,生成xml 蓄坏,對(duì)xml文件的增刪]        http://blog.csdn.net/jamsm/article/details/52205800
 *     dependent on:
 *     update log:
 * </pre>
 */
public class XmlUtils {

    /**
     * DOM解析
     *  把文檔中的所有元素丑念,按照其出現(xiàn)的層次關(guān)系脯倚,解析成一個(gè)個(gè)Node對(duì)象(節(jié)點(diǎn))嵌屎。
     *  缺點(diǎn)是消耗大量的內(nèi)存宝惰。
     * @param xmlFilePath   文件
     * @return  Document
     */
    public static Document loadWithDom(String xmlFilePath) {
        try {
            File file = new File(xmlFilePath);
            if (!file.exists()) {
                throw new RuntimeException("not find file:" + xmlFilePath);
            }
            else {
                InputStream inputStream = new FileInputStream(file);
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                Document document = documentBuilder.parse(inputStream);
                try {
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return document;
            }
        } catch (ParserConfigurationException | IOException | SAXException e) {
            return null;
        }
    }

    public static Observable<Document> loadWithDomRx(String xmlFilePath) {
        return Observable.just(loadWithDom(xmlFilePath));
    }

    /**
     * 保存修改后的Doc
     *  http://blog.csdn.net/franksun1991/article/details/41869521
     * @param doc   doc
     * @param saveXmlFilePath   路徑
     * @return  是否成功
     */
    public static boolean saveXmlWithDom(Document doc,String saveXmlFilePath) {
        if (doc==null || saveXmlFilePath==null || saveXmlFilePath.isEmpty())
            return false;
        try {
            //將內(nèi)存中的Dom保存到文件
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Transformer transformer = tFactory.newTransformer();
            //設(shè)置輸出的xml的格式,utf-8
            transformer.setOutputProperty("encoding", "utf-8");
            transformer.setOutputProperty("version",doc.getXmlVersion());
            DOMSource source = new DOMSource(doc);
            //打開輸出流
            File file = new File(saveXmlFilePath);
            if (!file.exists())
                Log.i("XmlUtils","saveXmlWithDom,createNewFile:"+file.createNewFile());
            OutputStream outputStream = new FileOutputStream(file);
            //xml的存放位置
            StreamResult src = new StreamResult(outputStream);
            transformer.transform(source, src);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public static Observable<Boolean> saveXmlWithDomRx(Document doc,String saveXmlFilePath) {
        return Observable.just(saveXmlWithDom(doc, saveXmlFilePath));
    }
}

小弟不才淤堵,如有遺漏或錯(cuò)誤顷扩,請(qǐng)多多交流~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市事富,隨后出現(xiàn)的幾起案子统台,更是在濱河造成了極大的恐慌贱勃,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仇穗,死亡現(xiàn)場(chǎng)離奇詭異纹坐,居然都是意外死亡舞丛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吨凑,“玉大人,你說(shuō)我怎么就攤上這事』烂睿” “怎么了焚鹊?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵末患,是天一觀的道長(zhǎng)璧针。 經(jīng)常有香客問(wèn)我渊啰,道長(zhǎng),這世上最難降的妖魔是什么隧膏? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任胞枕,我火速辦了婚禮魏宽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘派桩。我一直安慰自己窄坦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肠缨,像睡著了一般晒奕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上砰盐,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天坑律,我揣著相機(jī)與錄音晃择,去河邊找鬼。 笑死列疗,一個(gè)胖子當(dāng)著我的面吹牛抵栈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竭讳,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼灿渴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蹬挤?” 一聲冷哼從身側(cè)響起焰扳,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吨悍,失蹤者是張志新(化名)和其女友劉穎育瓜,沒想到半個(gè)月后躏仇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年船响,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灿意。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崇呵。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖域慷,靈堂內(nèi)的尸體忽然破棺而出荒辕,到底是詐尸還是另有隱情,我是刑警寧澤犹褒,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布抵窒,位于F島的核電站,受9級(jí)特大地震影響叠骑,放射性物質(zhì)發(fā)生泄漏李皇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一宙枷、第九天 我趴在偏房一處隱蔽的房頂上張望掉房。 院中可真熱鬧,春花似錦慰丛、人聲如沸卓囚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至银萍,卻和暖如春搀绣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背麻捻。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工明棍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓岛啸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芬首,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361