web-dev-qa-db-fra.com

Erreur de Jackson "Caractère illégal ... seuls les espaces blancs réguliers sont autorisés" lors de l'analyse JSON

J'essaie de récupérer des données JSON à partir d'une URL mais j'obtiens l'erreur suivante:

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

Mon code:

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

L'URL construite est par exemple https://www.example.org/api/search.json?keywords=iphone&count=5

Qu'est-ce qui ne va pas ici? Et comment puis-je analyser ces données avec succès?


Importations:

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;

exemple de réponse

{
    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

Le message devrait être assez explicite:

Il y a un caractère illégal (dans ce cas le code de caractère 31, c'est-à-dire le code de contrôle "Unit Separator") dans le JSON que vous traitez.

En d'autres termes, les données que vous recevez ne sont pas du bon JSON.


Contexte:

La spécification JSON ( RFC 7159 ) dit:

  1. Grammaire JSON

Un texte JSON est une séquence de jetons. L'ensemble de jetons comprend six caractères structurels, des chaînes, des nombres et trois noms littéraux.

[...]

Un espace insignifiant est autorisé avant ou après l'un des six caractères structurels.

ws = * (

% x20 /; Espace

% x09 /; Onglet horizontal

% x0A /; Saut de ligne ou Nouvelle ligne

% x0D); Retour chariot

En d'autres termes: JSON peut contenir des espaces entre les jetons ("tokens" signifiant la partie du JSON, c'est-à-dire des listes, des chaînes, etc.), mais "espaces" est défini pour ne signifier que les caractères Espace, Tabulation, Saut de ligne et Retour chariot .

Votre document contient quelque chose d'autre (code 31) où seuls les espaces blancs sont autorisés, donc n'est pas un JSON valide.


Pour analyser ceci:

Malheureusement, la bibliothèque Jackson que vous utilisez n'offre pas de moyen d'analyser ces données mal formées. Pour analyser cela avec succès, vous devrez filtrer le JSON avant qu'il ne soit géré par Jackson.

Vous devrez probablement récupérer vous-même le (pseudo-) JSON à partir du service REST, en utilisant HTTP standard en utilisant, par exemple Java.net.HttpUrlConnection . Ensuite, filtrez de manière appropriée "mauvais" caractères et passez la chaîne résultante à Jackson. La façon de procéder dépend exactement de la façon dont vous utilisez Jackson.

N'hésitez pas à poser des questions distinctes si vous rencontrez des problèmes :-).

5
sleske

J'ai eu ce même problème, et j'ai trouvé qu'il était dû au Content-Encoding: gzip entête. L'application cliente (où l'exception était levée) n'a pas pu gérer cet encodage de contenu. FWIW, l'application cliente utilisait io.github.openfeign:feign-core:9.5.0, et cette bibliothèque semble avoir des problèmes de compression ( link ).

Vous pourriez essayer d'ajouter l'en-tête Accept-Encoding: identity à votre demande, cependant, tous les serveurs Web/applications Web ne sont pas configurés correctement, et certains semblent ignorer cet en-tête. Voir cette question pour plus de détails sur la façon d'empêcher le contenu compressé.

11
ChocolateAndCheese

J'ai eu un problème similaire. Après quelques recherches, j'ai trouvé que restTemplate utilise SimpleClientHttpRequestFactory qui ne prend pas en charge le codage gzip. Pour activer le codage gzip pour votre réponse, vous devrez définir une nouvelle fabrique de demandes pour l'objet de modèle de repos - HttpComponentsClientHttpRequestFactory.

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

2
Yash

J'ai eu le même problème. Après avoir configuré Gzip, il a été corrigé. Veuillez me référer mon code

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