Java Transformer如何忽略名称空间

时间:2015-06-18 14:28:20

标签: java xml xslt

我必须将XML转换为XHTML,但XML定义了一个名称空间xmlns='http://www.lotus.com/dxl',它从未在整个XML中使用,因此解析器不会解析任何内容......

有没有办法忽略namepsaces?我正在使用Oracle java转换器import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory

或者有更好的图书馆吗?

2 个答案:

答案 0 :(得分:3)

不,你不能忽略命名空间。

如果名称空间声明xmlns =' http://www.lotus.com/dxl'出现在最外面的元素中,那么你就无法说它并没有在任何地方使用过。" - 相反,它无处不在!它有效地将文档中的每个元素名称更改为不同的名称。你无法忽视这一点。

如果您使用的是XSLT 2.0,那么您可以在样式表xpath-default-namespace="http://www.lotus.com/dxl"中说出几乎可以满足您的需求:它表示匹配模式或XPath表达式中任何未加前缀的名称都应该被解释引用命名空间http://www.lotus.com/dxl中的名称。遗憾的是,您选择了一台未实现XSLT 2.0的XSLT处理器。因此,您必须采取艰难的方式(通过搜索" XSLT默认命名空间"将在大约10,000个帖子中描述)。

答案 1 :(得分:3)

你不能轻易地忽略名称空间并且它不会很漂亮,但它是可能的。当然,在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和从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;
}