解析JSON时出现Jackson错误“非法字符...仅允许常规白色”

b4lqfgs4  于 2023-04-22  发布在  其他
关注(0)|答案(7)|浏览(556)

我试图从URL中检索JSON数据,但得到以下错误:

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"
            }
        ]
    }
}
6uxekuva

6uxekuva1#

我遇到了同样的问题,我发现它是由Content-Encoding: gzip头引起的。客户端应用程序(抛出异常的地方)无法处理此内容编码。FWIW客户端应用程序正在使用io.github.openfeign:feign-core:9.5.0,并且此库似乎在压缩(link)方面存在一些问题。
您可以尝试将标头Accept-Encoding: identity添加到您的请求中,但是,并非所有Web服务器/Web应用程序都配置正确,有些似乎忽略了此标头。有关如何阻止gzip内容的详细信息,请参阅this question

mitkmikd

mitkmikd2#

我也遇到了类似的问题。经过一些研究,我发现restTemplate使用的SimpleClientHttpRequestFactory不支持gzip编码。要为响应启用gzip编码,您需要为rest模板对象设置一个新的请求工厂-HttpComponentsClientHttpRequestFactory
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

jbose2ul

jbose2ul3#

信息应该是非常不言自明的:
您正在处理的JSON中有一个非法字符(在本例中为字符代码31,即控制代码“Unit Separator”)。
换句话说,你收到的数据不是正确的JSON。
背景:
JSON规范(RFC 7159)说:

  1. JSON语法
    JSON文本是一个标记序列。标记集包括六个结构化字符、字符串、数字和三个字面名称。
    [...]
    在六个结构字符中的任何一个之前或之后都允许使用无意义的空格。
    int x =(
    %x20 / ;空间
    %x09 / ;水平选项卡
    %x0A / ;换行或新行
    %x0D);回车
    换句话说:JSON可以在标记之间包含空白(“标记”意味着JSON的一部分,即列表,字符串等),但“空白”被定义为仅意味着字符空格,制表符,换行符和回车符。
    您的文档包含其他内容(代码31),其中只允许空格,因此不是有效的JSON。
    要解析此内容:
    不幸的是,您正在使用的Jackson库不提供解析这种格式错误的数据的方法。要成功解析,您必须在Jackson处理JSON之前对其进行过滤。
    您可能需要自己从REST服务中检索(伪)JSON,使用标准HTTP,例如java.net.HttpUrlConnection。然后适当地过滤掉“坏”字符,并将结果字符串传递给Jackson。如何做到这一点取决于您如何使用Jackson。
    如果你有问题,请随意问一个单独的问题:-)。
wdebmtf2

wdebmtf24#

我也遇到了同样的问题。在设置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;
    }

}
ryhaxcpt

ryhaxcpt5#

我在我的spring Boot 应用程序中使用zalando logbook时遇到了同样的问题,在仔细阅读了这里的答案后,我意识到,响应拦截器必须在任何需要减压的情况下应用:

@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;
       }
   }
}
jv4diomz

jv4diomz6#

我们最近在我们的集成测试中遇到了同样的问题。我们有一个spring boot应用程序,我们使用wiremock来模拟集成的微服务服务器。对于我们已经实现的一个测试get请求,我们开始得到这个错误。我们不得不将wiremock从2.18.0降级到2.17.0,它工作正常。由于一些bug,jackson parserwiremock的特定版本不能一起工作。我们没有时间来弄清楚这些库中的bug到底是什么。

dgenwo3n

dgenwo3n7#

使用FeignClient的朋友,请参考这个答案spring-feign-not-compressing-response
Spring不能动态地解码响应,所以你需要定义一个自定义的GZip解码器。
为我解决了。

相关问题