I'm upgrading an application from SpringBoot2.6 to SprintBoot2.7 (2.7.4). It relies on Azure SDK for Java, which has a dependency over jackson-xml. This is discussed in https://github.com/Azure/azure-sdk-for-java/issues/7694, and one solution to workaround this is to rely on a spring.properties file holding spring.xml.ignore=true.

While this covers many cases, I have unit-tests which are still failing as XML output is produced. It appears JacksonHttpMessageConvertersConfiguration.MappingJackson2XmlHttpMessageConverterConfiguration has no exclusion relating to spring.xml.ignore=true, hence MappingJackson2XmlHttpMessageConverter is being injected into HttpMessageConverters.

There is HttpMessageConverters.reorderXmlConvertersToEnd which may help, but it covers only defaultConverters, not the input converters as provided by HttpMessageConvertersAutoConfiguration

Why JacksonHttpMessageConvertersConfiguration.MappingJackson2XmlHttpMessageConverterConfiguration isn't excluded by spring.xml.ignore=true?

Comment From: bclozel

Those configuration flags are not officially supported and might be removed in spring-projects/spring-framework#28726.

Comment From: blacelle

@bclozel Thanks for the info. ~How is this related with #28726?~

Comment From: bclozel

Sorry I've edited my comment. The issue id was for a Framework issue.

Comment From: blacelle

Reading through lines, I suppose this would be deleted with Spring6, hence SpringBoot3. Doesn't this feel relevant to consider for SpringBoot2?

Comment From: bclozel

I don't think it would be wise to introduce a behavior change in Spring Boot 2.7 based on an undocumented property, to remove it right after without any deprecation notice.

We could try to solve the problem differently. It seems the Jackson xml dependency is brought by the azure one, but it's not clear how the extra message converter is breaking things for your application nor why the other solutions didn't work. Could you share a bit more about this?

Comment From: blacelle

For some reason, an XML converter is first in some chain of converters, and it leads to XML being outputted by request not specifying an Accept header.

Comment From: blacelle

why the other solutions didn't work

It is unclear to me what you refer to here. Excluding the dependency is an issue as some Azure cases require XML management. I can add explicitly an Accept header, but it is against my purpose of managing requests not expressing this header.

Something quite strange to me is that other unit-tests (which a priori looks similar) behave properly with spring.xml.ignore=true is set (hence the lack of homogeneity jumped to my face, hence this ticket).

Comment From: bclozel

Now I understand the core problem: your web API is meant to handle only a specific media type (JSON?) and some incoming requests do not have an Accept header. In this case, any other message converter could also be an issue.

I think that relying on the absence of a message converter to get the right output is not good practice and it would be better to annotate your controllers to signal what media types can be produced.

If anything, spring.xml.ignore=true is a distraction and should not be considered.

Comment From: blacelle

I think that relying on the absence of a message converter to get the right output is not good practice and it would be better to annotate your controllers to signal what media types can be produced.

I'm quite astonished SpringBoot position is one has to explicit on each Controller its mediaType. I may have an alternative version of my app preferring to produce XML as a preference over JSON.

If spring.xml.ignore=true is not the way to go, fine. But I would have expected a way to disable MappingJackson2XmlHttpMessageConverterConfiguration. By the way, why are these classes not public ? It make it more painful to refer to them (e.g. through other not-SpringBoot AutoConfigurations).

Comment From: bclozel

I'm quite astonished SpringBoot position is one has to explicit on each Controller its mediaType. I may have an alternative version of my app preferring to produce XML as a preference over JSON.

There are numerous ways to express opinions and customize things in Spring Boot applications. We're doing our best to produce the best possible result given the opinions expressed by the application. In this case, this is really a combination of: * the application having the Jackson XML dependency * the client not expressing any preference * the controller not expressing any preference

My comment was merely pointing to a possible solution, but it was clearly lacking the full context of your application and its constraints.

I'm actually surprised you're getting this behavior as Spring Boot's HttpMessageConverters should already reorder XML converters at the back of the list.

Here's what I'm getting with a sample application:

With the expected xml dependency

    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'

the following controller:

@RestController
public class JsonXmlController {

    @GetMapping("/book")
    public Book book() {
        return new Book("Intro to Spring Boot", "Spring team");
    }

    public record Book(String title, String author) {

    }

}

I'm getting the following behavior:

http localhost:8080/book Accept:text/xml -v
GET /book HTTP/1.1
Accept: text/xml
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/3.2.1



