Skip to content

Commit e22c0e4

Browse files
author
Radek Felcman
authored
Missing namespace when marshalling org.w3c.dom.Element with namespaced attribute - bugfix and unit test (#2459)
Signed-off-by: Radek Felcman <radek.felcman@oracle.com>
1 parent e84fdae commit e22c0e4

File tree

6 files changed

+281
-8
lines changed

6 files changed

+281
-8
lines changed

foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/oxm/record/WriterRecord.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -170,12 +170,36 @@ public void startPrefixMappings(NamespaceResolver namespaceResolver) {
170170
*/
171171
@Override
172172
public void attribute(String namespaceURI, String localName, String qName, String value) {
173-
builder.append(' ');
174-
builder.append(qName);
175-
builder.append('=');
176-
builder.append('\"');
177-
writeValue(value, true, this.builder);
178-
builder.append('\"');
173+
String prefix = null;
174+
boolean prefixFromInput = false;
175+
if (namespaceURI != null && !namespaceURI.isEmpty()
176+
&& !javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)
177+
&& !javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(namespaceURI)) {
178+
if (qName != null && qName.indexOf(Constants.COLON) > 0) {
179+
//resolve prefix from input
180+
prefix = qName.substring(0, qName.indexOf(Constants.COLON));
181+
prefixFromInput = true;
182+
} else {
183+
//resolve prefix from Namespaces resolver
184+
prefix = this.getNamespaceResolver().resolveNamespaceURI(namespaceURI);
185+
}
186+
if (prefix == null) {
187+
//generate new prefix
188+
prefix = this.getNamespaceResolver().generatePrefix();
189+
this.getNamespaceResolver().put(prefix, namespaceURI);
190+
this.namespaceDeclaration(prefix, namespaceURI);
191+
}
192+
}
193+
builder.append(' ');
194+
if (prefix != null && !prefixFromInput) {
195+
builder.append(prefix);
196+
builder.append(':');
197+
}
198+
builder.append(qName);
199+
builder.append('=');
200+
builder.append('\"');
201+
writeValue(value, true, this.builder);
202+
builder.append('\"');
179203
}
180204

181205
/**

moxy/org.eclipse.persistence.moxy/src/test/java/org/eclipse/persistence/testing/jaxb/JAXBTestSuite.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -115,6 +115,7 @@ public static Test suite() {
115115
suite.addTestSuite(org.eclipse.persistence.testing.jaxb.xmlanyelement.ns.qualified.XMLAnyElementNamespaceTestCases.class);
116116
suite.addTestSuite(org.eclipse.persistence.testing.jaxb.xmlanyelement.ns.qualified.DefaultNamespaceTestCases.class);
117117
suite.addTestSuite(org.eclipse.persistence.testing.jaxb.xmlanyelement.ns2.DefaultNamespace2TestCases.class);
118+
suite.addTestSuite(org.eclipse.persistence.testing.jaxb.xmlanyelement.ns3.DefaultNamespace3TestCases.class);
118119
suite.addTestSuite(org.eclipse.persistence.testing.jaxb.xmlelementref.collections.ChoiceCollectionTestCases.class);
119120
suite.addTestSuite(org.eclipse.persistence.testing.jaxb.xmlelementref.collections.ChoiceCollectionNullTestCases.class);
120121
suite.addTestSuite(org.eclipse.persistence.testing.jaxb.xmlelementref.EmployeeCollectionTestCases.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// Oracle - initial API and implementation
15+
package org.eclipse.persistence.testing.jaxb.xmlanyelement.ns3;
16+
17+
import org.eclipse.persistence.jaxb.MarshallerProperties;
18+
import org.eclipse.persistence.oxm.XMLDescriptor;
19+
import org.eclipse.persistence.oxm.XMLRoot;
20+
import org.eclipse.persistence.oxm.record.XMLStreamWriterRecord;
21+
import org.eclipse.persistence.platform.xml.XMLPlatform;
22+
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
23+
import org.eclipse.persistence.testing.jaxb.JAXBTestCases;
24+
import org.w3c.dom.Document;
25+
import org.w3c.dom.Element;
26+
27+
import javax.xml.stream.XMLOutputFactory;
28+
import javax.xml.stream.XMLStreamWriter;
29+
import java.io.StringWriter;
30+
31+
public class DefaultNamespace3TestCases extends JAXBTestCases {
32+
33+
private final static String XML_RESOURCE = "org/eclipse/persistence/testing/jaxb/xmlanyelement/ns/root3.xml";
34+
35+
public DefaultNamespace3TestCases(String name) throws Exception {
36+
super(name);
37+
setControlDocument(XML_RESOURCE);
38+
Class<?>[] classes = new Class<?>[1];
39+
classes[0] = Root.class;
40+
setClasses(classes);
41+
}
42+
43+
@Override
44+
protected Root getControlObject() {
45+
Root root = new Root();
46+
47+
XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
48+
Document document = xmlPlatform.createDocument();
49+
Element element = document.createElementNS("urn:namespace1", "childelem");
50+
element.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns", "urn:namespace1");
51+
element.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:ns1", "urn:attributeNs0");
52+
element.setAttributeNS("urn:attributeNs0", "ns1:attr1", "attr1Value");
53+
root.setChild(element);
54+
55+
return root;
56+
}
57+
58+
/*
59+
@Override
60+
public Root getReadControlObject() {
61+
Root root = new Root();
62+
63+
XMLPlatform xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
64+
Document document = xmlPlatform.createDocument();
65+
Element element = document.createElementNS("urn:namespace1", "childelem");
66+
element.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns", "urn:namespace1");
67+
element.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:ns1", "urn:attributeNs0");
68+
element.setAttributeNS("urn:attributeNs0", "ns1:attr1", "attr1Value");
69+
root.setChild(element);
70+
71+
return root;
72+
}
73+
*/
74+
75+
public void testObjectToXMLStreamWriterRepairing() throws Exception {
76+
if(XML_OUTPUT_FACTORY != null) {
77+
StringWriter writer = new StringWriter();
78+
79+
XMLOutputFactory factory = XMLOutputFactory.newInstance();
80+
factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
81+
XMLStreamWriter streamWriter= factory.createXMLStreamWriter(writer);
82+
83+
Object objectToWrite = getWriteControlObject();
84+
XMLDescriptor desc = null;
85+
if (objectToWrite instanceof XMLRoot) {
86+
desc = (XMLDescriptor)xmlContext.getSession(0).getProject().getDescriptor(((XMLRoot)objectToWrite).getObject().getClass());
87+
} else {
88+
desc = (XMLDescriptor)xmlContext.getSession(0).getProject().getDescriptor(objectToWrite.getClass());
89+
}
90+
jaxbMarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/xml");
91+
92+
int sizeBefore = getNamespaceResolverSize(desc);
93+
try {
94+
jaxbMarshaller.marshal(objectToWrite, streamWriter);
95+
} catch(Exception e) {
96+
assertMarshalException(e);
97+
return;
98+
}
99+
if(expectsMarshalException){
100+
fail("An exception should have occurred but didn't.");
101+
return;
102+
}
103+
104+
streamWriter.flush();
105+
int sizeAfter = getNamespaceResolverSize(desc);
106+
107+
assertEquals(sizeBefore, sizeAfter);
108+
Document testDocument = getTestDocument(writer.toString());
109+
110+
writer.close();
111+
objectToXMLDocumentTest(testDocument);
112+
}
113+
}
114+
115+
public void testObjectToXMLStreamWriterRepairingRecord() throws Exception {
116+
if(XML_OUTPUT_FACTORY != null) {
117+
StringWriter writer = new StringWriter();
118+
119+
XMLOutputFactory factory = XMLOutputFactory.newInstance();
120+
factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
121+
XMLStreamWriter streamWriter= factory.createXMLStreamWriter(writer);
122+
123+
Object objectToWrite = getWriteControlObject();
124+
XMLDescriptor desc = null;
125+
if (objectToWrite instanceof XMLRoot) {
126+
desc = (XMLDescriptor)xmlContext.getSession(0).getProject().getDescriptor(((XMLRoot)objectToWrite).getObject().getClass());
127+
} else {
128+
desc = (XMLDescriptor)xmlContext.getSession(0).getProject().getDescriptor(objectToWrite.getClass());
129+
}
130+
jaxbMarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/xml");
131+
132+
int sizeBefore = getNamespaceResolverSize(desc);
133+
XMLStreamWriterRecord record = new XMLStreamWriterRecord(streamWriter);
134+
try {
135+
((org.eclipse.persistence.jaxb.JAXBMarshaller)jaxbMarshaller).marshal(objectToWrite, record);
136+
} catch(Exception e) {
137+
assertMarshalException(e);
138+
return;
139+
}
140+
if(expectsMarshalException){
141+
fail("An exception should have occurred but didn't.");
142+
return;
143+
}
144+
145+
streamWriter.flush();
146+
int sizeAfter = getNamespaceResolverSize(desc);
147+
148+
assertEquals(sizeBefore, sizeAfter);
149+
150+
Document testDocument = getTestDocument(writer.toString());
151+
writer.close();
152+
objectToXMLDocumentTest(testDocument);
153+
}
154+
}
155+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// Oracle - initial API and implementation
15+
package org.eclipse.persistence.testing.jaxb.xmlanyelement.ns3;
16+
17+
import jakarta.xml.bind.annotation.XmlAnyElement;
18+
import jakarta.xml.bind.annotation.XmlRootElement;
19+
import org.eclipse.persistence.platform.xml.XMLComparer;
20+
import org.w3c.dom.Element;
21+
22+
@XmlRootElement(namespace = "urn:anyElementNs3")
23+
public class Root {
24+
25+
private Element child;
26+
27+
@XmlAnyElement
28+
public Element getChild() {
29+
return child;
30+
}
31+
32+
public void setChild(Element child) {
33+
this.child = child;
34+
}
35+
36+
@Override
37+
public boolean equals(Object obj) {
38+
if(null == obj || obj.getClass() != Root.class) {
39+
return false;
40+
}
41+
XMLComparer comp = new XMLComparer();
42+
return comp.isNodeEqual(child, ((Root)obj).child);
43+
}
44+
45+
@Override
46+
public int hashCode() {
47+
int result = child == null ? 0 : child.getTagName() == null ? 0 : child.getTagName().hashCode();
48+
if (child != null && child.getNamespaceURI() != null) {
49+
result = result * 31 + child.getNamespaceURI().hashCode();
50+
}
51+
return result;
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return "Root{" +
57+
"child=" + (child == null ? "null_child_value" : ("Namespace URI:[" + child.getNamespaceURI() + "]\tTag name[" + child.getTagName() + "]")) +
58+
'}';
59+
}
60+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// Oracle - initial API and implementation
15+
@jakarta.xml.bind.annotation.XmlSchema(
16+
xmlns = {@jakarta.xml.bind.annotation.XmlNs(prefix = "ns0", namespaceURI="urn:anyElementNs3")},
17+
elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED)
18+
package org.eclipse.persistence.testing.jaxb.xmlanyelement.ns3;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!--
2+
3+
Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
4+
5+
This program and the accompanying materials are made available under the
6+
terms of the Eclipse Public License v. 2.0 which is available at
7+
http://www.eclipse.org/legal/epl-2.0,
8+
or the Eclipse Distribution License v. 1.0 which is available at
9+
http://www.eclipse.org/org/documents/edl-v10.php.
10+
11+
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
12+
13+
-->
14+
15+
<ns0:root xmlns:ns0="urn:anyElementNs3"><childelem xmlns="urn:namespace1" xmlns:ns1="urn:attributeNs0" ns1:attr1="attr1Value"/></ns0:root>

0 commit comments

Comments
 (0)