Jerseyで構築され、AppEngineにデプロイされたRESTサービスがあります。 RESTサービスは、アプリケーション/ jsonメディアタイプを消費する動詞PUTを実装します。データバインディングはジャクソンによって実行されます。
この動詞は、JSONで表されるエンタープライズ部門の関係を消費します。
{"name":"myEnterprise", "departments":["HR","IT","SC"]}
クライアント側では、gsonを使用してJSON表現をJavaオブジェクトに変換します。次に、オブジェクトをRESTサービスに渡しますが、正常に機能します。
問題:
JSON表現にコレクション内のアイテムが1つしかない場合
{"name":"myEnterprise", "departments":["HR"]}
サービスはオブジェクトをデシリアライズできません。
ATTENTION: /enterprise/enterprise: org.codehaus.jackson.map.JsonMappingException:
Can not deserialize instance of Java.util.ArrayList out of VALUE_STRING token at
[Source: org.mortbay.jetty.HttpParser$Input@5a9c5842; line: 1, column: 2
他のユーザーから報告されているように、解決策はフラグACCEPT_SINGLE_VALUE_AS_ARRAYを追加することです(例: Jersey:StringからArrayListのインスタンスをデシリアライズできません )。それでも、サービス側ではJacksonによって透過的に作成されるため、ObjectMapperを制御していません。
質問:
ACCEPT_SINGLE_VALUE_AS_ARRAYを有効にするために、サービス側でObjectMapperを構成する方法はありますか?注釈? web.xml?
コードの詳細
Javaオブジェクト:
@XmlRootElement
public class Enterprise {
private String name;
private List<String> departments;
public Enterprise() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getDepartments() {
return departments;
}
public void setDepartments(List<String> departments) {
this.departments = departments;
}
}
RESTサービス側:
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("/enterprise")
public Response putEnterprise(Enterprise enterprise,
@Context HttpServletRequest req){
...
}
クライアント側:
...
String jsonString = "{\"name\":\"myEnterprise\", \"departments\":[\"HR\"]}";
Enterprise enterprise = gson.fromJson(jsonString, Enterprise.class);
System.out.println(gson.toJson(enterprise));
response = webResource
.type(MediaType.APPLICATION_JSON)
.put(ClientResponse.class,enterprise);
if (response.getStatus() >= 400) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
...
これは私の古い質問の解決策です:
DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY機能を有効にするために、独自のContextResolverを実装しました。
package org.lig.hadas.services.mapper;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
@Produces(MediaType.APPLICATION_JSON)
@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper>
{
ObjectMapper mapper;
public ObjectMapperProvider(){
mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
@Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
そして、web.xmlで、パッケージをサーブレット定義に登録しました...
<servlet>
<servlet-name>...</servlet-name>
<servlet-class>com.Sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.Sun.jersey.config.property.packages</param-name>
<param-value>...;org.lig.hadas.services.mapper</param-value>
</init-param>
...
</servlet>
...残りはすべてジャージ/ジャクソンによって透過的に行われます。
この属性をObjectMapperインスタンスに設定すると機能しますが、
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
やってみますか
[{"name":"myEnterprise", "departments":["HR"]}]
角括弧が重要なポイントです。
エラーメッセージを検索してこの質問を見つけた人は、List
タイプのプロパティにaという名前を付けて@JsonProperty
アノテーションを間違えた場合にもこのエラーを見ることができます。単一値フィールド:
@JsonProperty("someSingleValuedField") // Oops, should have been "someMultiValuedField"
public List<String> getMyField() { // deserialization fails - single value into List
return myField;
}
ジャクソン2.7.x以降では、メンバー変数自体に注釈を付ける方法があります。
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<String> newsletters;
詳細はこちら: Jackson @JsonFormat