If you look at the answer you linked to, you will see that a StreamResult
is used. In the answer, a StringWriter
is passed to the constructor, but if you look at the Javadoc, it has an overloaded constructor that also takes an OutputStream
. So if you're returning a StreamingOutput
, just pass the OutputStream
from the StreamingOutput#write(OutputStream)
method to the StreamResult
constructor. Everything else from the answer should be the same.
return new StreamingOutput() {
@Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(out);
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Here's the complete resource class I used to test. Notice that I use @Produces(MediaType.APPLICATION_XML)
. There's no point in setting to application/octet-stream
if the data is XML1.
@Path("dom")
public class DomXmlResource {
@GET
@Produces(MediaType.APPLICATION_XML)
public StreamingOutput getXml() throws Exception {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement("company");
doc.appendChild(rootElement);
Element staff = doc.createElement("Staff");
rootElement.appendChild(staff);
staff.setAttribute("id", "1");
Element firstname = doc.createElement("firstname");
firstname.appendChild(doc.createTextNode("yong"));
staff.appendChild(firstname);
return new StreamingOutput() {
@Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
try {
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(out);
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
}
Update
To automatically download the file (as opposed to displaying the XML result), we actually need to add the Content-Disposition
header with the attachment
value. To do that, instead of returning StreamingOutput
from the method, we should return Response
, where the entity will be the StreamingOutput
@Path("dom")
public class DomXmlResource {
@GET
@Produces(MediaType.APPLICATION_XML)
public Response getXml() throws Exception {
...
StreamingOutput entity = new StreamingOutput() {
@Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
...
}
};
return Response.ok(entity)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment;filename=file.xml")
.build();
}
}
Update 2
If you don't already know, you can simply return your POJOs (or list of them) and they will automatically get serialized to XML. You don't need to manually use the DOM classes to create XML structure. There are already Entity Providers that will handle the conversion from POJO to XML for us. For example, if we have the following POJO (needs to be annotated with @XmlRootElement
)
@XmlRootElement
public class User {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
then we can just return it and it will automatically be serialized to
<user><name>footer</name></user>
Here's an example
@Path("pojo")
public class PojoXmlResource {
@GET
@Produces("application/xml")
public Response getXml() {
User user = new User();
user.setName("Jane Doe");
return Response.ok(user)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment;filename=user.xml")
.build();
}
}
It's a lot less messy isn't it? If you want to return a list of Users, then you need to wrap it in GenericEntity
List<User> users = Arrays.asList(user1, user2, user3);
GenericEntity<List<User>> entity = new GenericEntity<List<User>>(users){};
return Response.ok(entity)
...
.build();
1. See: Do I need Content-Type: application/octet-stream for file download?