HTTP/1.1 200
Connection: keep-alive
Content-Type: text/xml;charset=UTF-8
Date: Wed, 05 Oct 2022 19:11:07 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked

<Book>
  <title>Intro to Spring Boot</title>
  <author>Spring team</author>
</Book>
http localhost:8080/book -v
GET /book HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/3.2.1



HTTP/1.1 200
Connection: keep-alive
Content-Type: application/json
Date: Wed, 05 Oct 2022 19:14:27 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked

{
    "author": "Spring team",
    "title": "Intro to Spring Boot"
}

There 's probably something missing here. Could you provide a minimal, sample application that reproduces the problem? I'm reopening this issue until we figure things out.

Comment From: blacelle

Here is a reproduction case:

@RestController
public class JsonXmlController {

    @GetMapping("/book")
    public Map<?, ?> book() {
        return Map.of("Intro to Spring Boot", "Spring team");
    }

}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = JsonXmlController.class)
@ContextConfiguration(classes = { JsonXmlController.class
// , DisableJacksonXmlAutoConfiguration.class
})
@WithMockUser
public class TestJacksonXmlController {

    final JsonXmlController controller = new JsonXmlController();

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testJsonAsDefault() throws Exception {
        String expected = "{'Intro to Spring Boot', 'Spring team'}";

        mockMvc.perform(MockMvcRequestBuilders.get("/book"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().json(expected.replace('\'', '"')));
    }
}

It fails with:

org.json.JSONException: Unparsable JSON string: <Map><Intro to Spring Boot>Spring team</Intro to Spring Boot></Map>
    at org.skyscreamer.jsonassert.JSONParser.parseJSON(JSONParser.java:56)
    at org.skyscreamer.jsonassert.JSONCompare.compareJSON(JSONCompare.java:50)
    at org.skyscreamer.jsonassert.JSONCompare.compareJSON(JSONCompare.java:125)
    at org.skyscreamer.jsonassert.JSONAssert.assertEquals(JSONAssert.java:415)
    at org.skyscreamer.jsonassert.JSONAssert.assertEquals(JSONAssert.java:394)
    at org.skyscreamer.jsonassert.JSONAssert.assertEquals(JSONAssert.java:336)
    at org.springframework.test.util.JsonExpectationsHelper.assertJsonEqual(JsonExpectationsHelper.java:61)
    at org.springframework.test.web.servlet.result.ContentResultMatchers.lambda$json$9(ContentResultMatchers.java:227)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:214)
    at io.mitrust.i18n.TestJacksonXmlController.testUnknownLanguage(TestJacksonXmlController.java:34)

For the record, as a (functional but not exciting) workaround, we rely on:

// https://github.com/spring-projects/spring-boot/issues/32494
@AutoConfigureBefore(name = "org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration")
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "com.fasterxml.jackson.dataformat.xml.XmlMapper")
// @ConditionalOnBean(Jackson2ObjectMapperBuilder.class)
public class DisableJacksonXmlAutoConfiguration {
    private static final Logger LOGGER = LoggerFactory.getLogger(DisableJacksonXmlAutoConfiguration.class);

    // This force disabling MappingJackson2XmlHttpMessageConverterConfiguration
    @Bean
    // @ConditionalOnMissingBean
    public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter(
            Jackson2ObjectMapperBuilder builder) {
        if (SpringProperties.getFlag("spring.xml.ignore")) {
            LOGGER.info("We force disabling {}", MappingJackson2XmlHttpMessageConverter.class);
            return new MappingJackson2XmlHttpMessageConverter() {
                @Override
                protected boolean canWrite(MediaType mediaType) {
                    return false;
                }
            };
        }

        // To fallback on default behavior
        return null;
    }
}

Comment From: NotFound403

I have solved this problem temporarily in a way that I do not approve of

@Configuration(proxyBeanMethods = false)
public class SpringMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(converter ->
                MappingJackson2XmlHttpMessageConverter.class.isAssignableFrom(converter.getClass()));
    }
}

XML format is not the default config,unintentional operations should not trigger this high priority config. If some developers need this they should do it proactively.

Comment From: wilkinsona

@blacelle That test passes for me once I'd updated expected to be {'Intro to Spring Boot':'Spring team'} (a : rather than a , between the two strings). There must be something else going on in your case that you haven't shared, such as auto-configuration of Spring MVC being disabled. Can you please provide a complete yet minimal sample that reproduces the problem? That sample should be something that we can run without any copy-paste or modifications and reproduce the problem.

Comment From: blacelle

!

