Ошибка Джексона. Недопустимый символ. При анализе JSON допускается только обычный пробел.

Я пытаюсь получить данные JSON из URL-адреса, но получаю следующую ошибку:

Illegal character ((CTRL-CHAR, code 31)):
only regular white space (\r, \n,\t) is allowed between tokens

Мой код:

final URI uri = new URIBuilder(UrlConstants.SEARCH_URL)
      .addParameter("keywords", searchTerm)
      .addParameter("count", "50")
      .build();
  node = new ObjectMapper().readTree(new URL(uri.toString())); <<<<< THROWS THE ERROR

Созданный URL-адрес, т.е. https://www.example.org/api/search.json?keywords=iphone&count=50

Что здесь происходит не так? И как я могу успешно разобрать эти данные?


Импорт:

import com.google.appengine.repackaged.org.codehaus.jackson.JsonNode;
import com.google.appengine.repackaged.org.codehaus.jackson.map.ObjectMapper;
import com.google.appengine.repackaged.org.codehaus.jackson.node.ArrayNode;
import org.apache.http.client.utils.URIBuilder;

пример ответа

{
    meta: {
        indexAllowed: false
    },
    products: {
        products: [ 
            {
                id: 1,
                name: "Apple iPhone 6 16GB 4G LTE GSM Factory Unlocked"
            },
            {
                id: 2,
                name: "Apple iPhone 7 8GB 4G LTE GSM Factory Unlocked"
            }
        ]
    }
}

person rogger2016    schedule 06.03.2017    source источник
comment
не могли бы вы добавить JSON, возвращаемый URL-адресом, для анализа?   -  person user1121883    schedule 06.03.2017
comment
Просто примечание: пожалуйста, всегда используйте example.org или example.com, например, доменные имена. Если вы создаете домен, вы можете создать проблемы тому, кто им владеет. См. example.com в Википедии.   -  person sleske    schedule 06.03.2017
comment
добавлен пример ответа   -  person rogger2016    schedule 06.03.2017
comment
@ rogger2016: Проблема, вероятно, в невидимом символе в ответе. Не могли бы вы добавить шестнадцатеричный дамп ответа? Например, в Linux или с помощью cygwin в Windows запустите: curl http://example.org/my-rest-url |hexdump -C .   -  person sleske    schedule 06.03.2017
comment
Привет @sleske, я свернул URL-адрес и получил ответ ... затем я запускаю JSON через JSLint и получаю «Действительный JSON»   -  person rogger2016    schedule 06.03.2017
comment
@rogger2016: Ну, какое это имеет значение? JSLint предназначен для проверки JavsScript, а не для JSON. Два разных (хотя они могут выглядеть похожими). В любом случае, некоторые инструменты проверки могут быть более терпимыми, чем Джексон, поэтому даже если средство проверки находит JSON в порядке, это может не иметь значения. Итак, пожалуйста, опубликуйте шестнадцатеричный дамп, как я написал, иначе мы никуда не денемся...   -  person sleske    schedule 06.03.2017


Ответы (6)


Сообщение должно быть довольно понятным:

В обрабатываемом JSON содержится недопустимый символ (в данном случае код символа 31, т. е. управляющий код «Разделитель единиц измерения»).

Другими словами, данные, которые вы получаете, не соответствуют JSON.


Задний план:

Спецификация JSON (RFC 7159) гласит:

  1. JSON-грамматика

Текст JSON представляет собой последовательность токенов. Набор токенов включает шесть структурных символов, строк, чисел и трех буквенных имен.

[...]

Незначительные пробелы разрешены до или после любого из шести структурных символов.

ws = *(

%x20 / ; Космос

%x09 / ; Горизонтальная вкладка

%x0А / ; Перевод строки или Новая строка

%x0D) ; Возврат каретки

Другими словами: JSON может содержать пробелы между токенами («токены» означают часть JSON, т. е. списки, строки и т. д.), но «пробел» определяется только как символ пробела, табуляции, перевода строки и возврата каретки. .

Ваш документ содержит что-то еще (код 31), где разрешены только пробелы, поэтому JSON недействителен.


Чтобы разобрать это:

К сожалению, используемая вами библиотека Джексона не предлагает способа анализа этих искаженных данных. Чтобы успешно проанализировать это, вам нужно будет отфильтровать JSON, прежде чем он будет обработан Джексоном.

Вам, вероятно, придется самостоятельно извлекать (псевдо-)JSON из службы REST, используя стандартный HTTP, используя, например. java.net.HttpUrlConnection. Затем соответствующим образом отфильтруйте «плохие» символы и передайте полученную строку Джексону. Как именно это сделать, зависит от того, как вы используете Джексона.

Не стесняйтесь задавать отдельные вопросы, если у вас возникли проблемы :-).

person sleske    schedule 06.03.2017
comment
Спасибо за ответ... Я не контролирую JSON, есть ли способ обойти его... Когда я нажимаю URL-адрес в браузере, я получаю ответ, так есть ли способ сделать ObjectMapper менее строгим??? - person rogger2016; 06.03.2017
comment
@rogger2016: Это другой вопрос :-). Я постараюсь расширить свой ответ. - person sleske; 06.03.2017
comment
stackoverflow.com/questions/42658481/ @sleske - person rogger2016; 08.03.2017
comment
@ rogger2016: я уже добавил помощь в свой ответ выше. Если у вас по-прежнему возникают проблемы, задайте более конкретный вопрос, описав, что вы пробовали и где не получилось. - person sleske; 08.03.2017

