69

I'm having problems posting JSON with UTF-8 encoding using RestTemplate. Default encoding for JSON is UTF-8 so the media type shouldn't even contain the charset. I have tried to put charset in the MediaType but it doesn't seem to work anyway.

My code:

String dataJson = "{\"food\": \"smörrebröd\"}";
HttpHeaders headers = new HttpHeaders();
MediaType mediaType = new MediaType("application", "json", StandardCharsets.UTF_8);
headers.setContentType(mediaType);

HttpEntity<String> entity = new HttpEntity<String>(dataJson, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Boolean> formEntity = restTemplate.exchange(postUrl, HttpMethod.POST, entity, Boolean.class);
anssias
  • 1,994
  • 2
  • 16
  • 21

10 Answers10

166

You need to add StringHttpMessageConverter to rest template's message converter with charset UTF-8. Like this

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters()
        .add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
Ortomala Lokni
  • 56,620
  • 24
  • 188
  • 240
mushfek0001
  • 3,845
  • 1
  • 21
  • 20
  • 3
    Worked like a charm. I had an issue with a special character which look's like '(single quotes). Had a nightmare in parsing it. Simple tweak worth a 1000 votes – RahulArackal Oct 31 '16 at 13:39
  • @mushfek0001 it didn't help i changed as mentioned below. I was able to change charset – Sandesh Jan 26 '17 at 20:08
  • How can I switch between charset 8 and 16 – Pasupathi Rajamanickam May 31 '18 at 15:09
  • 4
    Why `.add(0, ...` and not just `.add(new StringHttp...`? – Charles Wood Jan 23 '19 at 22:53
  • @mushfek0001 Update Charset.forName("UTF-8") to StandardCharsets.UTF_8 – AndersonBS May 20 '19 at 12:28
  • You should use StandardCharsets.UTF_8 here rather than a String representation i.e. .getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8)); – paulscott56 Oct 07 '19 at 11:49
  • simple and easy to use – horoyoi o Feb 26 '20 at 05:56
  • why this isn't default? It took me half an hour to check char by char to notice bad encoding :( – rado Apr 22 '21 at 04:53
  • For me the `StandardCharsets.UTF_8` also works in most cases when returning Strings. But I had a problem today when my ResponseEntity returned an png-image in the body. This could only be displayed when I set it to the (default) `StringHttpMessageConverter(StandardCharsets.ISO_8859_1)`. And there was no error, just the image _could not be displayed_ – leole Aug 05 '21 at 06:56
  • Thanks a lot man!! I was stuck since 2 days. This helped +1 – da-vinci-code Mar 10 '22 at 15:05
33

(Adding to solutions by mushfek0001 and zhouji)

By default RestTemplate has ISO-8859-1 StringHttpMessageConverter which is used to convert a JAVA object to request payload.

Difference between UTF-8 and ISO-8859:

UTF-8 is a multibyte encoding that can represent any Unicode character. ISO 8859-1 is a single-byte encoding that can represent the first 256 Unicode characters. Both encode ASCII exactly the same way.

Solution 1 (copied from mushfek001):

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters()
        .add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));

Solution 2:

Though above solution works, but as zhouji pointed out, it will add two string converters and it may lead to some regression issues.

If you set the right content type in http header, then ISO 8859 will take care of changing the UTF characters.

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

or

