diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/support/SaajXmlReader.java b/core/src/main/java/org/springframework/ws/soap/saaj/support/SaajXmlReader.java
new file mode 100644
index 00000000..9d384ac0
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/soap/saaj/support/SaajXmlReader.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.saaj.support;
+
+import java.util.Iterator;
+import javax.xml.soap.Name;
+import javax.xml.soap.Node;
+import javax.xml.soap.SOAPElement;
+import javax.xml.soap.Text;
+
+import org.springframework.xml.sax.AbstractXmlReader;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * SAX XMLReader that reads from a SAAJ Node. Consumes XMLEvents from an
+ * XMLEventReader, and calls the corresponding methods on the SAX callback interfaces.
+ *
+ * @author Arjen Poutsma
+ * @see javax.xml.soap.Node
+ * @see javax.xml.soap.SOAPElement
+ */
+public class SaajXmlReader extends AbstractXmlReader {
+
+ private final Node startNode;
+
+ /**
+ * Constructs a new instance of the SaajXmlReader that reads from the given Node.
+ *
+ * @param startNode the SAAJ Node to read from
+ */
+ public SaajXmlReader(Node startNode) {
+ this.startNode = startNode;
+ }
+
+ /**
+ * Parses the StAX XML reader passed at construction-time.
+ *
InputSource is not read, but ignored.
+ *
+ * @param ignored is ignored
+ * @throws org.xml.sax.SAXException A SAX exception, possibly wrapping a XMLStreamException
+ */
+ public final void parse(InputSource ignored) throws SAXException {
+ parse();
+ }
+
+ /**
+ * Parses the StAX XML reader passed at construction-time.
+ *
+ * Note that the given system identifier is not read, but ignored.
+ *
+ * @param ignored is ignored
+ * @throws SAXException A SAX exception, possibly wrapping a XMLStreamException
+ */
+ public final void parse(String ignored) throws SAXException {
+ parse();
+ }
+
+ private void parse() throws SAXException {
+ if (getContentHandler() != null) {
+ getContentHandler().startDocument();
+ }
+ handleNode(startNode);
+ if (getContentHandler() != null) {
+ getContentHandler().endDocument();
+ }
+ }
+
+ private void handleNode(Node node) throws SAXException {
+ if (node instanceof SOAPElement) {
+ handleElement((SOAPElement) node);
+ }
+ else if (node instanceof Text) {
+ Text text = (Text) node;
+ handleText(text);
+ }
+ }
+
+ private void handleText(Text text) throws SAXException {
+ if (!text.isComment() && getContentHandler() != null) {
+ char[] ch = text.getValue().toCharArray();
+ getContentHandler().characters(ch, 0, ch.length);
+ }
+ }
+
+ private void handleElement(SOAPElement element) throws SAXException {
+ Name elementName = element.getElementName();
+ if (getContentHandler() != null) {
+ for (Iterator iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
+ String prefix = (String) iterator.next();
+ String namespaceUri = element.getNamespaceURI(prefix);
+ getContentHandler().startPrefixMapping(prefix, namespaceUri);
+ }
+ getContentHandler()
+ .startElement(elementName.getURI(), elementName.getLocalName(), elementName.getQualifiedName(),
+ getAttributes(element));
+ }
+ for (Iterator iterator = element.getChildElements(); iterator.hasNext();) {
+ Node child = (Node) iterator.next();
+ handleNode(child);
+ }
+ if (getContentHandler() != null) {
+ getContentHandler()
+ .endElement(elementName.getURI(), elementName.getLocalName(), elementName.getQualifiedName());
+ for (Iterator iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
+ String prefix = (String) iterator.next();
+ getContentHandler().endPrefixMapping(prefix);
+ }
+ }
+ }
+
+ private Attributes getAttributes(SOAPElement element) {
+ AttributesImpl attributes = new AttributesImpl();
+ for (Iterator iterator = element.getAllAttributes(); iterator.hasNext();) {
+ Name attributeName = (Name) iterator.next();
+ String attributeValue = element.getAttributeValue(attributeName);
+ attributes.addAttribute(attributeName.getURI(), attributeName.getLocalName(),
+ attributeName.getQualifiedName(), "CDATA", attributeValue);
+ }
+ return attributes;
+ }
+
+
+}
diff --git a/core/src/test/java/org/springframework/ws/soap/saaj/support/SaajXmlReaderTest.java b/core/src/test/java/org/springframework/ws/soap/saaj/support/SaajXmlReaderTest.java
new file mode 100644
index 00000000..201ab9d4
--- /dev/null
+++ b/core/src/test/java/org/springframework/ws/soap/saaj/support/SaajXmlReaderTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.saaj.support;
+
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.SOAPMessage;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.springframework.xml.transform.StringResult;
+import org.xml.sax.InputSource;
+
+public class SaajXmlReaderTest extends XMLTestCase {
+
+ private SaajXmlReader reader;
+
+ private SOAPMessage message;
+
+ private Transformer transformer;
+
+ protected void setUp() throws Exception {
+ MessageFactory messageFactory = MessageFactory.newInstance();
+ message = messageFactory.createMessage();
+ reader = new SaajXmlReader(message.getSOAPPart().getEnvelope());
+ transformer = TransformerFactory.newInstance().newTransformer();
+ }
+
+ public void testIt() throws Exception {
+ Result result = new StringResult();
+ Source source = new SAXSource(reader, new InputSource());
+ transformer.transform(source, result);
+ Result expected = new StringResult();
+ transformer.transform(new DOMSource(message.getSOAPPart().getEnvelope()), expected);
+ assertXMLEqual(expected.toString(), result.toString());
+ }
+}
\ No newline at end of file