您不能轻易地忽略命名空间, 而且它不会很漂亮,但它是可能的。当然,将Transformer 实现中的正确部分欺骗为仅输出前缀而不感到慌张是实现依赖!
好吧,这对我从 Node 到 StringWriter 有用:
public static String nodeToString(Node node) throws TransformerException {
StringWriter results = new StringWriter();
Transformer transformer = createTransformer();
transformer.transform(new DOMSource(node), new StreamResult(results) {
@Override
public Writer getWriter() {
Field field = findFirstAssignable(transformer.getClass());
try {
field.setAccessible(true);
field.set(transformer, new TransletOutputHandlerFactory(false) {
@Override
public SerializationHandler getSerializationHandler() throws
IOException, ParserConfigurationException {
SerializationHandler handler = super.getSerializationHandler();
SerializerBase base = (SerializerBase) handler.asDOMSerializer();
base.setNamespaceMappings(new NamespaceMappings() {
@Override
public String lookupNamespace(String prefix) {
return prefix;
}
});
return handler;
}
});
} catch(IllegalAccessException e) {
throw new AssertionError("Must not happen", e);
}
return super.getWriter();
}
});
return results.toString();
}
private static <E> Field findFirstAssignable(Class<E> clazz) {
return Stream.<Class<? super E>>iterate(clazz, Convert::iteration)
.flatMap(Convert::classToFields)
.filter(Convert::canAssign).findFirst().get();
}
private static <E> Class<? super E> iteration(Class<? super E> c) {
return c == null ? null : c.getSuperclass();
}
private static boolean canAssign(Field f) {
return f == null ||
f.getType().isAssignableFrom(TransletOutputHandlerFactory.class);
}
private static <E> Stream<Field> classToFields(Class<? super E> c) {
return c == null ? Stream.of((Field) null) :
Arrays.stream(c.getDeclaredFields());
}
这几乎只是自定义命名空间到前缀的映射。每个前缀都映射到由其前缀标识的命名空间,因此甚至不应该有任何冲突。剩下的就是与 API 作斗争。
为了使示例完整,以下是与 XML 相互转换的方法:
public static Transformer createTransformer()
throws TransformerFactoryConfigurationError,
TransformerConfigurationException {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "no");
return transformer;
}
public static ArrayList<Node> parseNodes(String uri, String expression)
throws ParserConfigurationException, SAXException,
IOException,XPathExpressionException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(uri);
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile(expression);
NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
ArrayList<Node> nodes = new ArrayList<>();
for(int i = 0; i < nl.getLength(); i++) {
nodes.add(nl.item(i));
}
return nodes;
}