要对一个带CDATA的XML文档的某些element内容加密,加密后除了被加密的element外,其他内容不能变。这个需求看起来比较简单,为了有比较好的性能,用了java XML stream API。先看看能否把一个XML文档由XMLStreamReader读出,再由XMLStreamWriter写入新的XML文件。代码比较简单:
import javax.xml.stream.*;
import java.io.StringWriter;
import java.io.FileReader;
public class XMLStreamWriterTest {
public static void main(String...args ) throws Exception {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLStreamReader streamReader = inputFactory.createXMLStreamReader( new FileReader( args[0] ) );
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
StringWriter writer = new StringWriter();
XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter( writer );
StringBuilder content = new StringBuilder();
while (streamReader.hasNext()) {
switch( streamReader.next() ) {
case XMLStreamConstants.START_ELEMENT:
if( content.length() > 0 ) {
streamWriter.writeCharacters( content.toString() );
}
content.setLength( 0 );
streamWriter.writeStartElement( streamReader.getLocalName() );
//write the attribute
for( int i = 0, n = streamReader.getAttributeCount(); i < n; i++ ) {
streamWriter.writeAttribute(streamReader.getAttributeLocalName(i),streamReader.getAttributeValue(i));
}
break;
case XMLStreamConstants.END_ELEMENT:
if( content.length() > 0 ) {
streamWriter.writeCharacters( content.toString());
content.setLength(0);
}
streamWriter.writeEndElement();
break;
case XMLStreamConstants.SPACE:
case XMLStreamConstants.CHARACTERS:
content.append( streamReader.getText() );
break;
case XMLStreamConstants.CDATA:
streamWriter.writeCData( streamReader.getText());
break;
case XMLStreamConstants.ENTITY_REFERENCE:
streamWriter.writeEntityRef( streamReader.getText());
break;
case XMLStreamConstants.COMMENT:
streamWriter.writeComment( streamReader.getText());
break;
case XMLStreamConstants.DTD:
streamWriter.writeDTD( streamReader.getText());
break;
}
}
System.out.println( writer );
}
}
用文件Test1.xml测试一下,Test1.xml的内容如下:
<Element1>
<Child1>this is first child</Child1>
<Child2>this is second child</Child2>
</Element1>
$ java -cp . XMLStreamWriterTest Test1.xml
输出:
<Element1>
<Child1>this is first child</Child1>
<Child2>this is second child</Child2>
</Element1>
完全一样。
再用带CDATA的XML文件Test2.xml测试一下, Test2.xml的内容如下:
<Element1>
<Child1>this is first child</Child1>
<Child2><![CDATA[this is second child]]></Child2>
</Element1>
$ java -cp . XMLStreamWriterTest Test2.xml
输出:
<Element1>
<Child1>this is first child</Child1>
<Child2>this is second child</Child2>
</Element1>
给想要的结果不一样。怎么回事?上网查了一下,方法如下:
1) 需要将inputFactor中的属性javax.xml.stream.isCoalescing设置为false
2)需要将inputFactory中的属性"http://java.sun.com/xml/stream/properties/report-cdata-event"设置为true
修改代码:
import javax.xml.stream.*;
import java.io.StringWriter;
import java.io.FileReader;
public class XMLStreamWriterTest {
public static void main(String...args ) throws Exception {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
inputFactory.setProperty( XMLInputFactory.IS_COALESCING, Boolean.FALSE );
inputFactory.setProperty( "http://java.sun.com/xml/stream/properties/report-cdata-event", Boolean.TRUE );
XMLStreamReader streamReader = inputFactory.createXMLStreamReader( new FileReader( args[0] ) );
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
StringWriter writer = new StringWriter();
XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter( writer );
StringBuilder content = new StringBuilder();
while (streamReader.hasNext()) {
switch( streamReader.next() ) {
case XMLStreamConstants.START_ELEMENT:
if( content.length() > 0 ) {
streamWriter.writeCharacters( content.toString() );
}
content.setLength( 0 );
streamWriter.writeStartElement( streamReader.getLocalName() );
//write the attribute
for( int i = 0, n = streamReader.getAttributeCount(); i < n; i++ ) {
streamWriter.writeAttribute(streamReader.getAttributeLocalName(i),streamReader.getAttributeValue(i));
}
break;
case XMLStreamConstants.END_ELEMENT:
if( content.length() > 0 ) {
streamWriter.writeCharacters( content.toString());
content.setLength(0);
}
streamWriter.writeEndElement();
break;
case XMLStreamConstants.SPACE:
case XMLStreamConstants.CHARACTERS:
content.append( streamReader.getText() );
break;
case XMLStreamConstants.CDATA:
streamWriter.writeCData( streamReader.getText());
break;
case XMLStreamConstants.ENTITY_REFERENCE:
streamWriter.writeEntityRef( streamReader.getText());
break;
case XMLStreamConstants.COMMENT:
streamWriter.writeComment( streamReader.getText());
break;
case XMLStreamConstants.DTD:
streamWriter.writeDTD( streamReader.getText());
break;
}
}
System.out.println( writer );
}
}
再运行一次:
$ java -cp . XMLStreamWriterTest Test2.xml
结果如下:
<Element1>
<Child1>this is first child</Child1>
<Child2><![CDATA[this is second child]]></Child2>
</Element1>
结果符合预期。