groovy XmlParser 递归遍历 xml 文件,修改并保存

使用 groovy.util.XmlParser 解析 xml 文件,对文件进行修改(新增标签),然后保存。

是不是 XmlParser 没有提供方法遍历每个节点,难道要自己写?

什么是递归?

不用说,想必都懂得~

java 复制代码
import ***.XmlNodeCallback;
import ***.PluginLog;

import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import groovy.util.Node;
import groovy.util.XmlParser;
import groovy.xml.XmlUtil;


public class PluginXmlUtil {
	 /**
     * 
     * @param xmlFile   需要解析的 xml 文件
     * @param callback  回调每一个标签 node,可以对 node 进行 CURD
     * @return
     */
    public static Node parseXml(File xmlFile, XmlNodeCallback callback) {
        if (CommUtils.isEmptyOrNoExists(xmlFile)) {
            return null;
        }

        try {
            Node rootNode = new XmlParser().parse(xmlFile);
            traverseNode(rootNode, callback);
            return rootNode;
        } catch (IOException e) {
        } catch (SAXException e) {
        } catch (ParserConfigurationException e) {
        }

        return null;
    }

    /**
     * 
     * @param node          需要保存的往往是根节点 node(当然保存你想要的任意节点也是可以)
     * @param targetFile    保存文件
     * @return
     */
    public static boolean saveNodeToFile(Node node, File targetFile) {
        if (node == null || targetFile == null) {
            return false;
        }

        try {
        	// 使用 groovy 提供的 xml 序列化工具获得原始字符串
            String finalContent = XmlUtil.serialize(node);
            if (CommUtils.isEmptyOrNoExists(finalContent)) {
                return false;
            }
            // 使用 TRUNCATE_EXISTING,如果文件存在,那么截取长度为0(也就是覆盖文件内容),然后写入新内容
            Files.write(targetFile.toPath(), finalContent.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
            return true;
        } catch (IOException e) {
        }
        return false;
    }


    /**
     * 递归会写吧~
     * 
     * @param rootNode      根节点
     * @param callback      把每个 node 回调返回给外部,在回调中可操作 node 等
     */
    private static void traverseNode(Node node, XmlNodeCallback callback) {
        if (node == null) {
            return;
        }

        if (callback != null) {
            callback.onNode(node);
        }

        List<Object> children = node.children();
        boolean hasChildren = children != null && !children.isEmpty();
        if (hasChildren) {
            for (Object child : children) {
            	// 仅遍历 node 类型,因为 children 可存在 String 等,调用递归就不合适了
            	// 比如存在 <name>lf</name>,其中值 lf 也是作为 children 的一个元素,
            	// 目前不对他进行递归(如果需要回调给外部,也可以在 XmlNodeCallback 新增一个接口,通过 callback 回调数据) 
                if (child instanceof Node) {
                    traverseNode((Node) child, callback);
                } else {
                    PluginLog.d("traverseNode: " + child.getClass() + "  val:" + child);
                }
            }
        }
    }
}

使用接口,回调每一个递归遍历到的 node,在回调中处理逻辑

java 复制代码
public interface XmlNodeCallback {
    void onNode(Node node);
}

直接使用

groovy 复制代码
  File xmlFile = new File(****)
  def rootNode = PluginXmlUtil.parseXml(xmlFile, new XmlNodeCallback() {
     @Override
     void onNode(Node node) {
         if (node == null) {
             return
         }

         String nodeName = node.name()
         String[] nodeAttr = node.attributes()
         if (CommUtils.isEmptyOrNoExists(nodeName)) {
             return
         }
         PluginLog.d("nodeName:" + nodeName + "  nodeAttr:  " + nodeAttr + " value: " + node.value)
		
		// TODO: 2024/1/10  处理你的逻辑

     }
 })

  // 比如,我要在跟节点下面添加一个标签
 rootNode.append(ArgUtil.genDefaultBaseConfigNode())
 //然后保存修改
 def saveSuccess = PluginXmlUtil.saveNodeToFile(rootNode, xmlFile)

默认配置

java 复制代码
package ***.utils;

import org.xml.sax.SAXException;

import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;

import groovy.util.Node;
import groovy.util.XmlParser;


public class ArgUtil {
    public static Node genDefaultBaseConfigNode() throws ParserConfigurationException, SAXException, IOException {
        return new XmlParser().parseText("<base-config cleartextTrafficPermitted=\"true\">\n" +
                "        <trust-anchors>\n" +
                "            <certificates src=\"user\" />\n" +
                "            <certificates src=\"system\" />\n" +
                "        </trust-anchors>\n" +
                "    </base-config>");
    }

    public static Node genDefaultTrustAnchorsNode() throws ParserConfigurationException, SAXException, IOException {
        return new XmlParser().parseText("<trust-anchors>\n" +
                "            <certificates src=\"user\" />\n" +
                "            <certificates src=\"system\" />\n" +
                "        </trust-anchors>");
    }

    public static Node genDefaultCertificatesNode(String value) throws ParserConfigurationException, SAXException, IOException {
        return new XmlParser().parseText("<certificates src=" + value + " />");
    }
}

学会了新增,删除、修改都不是问题吧~

相关推荐
尚久龙28 分钟前
安卓学习 之 用户登录界面的简单实现
android·运维·服务器·学习·手机·android studio·安卓
Modu_MrLiu32 分钟前
Android实战进阶 - 启动页
android·实战进阶·启动页·倒计时场景
出门吃三碗饭1 小时前
编译器构造:从零手写汇编与反汇编程序(一)
android·汇编
Just_Paranoid2 小时前
【WorkManager】无法在 Direct Boot 模式下初始化
android·jetpack·usermanager·workmanager·directboot
前端小超超2 小时前
如何配置capacitor 打包的安卓app固定竖屏展示?
android·前端·gitee
顾林海2 小时前
探秘Android JVM TI:虚拟机背后的"隐形管家"
android·面试·性能优化
刘大国4 小时前
<android>反编译魔改安卓系统应用并替换
android
恋猫de小郭4 小时前
Flutter Riverpod 3.0 发布,大规模重构下的全新状态管理框架
android·前端·flutter
纤瘦的鲸鱼4 小时前
MySQL慢查询
android·adb
郭庆汝5 小时前
模型部署:(三)安卓端部署Yolov8-v8.2.99目标检测项目全流程记录
android·yolo·目标检测·yolov8