カスタムオブジェクトのPOST List
を作成しようとしています。リクエストボディの私のJSONはこれです:
{
"collection": [
{
"name": "Test order1",
"detail": "ahk ks"
},
{
"name": "Test order2",
"detail": "Fisteku"
}
]
}
リクエストを処理するサーバーサイドコード:
import Java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path(value = "/rest/corder")
public class COrderRestService {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response postOrder(Collection<COrder> orders) {
StringBuilder stringBuilder = new StringBuilder();
for (COrder c : orders) {
stringBuilder.append(c.toString());
}
System.out.println(stringBuilder);
return Response.ok(stringBuilder, MediaType.APPLICATION_JSON).build();
}
}
エンティティCOrder
:
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class COrder {
String name;
String detail;
@Override
public String toString() {
return "COrder [name=" + name + ", detail=" + detail
+ ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
+ ", toString()=" + super.toString() + "]";
}
}
しかし、例外がスローされます。
SEVERE: Failed executing POST /rest/corder
org.jboss.resteasy.spi.ReaderException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
at [Source: org.Apache.catalina.connector.CoyoteInputStream@6de8c535; line: 1, column: 1]
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.Java:183)
at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.Java:88)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.Java:111)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.Java:280)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.Java:234)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.Java:221)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.Java:356)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.Java:179)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.Java:220)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.Java:56)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.Java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:728)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:305)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:210)
at org.Apache.Tomcat.websocket.server.WsFilter.doFilter(WsFilter.Java:51)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:243)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:210)
at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:222)
at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:123)
at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:502)
at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:171)
at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:100)
at org.Apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.Java:953)
at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:118)
at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:408)
at org.Apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.Java:1041)
at org.Apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.Java:603)
at org.Apache.Tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.Java:312)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1145)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:615)
at Java.lang.Thread.run(Thread.Java:724)
問題はJSONです。これは、デフォルトでは、実際にはJSON配列ではないため、デフォルトでCollection
に逆シリアル化することはできません。これは次のようになります。
[
{
"name": "Test order1",
"detail": "ahk ks"
},
{
"name": "Test order2",
"detail": "Fisteku"
}
]
あなたはデシリアライゼーションの正確なプロセスを制御していないので(RestEasyはそうします) - 最初のオプションは単にJSONをString
としてインジェクトし、次にデシリアライゼーションプロセスを制御することです:
Collection<COrder> readValues = new ObjectMapper().readValue(
jsonAsString, new TypeReference<Collection<COrder>>() { }
);
あなたはそれを自分でしなくてもよいという便利さを少し失うことになりますが、問題を簡単に解決することができます。
もう1つのオプション - JSONを変更できない場合は、JSON入力の構造に合うようにラッパーを作成し、Collection<COrder>
の代わりにそれを使用します。
お役に立てれば。
JSONドキュメントの代わりに、以下のようにObjectMapperオブジェクトを更新することができます。
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
これはうまくいくでしょう:
単一の要素を持つリストをJsonNodeではなくJsonArrayとして読み込もうとしているとき、またはその逆の場合に問題が発生する可能性があります。その逆。
返されたリストに単一の要素が含まれているかどうかを確実に知ることはできないため、(jsonは次のようになります{...})またはmultiple elements (そしてjsonは次のようになります[{...}、{...}]) - 実行時にチェックインする必要があります要素のタイプ.
これは次のようになります。
(注:このコードサンプルでは、com.fasterxml.jacksonを使用しています)
String jsonStr = response.readEntity(String.class);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonStr);
// Start by checking if this is a list -> the order is important here:
if (rootNode instanceof ArrayNode) {
// Read the json as a list:
myObjClass[] objects = mapper.readValue(rootNode.toString(), myObjClass[].class);
...
} else if (rootNode instanceof JsonNode) {
// Read the json as a single object:
myObjClass object = mapper.readValue(rootNode.toString(), myObjClass.class);
...
} else {
...
}
Eugenの答えに関連して、Collection<COrder>
をメンバ変数として含むラッパーPOJOオブジェクトを作成することによって、この特定のケースを解決できます。これにより、Jacksonは実際のCollection
データをPOJOのメンバ変数内に配置し、APIリクエストで探しているJSONを生成するように正しく指示します。
例:
public class ApiRequest {
@JsonProperty("collection")
private Collection<COrder> collection;
// getters
}
それからCollection<COrder>
の代わりにあなたの新しいApiRequest
ラッパーPOJOになるようにCOrderRestService.postOrder()
のパラメータタイプを設定してください。
同じ問題:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `Java.util.UUID` out of START_OBJECT token
原因は次のとおりです。
ResponseEntity<UUID> response = restTemplate.postForEntity("/example/", null, UUID.class);
私のテストでは、意図的にリクエストをnull(コンテンツPOSTなし)に設定しました。前述のように、リクエストに有効なJSONが含まれていなかったため、OPの原因は同じでした。したがって、サーバーの制限であるapplication/jsonリクエストとして自動的に識別できませんでした(consumes = "application/json"
)。有効なJSONリクエストはそうなります。修正されたのは、エンティティに明示的にnullボディとjsonヘッダーを入力することでした。
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity request = new HttpEntity<>(null, headers);
ResponseEntity<UUID> response = restTemplate.postForEntity("/example/", request, UUID.class);
私はSpringフレームワークを使用して作成されたREST APIでこの問題を抱えていました。 @ResponseBodyアノテーションを追加して(レスポンスJSONにする)解決しました。
Dto response = softConvertValue(jsonData, Dto.class);
public static <T> T softConvertValue(Object fromValue, Class<T> toValueType)
{
ObjectMapper objMapper = new ObjectMapper();
return objMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.convertValue(fromValue, toValueType);
}
通常、JSONノードとJavaオブジェクトのマッピングに問題があると、この問題に直面します。 swaggerではノードはType配列として定義され、JSONオブジェクトは1つの要素しか持たないため、システムは1つの要素リストを配列にマッピングするのが困難だったため、同じ問題に直面しました。
Swaggerでは、要素は次のように定義されていました。
Test:
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/TestNew"
}
あるべきだが
Test:
"$ref": "#/definitions/TestNew"
そしてTestNew
はarray型であるべきです