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 + " />");
    }
}

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

相关推荐
百锦再8 分钟前
第15章 并发编程
android·java·开发语言·python·rust·django·go
Propeller1 小时前
【Android】模板化解决复杂场景的滑动冲突问题
android·java
byte轻骑兵2 小时前
Rust赋能Android蓝牙协议栈:从C++到安全高效的重构之路
android·c++·rust
從南走到北4 小时前
JAVA国际版二手车交易二手车市场系统源码支持Android+IOS+H5+APP
android·java·ios
江上清风山间明月4 小时前
Android 系统中进程和线程的区别
android·python·线程·进程
2501_940094025 小时前
mig烧录卡资源 Mig-Switch游戏合集 烧录卡 1.75T
android·游戏·安卓·switch
渡我白衣5 小时前
深入理解 OverlayFS:用分层的方式重新组织 Linux 文件系统
android·java·linux·运维·服务器·开发语言·人工智能
2501_915106325 小时前
iOS性能调优的系统化实践,从架构分层到多工具协同的全流程优化指南(开发者深度版)
android·ios·小程序·架构·uni-app·iphone·webview
stevenzqzq6 小时前
android recyclerview缓存_缓存问题解决办法
android·java·缓存
下位子6 小时前
『OpenGL学习滤镜相机』- Day10: 相机预览与 OpenGL 结合
android·opengl