如何阻止javax.xml.transform.Transformer在UTF-8编码下替换非BMP字符
阻止javax.xml.transform.Transformer在UTF-8编码下转非BMP Unicode字符为数值引用
背景
我希望生成包含非BMP Unicode字符的UTF-8编码XML文件。
问题现象
使用以下Java代码生成XML文件时,非BMP Unicode字符被自动替换为数值字符引用,不符合预期。
测试代码
package xml; import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; public class XMLClass { static String names[] = {"𠀋一郎", "𠮷野","辻󠄀","👨👩👦"}; public static void main(String[] args) { DocumentBuilder documentBuilder = null; try { documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } Document document = documentBuilder.newDocument(); document.setXmlStandalone(true); Element list = document.createElement("list"); document.appendChild(list); for (int i = 0; i < names.length; i++) { Element name = (Element) document.createElement("name").cloneNode(false); list.appendChild(name); name.appendChild(document.createTextNode(names[i])); } File file = new File("NameList.xml"); write(file, document); } public static boolean write(File file, Document document) { Transformer transformer = null; try { TransformerFactory transformerFactory = TransformerFactory.newInstance(); transformer = transformerFactory.newTransformer(); } catch (TransformerConfigurationException e) { e.printStackTrace(); return false; } transformer.setOutputProperty("indent", "yes"); // non-BMP characters written in characters (no numeric character reference style) // when you set encoding UTF-16 transformer.setOutputProperty("encoding", "UTF-8"); transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); try { transformer.transform(new DOMSource(document), new StreamResult( file)); } catch (TransformerException e) { e.printStackTrace(); return false; } return true; } }
期望输出
<?xml version="1.0" encoding="UTF-8"?><list> <name>𠀋一郎</name> <name>𠮷野</name> <name>辻󠄀</name> <name>👨👩👦</name> </list>
实际输出
<?xml version="1.0" encoding="UTF-8"?><list> <name>𠀋一郎</name> <name>𠮷野</name> <name>辻󠄀</name> <name>👨👩👦</name> </list>
核心问题
当指定使用UTF-8编码时,如何阻止javax.xml.transform.Transformer将非BMP Unicode字符替换为数值字符引用?
解决方案
要让Transformer直接输出非BMP Unicode字符,只需在配置Transformer时添加Xalan特定的输出属性,同时确保输出流的编码明确为UTF-8:
修改后的write方法
import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; // ... 保留原有导入 public static boolean write(File file, Document document) { Transformer transformer = null; try { TransformerFactory transformerFactory = TransformerFactory.newInstance(); transformer = transformerFactory.newTransformer(); } catch (TransformerConfigurationException e) { e.printStackTrace(); return false; } transformer.setOutputProperty("indent", "yes"); transformer.setOutputProperty("encoding", "UTF-8"); // 关键:设置此属性以保留非BMP Unicode字符 transformer.setOutputProperty("{http://xml.apache.org/xalan}characters", "true"); transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); try { // 明确指定UTF-8编码的Writer,避免编码不一致 try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { transformer.transform(new DOMSource(document), new StreamResult(writer)); } } catch (TransformerException | IOException e) { e.printStackTrace(); return false; } return true; }
说明
{http://xml.apache.org/xalan}characters属性设置为true后,JDK默认的Xalan Transformer会直接输出原始Unicode字符,而非将非BMP字符转为数值引用。- 使用
OutputStreamWriter明确指定UTF-8编码,确保文件实际编码与XML声明中的编码一致,避免潜在的乱码或编码冲突问题。
内容的提问来源于stack exchange,提问作者SATO Yusuke
相关产品推荐
相关产品推荐

