Java Xml附加到文件末尾而不是替换文件
有关此目标背后动机的更多详细信息,请参见我的观点。我(再次)决定把这个问题作为一个全新的问题提出,因为我认为它已经发展到值得这样做的程度。作为总结,我打算将JDOM与NIO结合使用,以便:Java Xml附加到文件末尾而不是替换文件,java,xml,dom,channel,file-locking,Java,Xml,Dom,Channel,File Locking,有关此目标背后动机的更多详细信息,请参见我的观点。我(再次)决定把这个问题作为一个全新的问题提出,因为我认为它已经发展到值得这样做的程度。作为总结,我打算将JDOM与NIO结合使用,以便: 获取xml文件的独占文件锁定 将文件读入文档对象 进行任意更改(锁定仍处于活动状态!) 将更改写回xml文件 释放文件锁 通过重写FilterInputStream的关闭行为解决了这个问题,现在我有了一个锁定通道,可以使用Transformer.transform()写入该通道。然而,问题是,Transfor
FilterInputStream
的关闭行为解决了这个问题,现在我有了一个锁定通道,可以使用Transformer.transform()
写入该通道。然而,问题是,Transformer.Transform
没有替换原始文件,而是将新文件附加到原始文件的末尾,而不是替换它(参见图)
问题不在于文档
对象本身,这可以通过打印以下方法返回的字符串(使用文档
对象作为输入)看到:
public String toXMLString(Node node) {
try {
this.removeBlankTextNodes(node);
StringWriter sw = new StringWriter();
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(node), new StreamResult(sw));
return sw.toString();
} catch (TransformerException ex) {
throw new RuntimeException("Error converting to String", ex);
}
}
当为同一文件建立新通道并用作Transformer.transform()
的结果时,追加问题也不会发生。因此,只有当同一通道用于读写时,才会出现此问题(可能这就是为什么他们在调用DocumentBuilder.parse()时选择自动关闭通道的原因)
我已经彻底检查了文档,找不到任何指定Transformer.transform输出的相关选项(我已经搜索了Transformer/Transformer factory/StreamResult),虽然由于许多类都是抽象的,但我正在努力寻找实际的实现代码。在调试器中检查StreamResult
对象时,实际上似乎append选项设置为false,因此我的主要怀疑是我需要以某种方式清除通道(或相关缓冲区?)在读取操作完成之后。我尝试的最后一件事是使用代码channel.open(path.get(path),StandardOpenOption.CREATE)打开除“APPEND”之外的几乎所有选项
;这同样没有效果。请注意,我不能只是关闭并重新打开频道,因为这将释放文件锁。任何指针/建议都会很好!代码如下:
import java.nio.channels.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
public class Test2{
String path = "...Test 2.xml";
public Test2(){
Document doc = null;
DocumentBuilderFactory dbFactory;
DocumentBuilder dBuilder;
NodeList itemList;
Transformer transformer;
FileChannel channel;
Element newElement;
int prevNumber;
TransformerFactory transformerFactory ;
DOMSource source;
StreamResult result;
NonClosingInputStream ncis = null;
try {
channel = new RandomAccessFile(new File(path), "rw").getChannel();
FileLock lock = channel.lock(0L, Long.MAX_VALUE, false);
try {
dbFactory = DocumentBuilderFactory.newInstance();
dBuilder = dbFactory.newDocumentBuilder();
ncis = new NonClosingInputStream(Channels.newInputStream(channel));
doc = dBuilder.parse(ncis);
} catch (SAXException | IOException | ParserConfigurationException e) {
e.printStackTrace();
}
doc.getDocumentElement().normalize();
itemList = doc.getElementsByTagName("Item");
newElement = doc.createElement("Item");
prevNumber = Integer.parseInt(((Element) itemList.item(itemList.getLength() - 1)).getAttribute("Number"));
newElement.setAttribute("Number", (prevNumber + 1) + "");
doc.getDocumentElement().appendChild(newElement);
transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer();
source = new DOMSource(doc);
//channel.open(Paths.get(path), StandardOpenOption.CREATE);
result = new StreamResult(Channels.newOutputStream(channel));
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.transform(source, result);
channel.close();
} catch (IOException | TransformerException e) {
e.printStackTrace();
} finally {
try {
ncis.reallyClose();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class NonClosingInputStream extends FilterInputStream {
public NonClosingInputStream(InputStream it) {
super(it);
}
@Override
public void close() throws IOException {
// Do nothing.
}
public void reallyClose() throws IOException {
// Actually close.
in.close();
}
}
public static void main(String[] args){
new Test2();
}
}
这种情况几乎肯定会发生,因为您的通道在读取文件后位于文件的末尾
在执行转换之前,您应该能够通过执行以下操作来解决此问题:
channel.truncate(0);
这会将文件截断为0大小,并将位置重置为0
您好,谢谢您的回复。设置频道位置确实如您所说的那样有效,这是正确方向上的一步。不幸的是,我无法删除频道(删除文件没有效果)在不释放相关文件锁的情况下,有没有办法调整频道的大小?@btrs20看起来你应该可以截断文件,我想这应该可以解决问题。你能试试吗?太好了,谢谢!我花了将近一天的时间试图找出如何用20个字符完成一些可能的事情…@btrs20我想我们可以都有这样的日子。:)