Skip to content

Commit 1c4f810

Browse files
committed
Use XML Transformer for xml serialization to fix missing default namespace when serializing xml
1 parent 71385d1 commit 1c4f810

File tree

2 files changed

+44
-16
lines changed

2 files changed

+44
-16
lines changed

src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,53 @@
11
package com.fasterxml.jackson.databind.ext;
22

3-
import java.io.IOException;
4-
5-
import org.w3c.dom.Node;
6-
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
7-
import org.w3c.dom.ls.DOMImplementationLS;
8-
import org.w3c.dom.ls.LSSerializer;
9-
10-
import com.fasterxml.jackson.core.*;
3+
import com.fasterxml.jackson.core.JsonGenerator;
114
import com.fasterxml.jackson.databind.JavaType;
125
import com.fasterxml.jackson.databind.JsonMappingException;
136
import com.fasterxml.jackson.databind.JsonNode;
147
import com.fasterxml.jackson.databind.SerializerProvider;
158
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
169
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
10+
import org.w3c.dom.Node;
1711

12+
import javax.xml.XMLConstants;
13+
import javax.xml.transform.*;
14+
import javax.xml.transform.dom.DOMSource;
15+
import javax.xml.transform.stream.StreamResult;
16+
import java.io.IOException;
17+
import java.io.StringWriter;
1818
@SuppressWarnings("serial")
1919
public class DOMSerializer extends StdSerializer<Node>
2020
{
21-
protected final DOMImplementationLS _domImpl;
21+
22+
private final TransformerFactory transformerFactory;
2223

2324
public DOMSerializer() {
2425
super(Node.class);
25-
DOMImplementationRegistry registry;
2626
try {
27-
registry = DOMImplementationRegistry.newInstance();
27+
transformerFactory = TransformerFactory.newInstance();
28+
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
2829
} catch (Exception e) {
29-
throw new IllegalStateException("Could not instantiate DOMImplementationRegistry: "+e.getMessage(), e);
30+
throw new IllegalStateException("Could not instantiate TransformerFactory: "+e.getMessage(), e);
3031
}
31-
_domImpl = (DOMImplementationLS)registry.getDOMImplementation("LS");
3232
}
3333

3434
@Override
3535
public void serialize(Node value, JsonGenerator jgen, SerializerProvider provider)
3636
throws IOException
3737
{
38-
if (_domImpl == null) throw new IllegalStateException("Could not find DOM LS");
39-
LSSerializer writer = _domImpl.createLSSerializer();
40-
jgen.writeString(writer.writeToString(value));
38+
try {
39+
Transformer transformer = transformerFactory.newTransformer();
40+
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
41+
transformer.setOutputProperty(OutputKeys.INDENT, "no");
42+
StreamResult result = new StreamResult(new StringWriter());
43+
DOMSource source = new DOMSource(value);
44+
transformer.transform(source, result);
45+
jgen.writeString(result.getWriter().toString());
46+
} catch (TransformerConfigurationException e) {
47+
throw new IllegalStateException("Could not create XML Transformer: "+e.getMessage(), e);
48+
} catch (TransformerException e) {
49+
provider.reportMappingProblem(e,"XML Transformation failed: %s", e.getMessage());
50+
}
4151
}
4252

4353
@Override

src/test/java/com/fasterxml/jackson/databind/ext/DOMTypeReadWriteTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class DOMTypeReadWriteTest extends com.fasterxml.jackson.databind.BaseMap
1414
"<root attr='3'><leaf>Rock &amp; Roll!</leaf><?proc instr?></root>";
1515
final static String SIMPLE_XML_NS =
1616
"<root ns:attr='abc' xmlns:ns='http://foo' />";
17+
final static String SIMPLE_XML_DEFAULT_NS =
18+
"<root xmlns='http://foo'/>";
1719

1820
private final ObjectMapper MAPPER = new ObjectMapper();
1921

@@ -33,6 +35,22 @@ public void testSerializeSimpleNonNS() throws Exception
3335
assertEquals(SIMPLE_XML, normalizeOutput(output));
3436
}
3537

38+
public void testSerializeSimpleDefaultNS() throws Exception
39+
{
40+
// Let's just parse first, easiest
41+
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse
42+
(new InputSource(new StringReader(SIMPLE_XML_DEFAULT_NS)));
43+
assertNotNull(doc);
44+
// need to strip xml declaration, if any
45+
String outputRaw = MAPPER.writeValueAsString(doc);
46+
// And re-parse as String, since JSON has quotes...
47+
String output = MAPPER.readValue(outputRaw, String.class);
48+
/* ... and finally, normalize to (close to) canonical XML
49+
* output (single vs double quotes, xml declaration etc)
50+
*/
51+
assertEquals(SIMPLE_XML_DEFAULT_NS, normalizeOutput(output));
52+
}
53+
3654
public void testDeserializeNonNS() throws Exception
3755
{
3856
for (int i = 0; i < 2; ++i) {

0 commit comments

Comments
 (0)