2017-09-15 1 views
0

J'ai une demande écrite en Java avec Spring Boot et Spring MVC qui répond à une requête get avec un objet qui contient un tableau d'octets:Confus par JSON Spring sérialisation

public HashedPasswordSpec get() { 
    User user = userRepository.findByEmail("[email protected]"); 
    return user.getHashedPasswordSpec(); 
} 

sur le côté client je tente de désérialiser automatiquement en utilisant la même classe:

RestTemplate restTemplate = new RestTemplate(); 
HashedPasswordSpec spec = restTemplate.getForObject("http://localhost:8080/v1/hashed-password-spec", HashedPasswordSpec.class); 

qui échoue avec cette erreur:

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 10 path $.salt 
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224) 
    at com.google.gson.Gson.fromJson(Gson.java:887) 
    at com.google.gson.Gson.fromJson(Gson.java:852) 
    at org.springframework.http.converter.json.GsonHttpMessageConverter.readTypeToken(GsonHttpMessageConverter.java:161) 
    ... 39 more 
Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 10 path $.salt 
    at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350) 
    at com.google.gson.internal.bind.ArrayTypeAdapter.read(ArrayTypeAdapter.java:70) 
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:129) 
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220) 
    ... 42 more 

salt est le byte[]. Quand je hériterez d'une chaîne et l'imprimer:

String spec = restTemplate.getForObject("http://localhost:8080/v1/hashed-password-spec", String.class); 
System.out.println(spec); 

Je vois ceci:

{"salt":"2GKm9SVxsvLmwaydk8heK/eB94HoPR21+2rTmKMjWo0=","algorithm":"SCrypt","cost":12,"blockSize":8,"parallelization":1,"keyLength":256} 

Comme vous pouvez le voir, le sel est sérialisé comme base64, mais ce n'est pas ce que Gson fait hors de la boîte avec des tableaux d'octets. Est-ce que Spring Boot étend Gson pour sérialiser les tableaux d'octets en tant que chaînes Base64? Si oui, RestTemplate omet-il cette extension? Sont-ils incompatibles les uns avec les autres? Qu'est-ce que j'oublie ici?

+1

Spring enregistre les sérialiseurs/désérialiseurs par défaut en fonction de ce qui se trouve dans le chemin de classe. Est-ce que le classpath de votre serveur contient Jackson? Votre classpath client contient-il uniquement Gson? –

+0

@SotiriosDelimanolis: à la fois le serveur et le client contiennent Gson dans le classpath, mais pas directement, à travers une autre dépendance. Je ne suis pas sûr de Jackson. – Pablo

+0

Jackson, par défaut, sérialise 'byte []' en base64, iirc. Gson ne le fait pas. Donc, votre serveur a probablement Jackson sur son classpath (qui est plus élevé dans son ordre d'enregistrement pour les sérialiseurs que Gson). –

Répondre

1

Les deux RestTemplate et Spring MVC's infrastructure enregistrent, par défaut, divers sérialiseurs/désérialiseurs en fonction de ce qu'ils trouvent sur le chemin de classe. En particulier, ils recherchent Jackson et Gson pour sa sérialisation JSON, les deux préférant Jackson si elle est trouvée. Dans votre cas, le fait que votre serveur sérialise le byte[] à une chaîne Base64 indique qu'il utilise Jackson puisque c'est sa stratégie par défaut (voir ByteArraySerializer).

Et nous pouvons voir à partir de votre journal des erreurs que RestTemplate ne parvient pas à analyser le JSON avec Gson. Gson n'utilise pas et n'attend pas de chaînes Base64 pour un champ de type byte[], il attend simplement un tableau JSON contenant des nombres. Votre client n'enregistre donc pas Jackson en tant que sérialiseur, soit parce qu'il n'est pas sur le chemin de classe, soit parce que vous avez enregistré une liste personnalisée d'instances HttpMessageConverter qui ne contiennent pas la variante Jackson.

+0

'Jackson2' a un ordre supérieur à' gson'. Alors, quelle est la solution pour contourner ce problème? Qu'est-ce que vous avez à faire pour 'gson' à enregistrer au lieu de' Jackson2'? Retirer Jackson2? Thx –

+1

@MinhKieu Oui, vous pouvez supprimer Jackson du classpath ou vous pouvez manuellement décider et enregistrer les instances 'HttpMessageConverter' que vous voulez. –