如何使用 Jackson Databind 反序列化以下 JSON?

How do I use Jackson Databind to deserialize the following JSON?

JSON看起来像这样可以有none,一个 更多 个数组中的元素。

{
   "players":[
      {
         "SteamId":"hidden",
         "CommunityBanned":false,
         "VACBanned":false,
         "NumberOfVACBans":0,
         "DaysSinceLastBan":0,
         "NumberOfGameBans":0,
         "EconomyBan":"none"
      },
      {
         "SteamId":"hidden",
         "CommunityBanned":false,
         "VACBanned":false,
         "NumberOfVACBans":0,
         "DaysSinceLastBan":0,
         "NumberOfGameBans":0,
         "EconomyBan":"none"
      }
   ]
}

这是我迄今为止尝试过的方法,但没有成功,我只是捕获了异常。

// I fetch the JSON from a specific API through an HTTP request.
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
          .GET()
          .header("accept", "application/json")
          .uri(URI.create(URL))
          .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// ... and try to deserialize it
ObjectMapper mapper = new ObjectMapper();
PlayersWrapper players = mapper.readValue(response.body(), PlayersWrapper.class); // <-- fails here.
players.getPlayerBans().forEach(System.out::println);

PlayersWrapper.java 经过一些谷歌搜索后,我看到了一些以某种奇特的方式使用包装器 classes 的解决方案,它所用于的 JSON 比我的要复杂一些,尽管有两到三层嵌套。我的比较简单

...

public class PlayersWrapper {
    List<PlayerBan> players = new ArrayList<PlayerBan>();
}

PlayerBan.java

...

public class PlayerBan {
    private String SteamId;
    private boolean CommunityBanned;
    private boolean VACBanned;
    private int NumberOfVACBans;
    private int DaysSinceLastBan;
    private int NumberOfGameBans;
    private String EconomyBan;
}

堆栈跟踪

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "players" (class com.domain.steamfinder.model.PlayersWrapper), not marked as ignorable (one known property: "playerBans"])
 at [Source: (String)"{"players":[{"SteamId":"hidden","CommunityBanned":false,"VACBanned":false,"NumberOfVACBans":0,"DaysSinceLastBan":0,"NumberOfGameBans":0,"EconomyBan":"none"}]}"; line: 1, column: 13] (through reference chain: com.domain.steamfinder.model.PlayersWrapper["players"])
        at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
        at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1127)
        at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1989)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1700)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1678)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:319)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
        at com.domain.steamfinder.finder.FinderService.findUser(FinderService.java:48)
        at com.domain.steamfinder.finder.FinderController.find(FinderController.java:27)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.base/java.lang.Thread.run(Thread.java:829)

编辑: 由于回答指出包装器中的不正确命名 class,原始问题得以解决。出现了一个新问题,请参阅上面的更新代码并查看下面的新堆栈跟踪。

堆栈跟踪

`Unrecognized field "SteamId" (class com.domain.steamfinder.model.PlayerBan), not marked as ignorable 
(7 known properties: "economyBan", "numberOfGameBans", "communityBanned", "steamId", "numberOfVACBans", "daysSinceLastBan", "vacbanned"]) 

... 

(through reference chain: com.domain.steamfinder.model.PlayersWrapper["players"]->java.util.ArrayList[0]->com.domain.steamfinder.model.PlayerBan["SteamId"])`

您可以将变量名称更改为 players 以匹配 JSON,因为这是错误的原因

public class PlayersWrapper {
    List<PlayerBan> players = new ArrayList<PlayerBan>();
}

您的代码存在一些问题,您的异常并不能说明一切,因为 Jackson 在第一个问题处就停止了。

第一个显然是 json 文件有一个名为 players 的数组,但在您的 class 中,您将列表命名为 playerBans。这可以通过多种方式解决:您可以将 List<PlayerBan> playerBans 变量重命名为 players,或者使用 @JsonProperty:

注释变量
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class PlayersWrapper {
    @JsonProperty("players")
    private List<PlayerBan> playerBans;
}

您可能已经注意到 @JsonAutoDetect 注释。这将是一个问题,主要是在您的 PlayerBan class 中,因为所有成员变量都是私有的。您可以通过用 @JsonAutoDetect.

注释您的 class 来告诉 Jackson 识别私有成员

此外,PlayerBan class 将有与 PlayersWrapper class 类似的问题,这意味着某些成员的命名与 json 文件。您也可以在那里使用 @JsonProperty,或者您可以重命名成员变量。

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) 的解释:

我们需要这个的原因是 Jackson 默认情况下不会自动检测 class 的私有成员。自动检测的最小可见性取决于上下文(通常 Jackson 会自动检测 public 成员),但我们可以调整得更宽,这意味着保护和私有字段也会被检测到。有关其他可见性选项,请参阅 JsonAutoDetect.Visibility docs.