web-dev-qa-db-ja.com

JSON解析時のジャクソンエラー「不正な文字...通常の空白のみ許可」

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=5

ここで何が問題になっていますか?そして、どうすればこのデータを正常に解析できますか?


輸入:

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"
            }
        ]
    }
}
9
rogger2016

メッセージはかなり自明であるはずです:

処理しているJSONに不正な文字(この場合は文字コード31、つまり制御コード「Unit Separator」)があります。

つまり、受信しているデータは適切なJSONではありません。


バックグラウンド:

JSON仕様( RFC 7159 )は次のように述べています。

  1. JSON文法

JSONテキストはトークンのシーケンスです。トークンのセットには、6つの構造文字、文字列、数字、および3つのリテラル名が含まれています。

[...]

意味のない空白は、6つの構造文字の前または後に許可されます。

ws = *(

%x20 /;スペース

%x09 /;水平タブ

%x0A /;改行または改行

%x0D);キャリッジリターン

言い換えると、JSONにはトークンの間に空白(「トークン」はJSONの一部、つまりリスト、文字列など)を含めることができますが、「空白」はスペース、タブ、改行、改行の文字のみを意味するように定義されています。

ドキュメントに空白(コード31)のみが許可されているため、有効なJSONではありません。


これを解析するには:

残念ながら、使用しているJacksonライブラリには、この不正な形式のデータを解析する方法がありません。これを正しく解析するには、Jacksonで処理される前にJSONをフィルタリングする必要があります。

RESTサービスから、たとえば Java.net.HttpUrlConnection を使用して、標準のHTTPを使用して、(疑似-)JSONを自分で取得する必要があります。次に、適切に除外します。 「悪い」文字、および結果の文字列をジャクソンに渡します。これを行う方法は、ジャクソンの使用方法によって異なります。

問題が発生した場合は、個別に質問してください:-)。

5
sleske

これと同じ問題が発生し、Content-Encoding: gzipヘッダー。クライアントアプリケーション(例外がスローされた場所)は、このコンテンツエンコーディングを処理できませんでした。クライアントアプリケーションが使用していたFWIW io.github.openfeign:feign-core:9.5.0、そしてこのライブラリには圧縮に関するいくつかの問題があるようです( link )。

ヘッダーを追加してみてくださいAccept-Encoding: identityただし、リクエストに対して、すべてのWebサーバー/ Webアプリケーションが適切に構成されているわけではなく、一部はこのヘッダーを無視しているようです。 gzip圧縮されたコンテンツを防ぐ方法の詳細については、 この質問 を参照してください。

11

同様の問題がありました。調査の結果、restTemplateはgzipエンコーディングをサポートしないSimpleClientHttpRequestFactoryを使用していることがわかりました。応答のgzipエンコーディングを有効にするには、残りのテンプレートオブジェクトの新しいリクエストファクトリを設定する必要があります-HttpComponentsClientHttpRequestFactory。

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

2
Yash

私も同じ問題を抱えていました。 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;
    }

}
2
Gayan Chinthaka