这几天工作中遇到一个利用 dom4j 更新XML文件的任务,由于XML文件中部分属性包含有换行符,利用 dom4j(1.6.1) 默认的方法更新XML文件后换行符会丢失。 各种Google、StackOverflow折腾好久后终于解决该问题,简单记录下。

对于修改XML文件,自己很自然的想到利用 dom4j XPath 来实现功能,使用的代码类似如下:

public static void updateXML() {
    SAXReader saxReader = new SAXReader();
    File oldFile = new File("D:\\test\\old_test.xml");
    File newFile = new File("D:\\test\\new_test.xml");
    try {
       Document oldDoc = saxReader.read(oldFile);
       Element oldRoot = oldDoc.getRootElement();
       Element oldEle = (Element) oldRoot.selectSingleNode("//book[@id='01001']/name");
       System.out.println(oldEle.attributeValue("description"));
       OutputFormat format = OutputFormat.createPrettyPrint();
       format.setEncoding("UTF-8");
       format.setNewLineAfterDeclaration(false);
       //写入新文件
       XMLWriter writer = new XMLWriter(new FileWriter(newFile), format);
       writer.write(oldDoc);
       writer.flush();
       writer.close();
       System.out.println("\n================分隔符==================\n");
       //从新文件中读取数据
       Document newDoc = saxReader.read(newFile);
       Element newRoot = newDoc.getRootElement();
       Element newEle = (Element) newRoot.selectSingleNode("//book[@id='01001']/name");
       System.out.println(newEle.attributeValue("description"));
    } catch (DocumentException e) {
       e.printStackTrace();
    } catch (IOException e) {
       e.printStackTrace();

对应的XML文件类似如下:

<?xml version="1.0" encoding="utf-8"?>
<books>
  <book id="01001">
    <name description="New coverage includes:
Functional interfaces, lambda expressions, method references, and streams
Default and static methods in interfaces
Type inference, including the diamond operator for generic types
The @SafeVarargs annotation
The try-with-resources statement">Effective Java</name>
    <price>50.6</price>
    <author>Joshua Bloch</author>
    <publishDate>2017-12-08</publishDate>
  </book>
</books>

上述代码的逻辑很简单:首先从XML打开一个XML文件,然后输出某个书本的描述信息,将其写入新的XML文件,然后在新XML文件中读取相同的信息。理论上前后两次输出的结果应该一样,但实际运行后发现从新的XML文件中读取出的描述信息换行符都丢失了,前后两次输出的结果不一致! 

对比新旧XML文件后发现,产生此现象的原因是: dom4j 自作主张的在写入XML文件时将 &#xD;&#xA; 替换为了 \r\n ,而XML文件中标准的回车换行符是用 &#xD;&#xA; 来表示的 ,将它们替换后再次读取的结果很显然不符合要求。

了解到问题产生的根源后,则其解决思路也很明确: 写入XML文件时,将 \r\n 再次替换为 &#xD;&#xA; 即可。最开始自己想采用如下的方法来简单替换,运行完毕后发现结果和前面的一致,问题依旧。

String description = oldEle.attributeValue("description");
description = description.replaceAll("\r\n","
oldEle.attributeValue("description",description);

进一步分析后发现,*dom4j* 不仅会在读取XML文件时对 &#xD;&#xA; 进行转义,而且在写入XML文件时也会对 &#xD;&#xA; 进行转义,前面的方法只是解决了读取的问题,写入时没有处理,所以问题依旧。

写入时主要的操作类是 OutputFormat 和 XMLWriter ,自己一开始以为可以通过 OutputFormat 进行响应的设置实现,将代码修改如下,然并卵,问题依旧!

OutputFormat format = OutputFormat.createPrettyPrint();
format.setNewlines(true);
format.setLineSeparator("\r\n");
format.setEncoding("UTF-8");
format.setNewLineAfterDeclaration(false);

OutputFormat 不好使,只能从 XMLWriter 着手,调用 writer.setEscapeText(false) 方法也不能解决问题,看来只能放出大招,自己定义实现一个 XMLWriter类,将以及转义后的回车换行符又换回去,代码如下:

public class HRXMLWriter extends XMLWriter {
    public HRXMLWriter(Writer wr, OutputFormat format) {
       super(wr, format);
    @Override
    protected String escapeAttributeEntities(String text) {
       text = super.escapeAttributeEntities(text);
       if (text.indexOf("\r\n") > -1) {
         text = text.replaceAll("\r\n", "
       return text;

然后将写入时的代码修改如下:

//採用定义写入类HRXMLWriter
XMLWriter writer = new HRXMLWriter(new FileWriter(newFile), format);
writer.write(oldDoc);
writer.flush();
writer.close();

运行结果如下,问题顺利解决!

----------------------------------------------------------------------------------------------------------------------------------

上面是我查找资料时看到的说明比较全面的一篇,所以转载过来了。

但是实际我的问题不太一样,是用另一种方式解决的。如下

//直接传递的是xml格式的字符串,在存储的时候转换成字符串形式的\r\n存储
//数据库存储形式是clob
SAXReader xmlReader = new SAXReader();
Document document = xmlReader.read(new StringReader(HtmlUtils.htmlUnescape(xml.replaceAll("&#10;", "\\\\r\\\\n"))));
//从数据库获取到数据,在替换\r\n为&#xa;
xml = xml.replaceAll("\\\\r\\\\n", "&#xa;").replace(">&#xa;<", "><");
这几天工作中遇到一个利用dom4j更新XML文件的任务,由于XML文件中部分属性包含有换行符,利用dom4j(1.6.1)默认的方法更新XML文件后换行符会丢失。 各种Google、StackOverflow折腾好久后终于解决该问题,简单记录下。对于修改XML文件,自己很自然的想到利用dom4j和XPath来实现功能,使用的代码类似如下:public static void updateXML() { SAXReader saxReader = new SAXReader(...
直接用dom4j是没法解决问题的,因为在第一步parseText返回 Document对象的时候内部节点属性值中回车就被删了。 通过所谓的setTrim 那个方法没有用,因为第一步就错了 需要用到 dom4j 和 jsoup <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --> <dependency> <groupId>org.jsoup</g
public static List<Element> parseXML(HashMap<String, List<String>> map) throws Exception { // 创建saxreader对象 SAXReader reader = new SAXReader(); // 读取一个文件,把这个文件转换成Document对象 Document document = reader...
使用SAX解析XML文档效率比较高,占用内存比较少,所以是Android中解析XML的首先方案。但使用SAX解析XML文档编写程序确不太方便,如果对SAX解析机制不太了解的话,很容易漏掉文本内容。 使用SAX解析XML主要用到的方法有以下3个: public void startElement(String uri, String localName, String qName, Attrib...
android使用sax解析xml时,碰到换行符不能读取的问题,以下方法可以解决,定义temp变量累加new String(ch,start,length)读取的值,最后在endElement方法中赋值给你需要的属性或变量就可以了,原因可能是sax循环读取字符内容时,碰到回车扔掉之前读取的值。 1privateStringtemp="";23/*...