对 Spring 的 JSON 序列化感到困惑
Confused by Spring's JSON serialization
我有一个用 Java 编写的应用程序,带有 Spring 引导和 Spring MVC,它使用包含字节数组的对象响应获取请求:
public HashedPasswordSpec get() {
User user = userRepository.findByEmail("example@example.com");
return user.getHashedPasswordSpec();
}
在客户端,我尝试使用相同的 class:
自动反序列化它
RestTemplate restTemplate = new RestTemplate();
HashedPasswordSpec spec = restTemplate.getForObject("http://localhost:8080/v1/hashed-password-spec", HashedPasswordSpec.class);
失败并出现此错误:
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.read(ReflectiveTypeAdapterFactory.java:129)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220)
... 42 more
salt
就是byte[]
。当我得到一个字符串并将其打印出来时:
String spec = restTemplate.getForObject("http://localhost:8080/v1/hashed-password-spec", String.class);
System.out.println(spec);
我看到了这个:
{"salt":"2GKm9SVxsvLmwaydk8heK/eB94HoPR21+2rTmKMjWo0=","algorithm":"SCrypt","cost":12,"blockSize":8,"parallelization":1,"keyLength":256}
如您所见,salt 被序列化为 Base64,但这不是 Gson 开箱即用的字节数组。 Spring Boot 是否扩展 Gson 以将字节数组序列化为 Base64 字符串?如果是这样,RestTemplate 是否省略了这个扩展?它们彼此不相容吗?我在这里错过了什么?
默认情况下,RestTemplate
and Spring MVC's infrastructure 都会注册各种 serializers/deserializers,具体取决于它们在类路径中找到的内容。
具体来说,他们正在寻找 Jackson 和 Gson 的 JSON 序列化,如果找到的话,他们都更喜欢 Jackson。
在您的情况下,您的服务器将 byte[]
序列化为 Base64 字符串这一事实表明它正在使用 Jackson,因为这是它的默认策略(参见 ByteArraySerializer
)。
而且我们可以从您的错误日志中看到 RestTemplate
无法用 Gson
解析 JSON。 Gson
不使用,也不期望 byte[]
类型的字段使用 Base64 字符串,它只期望包含数字的 JSON 数组。
因此,您的客户端没有将 Jackson 注册为序列化程序,因为它不在类路径中,或者因为您已经注册了 HttpMessageConverter
个不包含 Jackson 变体的实例的自定义列表。
我有一个用 Java 编写的应用程序,带有 Spring 引导和 Spring MVC,它使用包含字节数组的对象响应获取请求:
public HashedPasswordSpec get() {
User user = userRepository.findByEmail("example@example.com");
return user.getHashedPasswordSpec();
}
在客户端,我尝试使用相同的 class:
自动反序列化它RestTemplate restTemplate = new RestTemplate();
HashedPasswordSpec spec = restTemplate.getForObject("http://localhost:8080/v1/hashed-password-spec", HashedPasswordSpec.class);
失败并出现此错误:
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.read(ReflectiveTypeAdapterFactory.java:129)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220)
... 42 more
salt
就是byte[]
。当我得到一个字符串并将其打印出来时:
String spec = restTemplate.getForObject("http://localhost:8080/v1/hashed-password-spec", String.class);
System.out.println(spec);
我看到了这个:
{"salt":"2GKm9SVxsvLmwaydk8heK/eB94HoPR21+2rTmKMjWo0=","algorithm":"SCrypt","cost":12,"blockSize":8,"parallelization":1,"keyLength":256}
如您所见,salt 被序列化为 Base64,但这不是 Gson 开箱即用的字节数组。 Spring Boot 是否扩展 Gson 以将字节数组序列化为 Base64 字符串?如果是这样,RestTemplate 是否省略了这个扩展?它们彼此不相容吗?我在这里错过了什么?
默认情况下,RestTemplate
and Spring MVC's infrastructure 都会注册各种 serializers/deserializers,具体取决于它们在类路径中找到的内容。
具体来说,他们正在寻找 Jackson 和 Gson 的 JSON 序列化,如果找到的话,他们都更喜欢 Jackson。
在您的情况下,您的服务器将 byte[]
序列化为 Base64 字符串这一事实表明它正在使用 Jackson,因为这是它的默认策略(参见 ByteArraySerializer
)。
而且我们可以从您的错误日志中看到 RestTemplate
无法用 Gson
解析 JSON。 Gson
不使用,也不期望 byte[]
类型的字段使用 Base64 字符串,它只期望包含数字的 JSON 数组。
因此,您的客户端没有将 Jackson 注册为序列化程序,因为它不在类路径中,或者因为您已经注册了 HttpMessageConverter
个不包含 Jackson 变体的实例的自定义列表。