Nevcfrdefdz
New Member
So we have an XML Document with custom namespaces. (The XML is generated by software we don't control. It's parsed by a namespace-unaware DOM parser; standard Java7SE/Xerces stuff, but also outside our effective control.) The input data looks like this:\[code\]<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?><MainTag xmlns="http://BlahBlahBlah" xmlns:CustomAttr="http://BlitherBlither"> .... 18 blarzillion lines of XML .... <Thing CustomAttr="borkborkbork" ... /> .... another 27 blarzillion lines ....</MainTag>\[/code\]The Document we get is usable and xpath-queryable and traversable and so on.Converting this Document into a text format for writing out to a data sink uses the standard Transformer approach described in a hundred SO "how do I change my XML Document into a Java string?" questions:\[code\]Transformer transformer = TransformerFactory.newInstance().newTransformer();transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");transformer.setOutputProperty(OutputKeys.INDENT, "yes");StringWriter stringwriter = new StringWriter();transformer.transform (new DOMSource(theXMLDocument), new StreamResult(stringwriter));return stringwriter.toString();\[/code\]and it works perfectly.But now I'd like to transform individual arbitrary Nodes from that Document into strings. A \[code\]DOMSource\[/code\] constructor accepts Node pointers just the same as it accepts a \[code\]Document\[/code\] (and in fact Document is just a subclass of Node, so it's the same API as far as I can tell). So passing in an individual Node in the place of "theXMLDocument" in the snippet above works great... until we get to the \[code\]Thing\[/code\].At that point, \[code\]transform()\[/code\] throws an exception:\[code\]java.lang.RuntimeException: Namespace for prefix 'CustomAttr' has not been declared. at com.sun.org.apache.xml.internal.serializer.SerializerBase.getNamespaceURI(Unknown Source) at com.sun.org.apache.xml.internal.serializer.SerializerBase.addAttribute(Unknown Source) at com.sun.org.apache.xml.internal.serializer.ToUnknownStream.addAttribute(Unknown Source) ......\[/code\]That makes sense. (The "com.sun.org.apache" is weird to read, but whatever.) It makes sense, because the namespace for the custom attribute was declared at the root node, but now the transformer is starting at a child node and can't see the declarations "above" it in the tree. So I think I understand the problem, or at least the symptom, but I'm not sure how to solve it though.
- If this were a String-to-Document conversion, we'd be using a \[code\]DocumentBuilderFactory\[/code\] instance and could call \[code\].setNamespaceAware(false)\[/code\], but this is going in the other direction.
- None of the available properties for \[code\]transformer.setOutputProperty()\[/code\] affect the namespaceURI lookup, which makes sense.
- There is no such corresponding \[code\]setInputProperty\[/code\] or similar function.
- The input parser wasn't namespace aware, which is how the "upstream" code got as far as creating its Document to hand to us. I don't know how to hand that particular status flag on to the transforming code, which is what I really would like to do, I think.
- I believe it's possible to (somehow) add a \[code\]xmlns:CustomAttr="http://BlitherBlither"\[/code\] attribute to the Thing node, the same as the root MainTag had. But at that point the output is no longer identical XML to what was read in, even if it "means" the same thing, and the text strings are eventually going to be compared in the future. We wouldn't know if it were needed until the exception got thrown, then we could add it and try again... ick. For that matter, changing the Node would alter the original Document, and this really ought to be a read-only operation.