使用 groovy.util.XmlParser
解析 xml 文件,对文件进行修改(新增标签),然后保存。
是不是 XmlParser 没有提供方法遍历每个节点,难道要自己写?
什么是递归?
不用说,想必都懂得~
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,在回调中处理逻辑
public interface XmlNodeCallback {
void onNode(Node node);
}
直接使用
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)
默认配置
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 + " />");
}
}
学会了新增,删除、修改都不是问题吧~