// HttpUriRequest request
request.addHeader("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
Community
  • 1
  • 1
Rohit
  • 6,365
  • 14
  • 59
  • 90
  • Solution 2 is not an option for non-standard content types, like the one used for bulk operation in ElasticSearch REST API (application/x-ndjson). – Krzysztof Tomaszewski Oct 05 '18 at 13:07
  • solution 1 works, but solution 2 `headers.setContentType(MediaType.APPLICATION_JSON_UTF8);` not work, sprint boot version 2.1.2, jdk 1.8 – Joey Jan 17 '19 at 08:12
  • Solution 2 is working simply with headers.setContentType(MediaType.APPLICATION_JSON_UTF8); It is simple and elegant solution. Nice explanation too. – UM1979 Jul 11 '19 at 09:54
  • 2
    MediaType.APPLICATION_JSON_UTF8_VALUE is deprecated now – FishingIsLife Mar 11 '20 at 13:08
  • can we do the same with @Autowired RestTemplate? – USM Apr 06 '20 at 13:21
  • @USM Yes you can, but you should update it in the Spring configuration where it is defined, rather than modifying it in your application code while processing a request. – nbrooks Apr 23 '20 at 03:41
  • solution 2 is best solution. – bebosh Dec 08 '20 at 07:52
10

The answer by @mushfek0001 produce two StringHttpMessageConverter which will send two text/plain and */*, such as the debug picture.

enter image description here

Even the Accept header of client will be:

text/plain, text/plain, */*, */*

So the better one is remove the ISO-8859-1 StringHttpMessageConverter and use single UTF-8 StringHttpMessageConverter.

Use this:

RestTemplate restTemplate = new RestTemplate();
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
stringHttpMessageConverter.setWriteAcceptCharset(true);
for (int i = 0; i < restTemplate.getMessageConverters().size(); i++) {
    if (restTemplate.getMessageConverters().get(i) instanceof StringHttpMessageConverter) {
        restTemplate.getMessageConverters().remove(i);
        restTemplate.getMessageConverters().add(i, stringHttpMessageConverter);
        break;
    }
}
zhouji
  • 5,056
  • 1
  • 26
  • 26
3

Adding new StringHttpMessageConverter won't help. In existing StringHttpMessageConverter we need to set writeAcceptCharset to false and build httpheader with charset we want.

RestTemplate restTemplate = new RestTemplate();
   List<HttpMessageConverter<?>> c = restTemplate.getMessageConverters();
        for(HttpMessageConverter<?> mc :c){
            if (mc instanceof StringHttpMessageConverter) {
                StringHttpMessageConverter mcc = (StringHttpMessageConverter) mc;
                mcc.setWriteAcceptCharset(false);
            }
   }

  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
  headers.setAcceptCharset(Arrays.asList(Charset.forName("UTF-8")));
  HttpEntity<String> entity = new HttpEntity<String>(jsonPayload, headers);

  restTemplate.postForEntity(postResourceUrl, entity, String.class);
Sandesh
  • 91
  • 2
2

Removing the existing converter, but with Java 8 (why do people still write Java 7 Code anyways?)

List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof StringHttpMessageConverter);
converters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
Michel Jung
  • 2,966
  • 6
  • 31
  • 51
1
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_16LE));

String response = restTemplate.getForObject(url, String.class);
Ran Adler
  • 3,587
  • 30
  • 27
  • Can you provide some context to your answer, that way future readers can learn how to apply it to their issues and not just in this situation. – Newd Jul 28 '15 at 14:28
1

Better you should remove the StringHttpMessageConverter first before adding new. so this way you will have one instance of StringHttpMessageConverter in our MessageConverters list.

RestTemplate restTemplate = new RestTemplate();
        final Iterator<HttpMessageConverter<?>> iterator = restTemplate.getMessageConverters().iterator();
        while (iterator.hasNext()) {
            final HttpMessageConverter<?> converter = iterator.next();
            if (converter instanceof StringHttpMessageConverter) {
                iterator.remove();
            }
        }


        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
pankaj desai
  • 93
  • 1
  • 8
0

Try this

restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(Charsets.UTF_8));
aqiao
  • 11
  • 2
0

If you're using Spring Boot it will auto-configure RestTemplate with a high-priority UTF-8 charset StringHttpMessageConverter, if you use RestTemplateBuilder to create the RestTemplate instance. E.g. restTemplateBuilder.build()

Andy Birchall
  • 311
  • 1
  • 3
  • 12
0

You can also do

HttpHeaders attachmentHeader = new HttpHeaders();
attachmentHeader.setContentType(MediaType.valueOf("text/csv; charset=UTF-8"));

Or

 attachmentHeader.set("Content-Type", "text/csv; charset=UTF-8");
Hohenheimsenberg
  • 935
  • 10
  • 25