1

I'd like to find how to influence how Spring automatically marshals Java objects to XML when sending a POST request via RestTemplate. Particularly, how to configure what is in the XML headers (encoding, DOCTYPE, ...).

There are plenty of questions closely touching the topic (Include DOCTYPE for Spring Jaxb2Marshaller, How to add DOCTYPE and xml processing instructions when marshalling with JAXB?, how to add DOCTYPE in jaxb marshaller, How to declare doctype ,xml version and encoding in XML file using DOM parser in java?) but none of them seems to help here.

enter image description here

I am aware that I may marshal the object to XML string first and then send the XML string. However, I'd like to use the automatic marshaling as it seems more elegant and proper.

I have a class like

@XmlRootElement(name = "MyRequest")
public class MyRequest {
    @XmlAttribute(required = true)
    String field1;
    @XmlAttribute(required = true)
    String field2;
    ...
}

The code sending the HTTP POST request is like:

final MyRequest requestBody = new MyRequest("VALUE1", "VALUE2");

final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML, MediaType.ALL));
final HttpEntity<MyRequest> requestHttpEntity = new HttpEntity<>(requestBody, headers);

return restTemplate.postForEntity(url, requestHttpEntity, MyResponse.class);

When I intercept what is sent, it is something like this:

POST /webservice HTTP/1.1
Accept: application/xml, */*
Content-Type: application/xml
Host: example.com:8080
Content-Length: ...

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MyRequest field1="VALUE1" field2="VALUE2">
</MyRequest>

and what I want to receive is

POST ...

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE MyRequest SYSTEM "MyRequest.dtd">
<MyRequest field1="VALUE1" field2="VALUE2">
</MyRequest>

Question: How can I customize the marshaling without completely avoiding the automagic behavior of Spring RestTemplate? I want to change the encoding, remove the standalone attribute (where does it come from?) and add the <!DOCTYPE> element.

Honza Zidek
  • 9,204
  • 4
  • 72
  • 118

1 Answers1

4

You can replace the XML converter that RestTemplate uses with a customized one:

RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
for (int i = 0; i != converters.size(); i++) {
    if (converters.get(i) instanceof Jaxb2RootElementHttpMessageConverter) {
        Jaxb2RootElementHttpMessageConverter xmlConverter = new Jaxb2RootElementHttpMessageConverter(){
            @Override
            protected void customizeMarshaller(Marshaller marshaller) {
                marshaller.setProperty( "com.sun.xml.internal.bind.xmlHeaders", "<!DOCTYPE MyRequest SYSTEM \"MyRequest.dtd\">");
                // add other customizations
            }
        };
        converters.set(i, xmlConverter);
        break;
    }
}

Wrap the whole thing in a method annotated with @Bean @Qualified, and use that to autowire the RestTemplate wherever you need it, if you have many places to inject it to

Alex Savitsky
  • 2,306
  • 5
  • 24
  • 30
  • @HonzaZidek I'm not aware of a better way to get a hold on the XML converter that the `RestTemplate` uses - this is the way we're been using in our projects (we've customized the JSON converter, though). `RestTemplate` only exposes the list of `HttpMessageConverter`s, but has no means to get a hold of any specific one, as far as I know. BTW, just for the record, using `instanceof` doesn't really count as "reflection" – Alex Savitsky Dec 17 '17 at 21:46
  • @HonzaZidek As for your other question, you can customize the same `Jaxb2RootElementHttpMessageConverter` instance to also enable the DTD support. Again, I wish I has a nicer-looking way to get a hold of the converter, but this is all Spring is exposing to us. Maybe newer Spring version have added something for this use case? – Alex Savitsky Dec 17 '17 at 21:49
  • See also the accepted answer here https://stackoverflow.com/q/47854054/2886891 (by @M.Deinum), it is also applicable. – Honza Zidek Feb 21 '20 at 06:54