It turned to be quite difficult to reproduce. Until I spotted this issue (XML while expecting JSON) is triggered by explicit spring.xml.ignore=XYZ in a spring.properties file.

if no spring.properties (or property is commented): OK (json). if spring.properties with spring.xml.ignore=false: KO (xml). if spring.properties with spring.xml.ignore=true: KO (xml).

it is reproduced by https://github.com/solven-eu/spring-boot-issues-32494/blob/master/src/test/java/issue/TestJacksonXmlController.java

Hence, the use of spring.xml.ignore=true fixed some issues (e.g. it works OK with RestTemplate configuration), but popped another issue (KO with MVC).

Comment From: bclozel

If I understand correctly, removing spring.xml.ignore solves the issue on the server side at least. This option was never meant to be officially supported in the first place and has been removed in spring-projects/spring-framework#29277.

It sounds like there is a remaining issue on the client side but we've never discussed this. Could this be a bug? Do you have a minimal sample to share? Note that to get the codecs preferences from Spring Boot you would need to create a RestTemplate instance from a RestTemplateBuilder bean.

Leaving this opened still to ensure that the problem is solved.

Comment From: blacelle

I provided a repo reproducing the server issue. It is unclear to me what other reproduction scenario would help.

Le mer. 12 oct. 2022, 09:20, Brian Clozel @.***> a écrit :

If I understand correctly, removing spring.xml.ignore solves the issue on the server side at least. This option was never meant to be officially supported in the first place and has been removed in spring-projects/spring-framework#29277 https://github.com/spring-projects/spring-framework/issues/29277.

It sounds like there is a remaining issue on the client side but we've never discussed this. Could this be a bug? Do you have a minimal sample to share? Note that to get the codecs preferences from Spring Boot you would need to create a RestTemplate instance from a RestTemplateBuilder bean.

Leaving this opened still to ensure that the problem is solved.

— Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-boot/issues/32494#issuecomment-1275603716, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQFCF5XFG3M5UGAEU2A3XDWCZDC5ANCNFSM6AAAAAAQVAFTMQ . You are receiving this because you were mentioned.Message ID: @.***>

Comment From: bclozel

Hence, the use of spring.xml.ignore=true fixed some issues (e.g. it works OK with RestTemplate configuration), but popped another issue (KO with MVC).

To me this reads like "if I don't use spring.xml.ignore" everything works on the server side but not on the client side.

Again, the spring.xml.ignore property should not be used at all. Hence I'm moving to the next issue: what's not working with the client?

Comment From: blacelle

Again, the spring.xml.ignore property should not be used at all.

Why isn't it deprecated then (i.e. clearly flagging any use as deprecated in latest 5.X) ? Or generating a warn?

Hence I'm moving to the next issue: what's not working with the client?

Ok, got it. I'm so confused by spring.xml.ignore=false generating unexpected behavior, that I did not understand I have to start by dropping this option alltogether (hence the request for a stronger deprecation notice (e.g. a WARN ?)).

Note that to get the codecs preferences from Spring Boot you would need to create a RestTemplate instance from a RestTemplateBuilder bean.

This seems to be one of our issue in the client-side. I migrated from manual RestTemplate instantiation to RestTemplateBuilder use. We are left with some issues:

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.shouldIgnoreXml

it leads to issues with some MockVc based tests, as described in https://github.com/solven-eu/spring-boot-issues-32494/blob/master/src/test/java/issue/TestMiTrustExceptionMvcControllerAdvice.java

Comment From: snicoll

Why isn't it deprecated then

Why would we deprecate something that's internal, not meant to be used externally and not documented. Let's stop this discussion please. Brian also shared previously that "This option was never meant to be officially supported in the first place".

Comment From: mentallurg

@blacelle: It is not quite correct to expect particular content type, if you don't specify Accept header.

I may have an alternative version of my app preferring to produce XML as a preference over JSON.

Using any global variable for this is not a good approach. Will your app return XML, if the client sends Accept: application/json?

I'd suggest you to change your test and to include Accept header. Only then you will have predictable behaviour.

Comment From: a19930905a

How we deal with "Jackson-dataformat-xml" prioritized over then Json format issue in RestTemplate default MessageConverters? I know we can do some override and change HttpMessageConverterList Seq if we extend WebMvcConfigurationSupport but how about external library using it? Like spring-security-oauth2.DefaultOAuth2ExceptionRenderer.messageConverters?

Comment From: wilkinsona

There doesn't seem to be anything left in this issue that's actionable. Closing for now. We can re-open of course if I have overlooked something.