Bug #3068
closedCannot output a namespace node for the default namespace when the element is in no namespace
100%
Description
When an XML element is defined in the XML Schema as having a namespaced attribute with a default value, the identity transform breaks when serializing the XML.
XML defAttr.xml
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="main.xsd"/>
XML Schema main.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:ns1="defAttrNS">
<xs:import namespace="defAttrNS" schemaLocation="attributes.xsd"/>
<xs:element name="root">
<xs:complexType>
<xs:attribute ref="ns1:defAttr1" default="defVal"/>
</xs:complexType>
</xs:element>
</xs:schema>
Java code:
public class RunTransformationWithSAXParser {
private static ErrorListener errorListener = new ErrorListener() {
@Override
public void warning(TransformerException ex) throws TransformerException {
System.out.println("Warning: " + ex + "\nLocation: " + ex.getLocator());
}
@Override
public void fatalError(TransformerException ex) throws TransformerException {
System.out.println("Error: " + ex.getMessage());
}
@Override
public void error(TransformerException ex) throws TransformerException {
System.out.println("Error: " + ex + "\nLocation: " + ex.getLocator());
}
};
private static String xmlPath = "samples/dita_xsd_based/defAttr.xml";
private static String outPath = "samples/dita_xsd_based/out.xml";
public static void main(String[] args) throws TransformerException, InterruptedException, SAXNotRecognizedException, SAXNotSupportedException {
// Create transformer
TransformerFactoryImpl transformerFactory = new EnterpriseTransformerFactory();
transformerFactory.setErrorListener(errorListener);
transformerFactory.getConfiguration().setConfigurationProperty(
FeatureKeys.SCHEMA_VALIDATION_MODE,
Validation.toString(Validation.STRICT));
transformerFactory.setAttribute(net.sf.saxon.lib.FeatureKeys.XSLT_SCHEMA_AWARE, true);
Transformer transformer = transformerFactory.newTransformer();
transformer.setErrorListener(errorListener);
// Create XML SAX source
SAXSource xmlSource = new SAXSource(new InputSource(new File(xmlPath).toURI().toString()));
SAXParser saxParser = new SAXParser();
//EXM-11081 Set this feature so that the default attributes from the schema are reported...
saxParser.setFeature("http://apache.org/xml/features/validation/schema", true);
xmlSource.setXMLReader(saxParser);
// Output stream result
File outFile = new File(outPath);
if (outFile.exists()) {
outFile.delete();
}
Result outputTarget = new StreamResult(outFile);
// Run transformation
System.out.println("Run transformation...");
try {
transformer.transform(xmlSource, outputTarget);
System.out.println("Transformation done.");
} catch (Exception e) {
System.out.println("Transformation failed: " + e);
e.printStackTrace();
}
}
}
This used to work with Saxon prior to 9.7. Looking at this answer to a similar question on the Xerces mailing list:
http://mail-archives.apache.org/mod_mbox/xerces-j-users/201612.mbox/browser
it would seem that Xerces is not required to provide a qname for the default attribute on the SAX calls, it only provides local name and namespace.
Updated by Michael Kay almost 8 years ago
Thanks for bringing this to our attention.
XDM explicitly states: In the node-name of an attribute node, if a namespace URI is present then a prefix must also be present.
The cited message from Michael Glavassevich suggests that he believes the PSVI does not have the same constraint, in which case the mapping from PSVI to XDM needs to invent a prefix and add a namespace node.
Updated by Radu Coravu almost 8 years ago
Yes, exactly, we do a similar thing on our side when we use SAX to construct some kind of DOM hierarchy we use for the Author visual editing, something like:
public void startElement(String namespaceURI, String localName, String qName, Attributes attrs)
throws SAXException {
int n = attrs.getLength();
// Puts the attributes into the newly created element.
for (int i = 0; i < n; i++) {
String attrQName = attrs.getQName(i);
String uri = attrs.getURI(i);
if(uri != null && uri.length() > 0 && attrQName.indexOf(":") == -1) {
// Declare the synthetic namespace prefix.
String prefix = pnm.getPrefixForAttributeNamespace(uri);
if (prefix == null) {
// No known prefix, compute one and declare it.
prefix = XmlUtil.getProxyForNamespace(uri, pnm, prefixIndicesMap);
}
// Compose the QName.
attrQName = prefix + ":" + attrs.getLocalName(i);
}
and we keep like a stack of already existing proxy namespace mappings just to avoid declaring another prefix for a namespace which was already declared on a parent element.
Updated by Radu Coravu almost 8 years ago
Somehow the same sample XML+XSL and Java code works with Saxon 9.6. So there has been a change somewhere...
Updated by Michael Kay almost 8 years ago
The difference between 9.6 and 9.7 was that in 9.6 there was a NamespaceReducer in the pipeline. (It's still there as commented-out code in 9.7, IdentityTransformer line 372).
I have to say I'm a bit reluctant to add it back in just for this case. I think that the IdentityTransformer is entitled to assume that the event stream arriving from the SAXSource represents a well-formed XML document. The NamespaceReducer (which essentially performs the task of "namespace fixup") is doing a lot of work to validate that this is the case, yet there are many other possible errors in the input that it won't pick up.
The JAXP spec of course is as usual imprecise. The spec of SAXSource does say "Attempting to transform an input source that is not generated with a namespace-aware parser may result in errors." which I think provides some justification for the theory that if you get an event stream that doesn't correspond to a namespace-well-formed source document then you don't have to deal with it.
The actual error occurs because Saxon (despite the absence of a NamespaceReducer) is in fact attempting a certain level of namespace fixup. The first phase of serialization, called Sequence Normalization, is not really needed when doing an identity transformation, but Saxon does it anyway, by inserting a ComplexContentOutputter into the pipeline; and one of the things the CCO does (for reasons that escape me) is to check that prefixes for element and attribute names are declared, and where necessary to insert a namespace declaration. This check is detecting the presence of an attribute with a namespace URI but no prefix, and it is attempting to add a default namespace declaration to correct this, which of course cannot possibly succeed because the attribute would not use the default namespace anyway. So I guess I could improve this check.
Updated by Radu Coravu almost 8 years ago
Do you mean that you want to report a more precise problem (indeed the current reported error message has no connection to the problem) or fix the qname of the attribute so that this case starts working?
I'm not sure if the "net.sf.saxon.event.ComplexContentOutputter.namespace(NamespaceBinding, int)" method can fix the attribute qname, probably this needs to be done before calling the outputter.
Updated by Michael Kay almost 8 years ago
- Category set to JAXP Java API
- Status changed from New to Resolved
- Assignee set to Michael Kay
- Priority changed from Low to Normal
- Applies to branch 9.8 added
- Fix Committed on Branch 9.7, 9.8 added
I've changed the code in ComplexContentOutputter.checkProposedPrefix() so that in the case where an attribute node has a namespace URI but no prefix, rather than attempting to emit a default namespace declaration for that namespace URI, it generates an arbitrary prefix and emits a namespace declaration for that prefix.
This fixes the supplied test case (there is no longer an error, and the output contains the attribute with a prefix, and the prefix is declared).
This doesn't imply any commitment to accept any arbitrary event stream coming over the SAX interface in future...
Updated by O'Neil Delpratt almost 8 years ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in Maintenance Release 9.7.0.15 added
Bug fix applied to the Saxon 9.7.0.15 maintenance release
Updated by Radu Coravu over 7 years ago
I think we need to reopen this issue and refine the fix a little, right now Saxon generates something like this:
<codeblock xmlns:_2="http://www.w3.org/XML/1998/namespace" class="+ topic/pre pr-d/codeblock " _2:space="preserve" xtrf="file:/C:/Users/radu_coravu/Desktop/arka/my_task.dita" xtrc="codeblock:1;8:32">bla</codeblock>
for an XML element which in the XML Schemas specifies xml:space preserve as a default attribute value.
Updated by Radu Coravu over 7 years ago
Attaching again the XML fragment:
<codeblock xmlns:_2="http://www.w3.org/XML/1998/namespace" class="+ topic/pre pr-d/codeblock " _2:space="preserve" xtrf="file:/C:/Users/radu_coravu/Desktop/arka/my_task.dita" xtrc="codeblock:1;8:32">bla</codeblock>
Updated by Radu Coravu over 7 years ago
And this produces invalid XML content:
[move-meta] SXXP0003: Error reported by XML parser: The prefix "xml" cannot be bound to any namespace
Updated by Michael Kay over 7 years ago
- Status changed from Closed to In Progress
Updated by O'Neil Delpratt over 7 years ago
- Fix Committed on Branch trunk added
- Fix Committed on Branch deleted (
9.8)
Updated by Michael Kay over 7 years ago
- Status changed from In Progress to Resolved
- Applies to branch 9.8 added
- Fix Committed on Branch 9.8 added
- Fix Committed on Branch deleted (
trunk)
I have fixed the supplementary issue with default attributes in the XML namespace by changing ComplexContentOutputter.getSubstitutePrefix to always allocate the prefix "xml" if the namespace is the XML namespace.
Patched the 9.7 and 9.8 branches.
Updated by O'Neil Delpratt over 7 years ago
- Fixed in Maintenance Release 9.8.0.3 added
Bug fix relating to comment #15 applied in the Saxon 9.8.0.3 maintenance release. Leaving this marked as resolved until applied in the 9.7 release.
Updated by O'Neil Delpratt over 7 years ago
- Status changed from Resolved to Closed
- Fixed in Maintenance Release 9.7.0.19 added
Further bug fix applied in the 9.7.0.19 maintenance release. See comment #15
Please register to edit this issue