У меня возникла такая же проблема, и я обнаружил, что она была вызвана заголовком Content-Encoding: gzip. Клиентское приложение (где выбрасывалось исключение) не смогло обработать это кодирование содержимого. FWIW клиентское приложение использовало io.github.openfeign:feign-core:9.5.0, и у этой библиотеки, похоже, есть некоторые проблемы со сжатием (ссылка).

Вы можете попробовать добавить заголовок Accept-Encoding: identity к своему запросу, однако не все веб-серверы/веб-приложения настроены правильно, а некоторые, похоже, игнорируют этот заголовок. См. этот вопрос для подробнее о том, как предотвратить сжатие содержимого.

person ChocolateAndCheese    schedule 31.01.2018
comment
Большое спасибо. Вы действительно спасатель жизни. - person omer khalid; 30.01.2019
comment
Спасибо за ответ, вы спасли меня от часов отладки! - person Igor Bljahhin; 28.09.2020
comment
Те, кто сталкивается с этой проблемой после обновления версии spring-cloud-openfeign-core до 2.2.5.RELEASE, могут обратиться к stackoverflow.com/questions/63608735/ - person Prasanth Rajendran; 12.11.2020
comment
Спасибо! Это решило проблему для меня! - person Matsu Q.; 28.01.2021

У меня была аналогичная проблема. После некоторых исследований я обнаружил, что restTemplate использует SimpleClientHttpRequestFactory, который не поддерживает кодировку gzip. Чтобы включить кодировку gzip для вашего ответа, вам нужно будет установить новую фабрику запросов для остального объекта шаблона — HttpComponentsClientHttpRequestFactory.

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

person Yash    schedule 10.06.2019
comment
Это работает и для меня. - person Mingtao Sun; 13.01.2021

У меня такая же проблема. После установки Gzip это было исправлено. Пожалуйста, обратитесь к моему коду

public String sendPostRequest(String req) throws Exception {

    // Create connection
    URL urlObject = new URL(mURL);
    HttpURLConnection connection = (HttpURLConnection) urlObject.openConnection();
    connection.setRequestMethod("POST");
    connection.setRequestProperty("Content-Type", "application/json");
    connection.setRequestProperty("Content-Length", Integer.toString(req.getBytes().length));
    connection.setRequestProperty("Content-Language", "en-US");
    connection.setUseCaches(false);
    connection.setDoOutput(true);

    // Send request
    DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
    wr.writeBytes(req);
    wr.close();

    //Response handling
    InputStream responseBody                = null;
    if (isGzipResponse(connection)) {
        responseBody                = new GZIPInputStream(connection.getInputStream());         
    }else{
        responseBody = connection.getInputStream();
    }
    convertStreamToString(responseBody);

    return response.toString();

}

protected boolean isGzipResponse(HttpURLConnection con) {
    String encodingHeader = con.getHeaderField("Content-Encoding");
    return (encodingHeader != null && encodingHeader.toLowerCase().indexOf("gzip") != -1);
}

public void convertStreamToString(InputStream in) throws Exception {
    if (in != null) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int length = 0;
        while ((length = in.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }

        response = new String(baos.toByteArray());

        baos.close();

    } else {
        response = null;
    }

}
person Gayan Chinthaka    schedule 07.06.2019

Недавно у нас была такая же проблема в интеграционных тестах. У нас есть приложение spring boot, и мы используем wiremock для имитации интегрированного сервера микросервисов. Для одного из тестовых запросов get, которые мы реализовали, мы начали получать эту ошибку. Нам пришлось понизить версию wiremock с 2.18.0 до 2.17.0, и все заработало. Из-за какой-то ошибки jackson parser и конкретная версия wiremock не работали вместе. У нас не было времени выяснить, в чем на самом деле ошибка в этих библиотеках.

person humbleCoder    schedule 14.09.2020

У меня была такая же проблема с журналом регистрации zalando в моем весеннем загрузочном приложении, и, внимательно прочитав ответы здесь, я понял, что перехватчик ответов должен применяться после всего, что заботится о распаковке:

@Configuration
public class RestTemplateConfig {

   [....]

   @Bean
   public RestTemplate restTemplate() {
       return new RestTemplateBuilder()
               .requestFactory(new MyRequestFactorySupplier())
               .build();
   }

   class MyRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {
       @Override
       public ClientHttpRequestFactory get() {
           CloseableHttpClient client = HttpClientBuilder.create()
                   .addInterceptorFirst(logbookHttpRequestInterceptor)
        // wrong:  .addInterceptorFirst(logbookHttpResponseInterceptor)
                   .addInterceptorLast(logbookHttpResponseInterceptor)
                   .build();
           HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = 
                  new HttpComponentsClientHttpRequestFactory(client);
           return clientHttpRequestFactory;
       }
   }
}
person user2081279    schedule 05.01.2021