之前写过一篇文章,使用xstream将xml文件转成对象,文章链接:java使用xstream实现xml文件和对象之间的相互转换,使用起来很简单,但是有个问题,就是这种方法只能处理已知的xml中的字段,然后提前将对象创建处理,如果xml中有一些我们不能提前获知的字段,使用这种方法转成对象之后,在反转回xml,那些在对象中没有创建的字段就会丢失。
这里我们使用dom4j的方法,直接操作xml文件,就能避免这种问题。但是两种方法各有优劣,使用的时候可以根据情况进行选择。
在这对上面两种解析xml的方式做一个简单对比:
解析方式 | xstream | dom4j |
---|---|---|
优点 | 解析操作简单,代码复用性强 ,存入数据库操作方便 | 可以直接操作xml文件,不回造成字段丢失 |
缺点 | 对xml中未知字段无法处理 | 解析操作相对复杂,代码复用性低,同时操作文件问题 |
添加xstream的依赖
org.dom4j
dom4j
2.1.3
这里还是使用的是大疆无人机的地图文件template.kml :
xml文件参考文档:大疆云上API地址: 大疆云上API。
文件具体格式如下,基本包含了xml所有需要使用的示例。
注:文件虽然是.kml格式,但其实就是xml的格式。
Name
1637600807044
1637600875837
safely
goHome
goContinue
20
10
67
0
52
0
0
waypoint
0
0
WGS84
EGM96
100
100
GPS
7
7
usePointSetting
followWayline
toPointAndStopWithDiscontinuityCurvature
longitude,latitude
0
90.2
100
1
1
1
1
0
longitude,latitude
1
90.2
100
1
1
1
1
0
0
1
1
sequence
reachPoint
0
gimbalRotate
absoluteAngle
0
0
0
0
1
30
0
0
0
1
takePhoto
point1
0
/**
* 航线文件常量
*
* @author nhx
* @date 2022-08-22
*/
public interface WaylineConstant {
/**
* Document
*/
String Document = "Document";
/**
* Folder
*/
String Folder = "Folder";
/**
* Placemark
*/
String Placemark = "Placemark";
/**
* actionGroup
*/
String actionGroup = "actionGroup";
/**
* index
*/
String index = "index";
/**
* action
*/
String action = "action";
/**
* actionActuatorFunc
*/
String actionActuatorFunc = "actionActuatorFunc";
/**
* actionActuatorFuncParam
*/
String actionActuatorFuncParam = "actionActuatorFuncParam";
/**
* takePhoto
*/
String takePhoto = "takePhoto";
/**
* wpml:fileSuffix
*/
String addFileSuffix = "wpml:fileSuffix";
/**
* fileSuffix
*/
String fileSuffix = "fileSuffix";
/**
* fileSuffix
*/
String actionGroupStartIndex = "actionGroupStartIndex";
/**
* fileSuffix
*/
String actionGroupEndIndex = "actionGroupEndIndex";
}
此处对读写xml文件单独封装一个方法,在其中对xml文件修改保存时进行了一些格式设置,具体详见代码。
/**
* 将Document对象写入到xml文件
*
* @param document 写入的Document
* @param fileNameDec 写入文件路径
* @param append 是否追加写入,true表示不覆盖原来的内容,而是加到文件的后面
*/
public static void documentToFile(Document document, String fileNameDec, boolean append) {
try {
OutputFormat outputFormat = OutputFormat.createPrettyPrint();
// 设置XML编码方式,即是用指定的编码方式保存XML文档到字符串(String),这里也可以指定为GBK或是ISO8859-1
outputFormat.setEncoding("UTF-8");
//outputFormat.setSuppressDeclaration(true); //是否生产xml头
// 设置是否缩进
outputFormat.setIndent(true);
// 以两个空格方式实现缩进
outputFormat.setIndent(" ");
// 设置是否换行
outputFormat.setNewlines(true);
// xml字符串
StringWriter stringWriter = new StringWriter();
// xmlWriter是用来把XML文档写入字符串
XMLWriter xmlWriter = new XMLWriter(stringWriter, outputFormat);
// 把创建好的XML文档写入字符串
xmlWriter.write(document);
//写入文件
writerFile(stringWriter.toString(), fileNameDec, append);
} catch (Exception e) {
log.error("documentToFile:{}", e.toString());
e.printStackTrace();
}
}
/**
* 将字符串写入指定路径的文件中
*
* @param encryptText 写入字符串
* @param fileNameDec 写入文件路径
* @param append 是否追加写入,true表示不覆盖原来的内容,而是加到文件的后面
*/
public static void writerFile(String encryptText, String fileNameDec, boolean append) {
FileWriter writer = null;
try {
writer = new FileWriter(fileNameDec, append);
writer.write(encryptText);
writer.flush();
writer.close();
} catch (IOException e) {
log.error("writerFile:{}", e.toString());
e.printStackTrace();
}
}
使用dom4j这种方式,就需要用代码进行逐层读取,这就是代码复用性差的原因,因为一旦xml结构变了,解析的代码需要重新写。
/**
* xml解析测试方法
*
* @param filePath 文件路径
*/
public static void testXml(String filePath) {
try {
SAXReader reader = new SAXReader();
Document document = reader.read(filePath);
if (Objects.isNull(document)) {
return;
}
Element root = document.getRootElement();
if (Objects.isNull(root)) {
return;
}
Element doc = root.element(WaylineConstant.Document);
if (Objects.isNull(doc)) {
return;
}
Element folder = doc.element(WaylineConstant.Folder);
if (Objects.isNull(folder)) {
return;
}
List elementList = folder.elements(WaylineConstant.Placemark);
if (Objects.isNull(elementList) || elementList.size() <= 0) {
return;
}
for (Element placemark : elementList) {
// 循环航点
Element actionGroup = placemark.element(WaylineConstant.actionGroup);
if (Objects.isNull(actionGroup)) {
continue;
}
String index = placemark.element(WaylineConstant.index).getText();
List actionList = actionGroup.elements(WaylineConstant.action);
if (Objects.isNull(actionList) || actionList.size() <= 0) {
continue;
}
for (Element action : actionList) {
// 循环航点动作
}
}
// 写入文件
FileUtils.documentToFile(document, filePath, false);
} catch (DocumentException e) {
e.printStackTrace();
}
}
至此,解析和修改参数的工作就完成了,还需要注意的,由于直接对xml文件进行操作,如果有同时操作同一个航线文件的情况,会有问题,需要加一个线程锁或者文件锁之类的判断。