我知道为什么我的代码会引发一个问题,即xml文件中的某些节点实际上并没有被删除。
我需要维护一个java应用程序,这个应用程序的目的是比较两个PDF文件。应用程序从xml文件中的两个服务器中提取数据;然后它删除两个xml文件中的一些节点以忽略某些部分,最后应用程序将两个被操纵的xml文件发送回两个服务器以获取PDF文件,并拉取PDF文件以字节比较。该应用程序使用JDOM API来解析和操作xml文件。
这是我的代码:
/**
* Ignores all Changes of this Form
*/
@SuppressWarnings("rawtypes")
protected void ignoreChangesSection() {
Element formNumberElement=retrieveElement(getSiapXmlDocument(), FORM_REQUESTED_ELEMENT).getChild(FORM_NUMBER_ELEMENT);
String formNumber=formNumberElement.getText();
/*retrieveElement(getSiapXmlDocument(), DATA_ELEMENT).getChild(APPROACH_RESOURCE_INSTANCE_ELEMENT).
getChild(APPROACH_ELEMENT).removeChildren(CHANGES_ELEMENT);*/
Element approachElement =
retrieveElement(getSiapXmlDocument(), DATA_ELEMENT).getChild(APPROACH_RESOURCE_INSTANCE_ELEMENT).
getChild(APPROACH_ELEMENT);
List changeReasonList1 = approachElement.getChildren(CHANGES_ELEMENT);
if (changeReasonList1!=null)
{
for (int i=0; i<changeReasonList1.size(); i++)
{
Element chngRsn = (Element)changeReasonList1.get(i);
String ind = chngRsn.getChildText(REASON_CHANGE_IND);
if (ind != null && !"".equals(ind))
{
if (ind.equals(CHANGE) || (formNumber.equals("8260-7A") && ind.equals(CHANGE_FORM_7A)))
{
chngRsn.detach();
}
}
}
}
}
在approachElement下有9个节点与“ChangeReasons”完全相同,是否应该删除它们取决于“REASON_CHANGE_IND”属性的值,所以我使用循环来完成这项工作。但是,通常不会删除一个应该删除的节点。这个问题不时发生,而不是每次都发生。我怀疑它与线程有关。由于有2个线程正在执行相同的工作,因此从不同的xml文件中删除相同的节点。但是,该对象是在每个线程内创建的,其类具有此方法。所以我想没有线程会对其他线程有所了解。如果我在代码中使用removeChilren,我没有这个问题。但它会删除所有节点,而不是我要删除的某些节点。任何人都可以给我一个暗示,为什么会出现这个问题和任何解决方案?
非常感谢!
萨姆
答案 0 :(得分:0)
分离节点时,还要将列表的大小减一......考虑一个包含四个节点A,B,B,C的列表。
您想要删除/分离B节点
在你的循环中,你迭代并从0开始,然后移到1 ....你删除节点1.但是,现在你的列表是:A,B,C,你的索引是1.然后你再次循环,你的索引是2,它指向C,然后你退出循环。
您不会删除第二个节点B.
这是一个常见的计算错误,在循环遍历列表时,您无法轻松地从列表中删除节点。
Java提供了一个Iterator,JDOM也实现了它,这将是您正确的选择......现在,您应该使用JDOM 2.x而不是JDOM 1.x. 2会更容易,但是,即使使用JDOM 1 ....使用迭代器:
for (Iterator it = changeReasonList1.iterator(); it.hasNext();) {
Element chngRsn = (Element)it.next();
if (various reasons) {
// detach the element:
it.remove();
}
}
编辑,从评论中更新:
两个关键点是:
emt.detach()
从其父级中移除emt
。approachElement.getChildren(...)
返回&#39;直播&#39;名单。对列表的更改将反映在元素中,反之亦然。因此,当您遍历luve列表并将emt
作为索引get(...)
时,您将获得一个子元素。当您分离该子项时,您将其与父项断开连接,从而也将该子项从approachElement
列表中删除。该列表缩小了一个。
所以,你的循环:
for (int i=0; i<changeReasonList1.size(); i++)
当你这样做时:
chngRsn.detach();
实质上你也在做:
changeReasonList1.remove(i);
这是关键点.....你在列表循环中有效地删除(i)。如果你把JDOM从quation中拿出来,并且情况更简单:
List<Integer> data = new ArrayList<>();
data.add(1);
data.add(2);
data.add(3);
for (int i = 0; i < data.size(); i++) {
System.out.println("before: " + data.get(i));
if (i == 1) {
data.remove(i);
}
System.out.println("after: " + data.get(i));
}
使用上面的代码,它将起作用,并打印:
before: 1
after: 1
before: 2
after: 3
然后退出.....
因此,它永远不会测试&#39;之前的值3
因为,删除2
,我们缩小列表,对于最后一个循环,大小为2,并且索引在循环中递增为2,因此循环是终止。
Iterator避免了这一点。