【问题标题】:How do I use Jackson Databind to deserialize the following JSON?如何使用 Jackson Databind 反序列化以下 JSON?
【发布时间】:2022-01-08 18:27:04
【问题描述】:

JSON 看起来像这样,并且可以在数组中包含 noneonemore 元素。

{
   "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 经过一番谷歌搜索后,我看到了一些以某种奇特方式使用包装类的解决方案,它用于的 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)

编辑:原始问题得到解决,这要归功于指出包装类中的错误命名的答案。出现了一个新问题,请参阅上面的更新代码并在下面查看新的堆栈跟踪。

堆栈跟踪

`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"])`

【问题讨论】:

    标签: java json jackson jackson-databind


    【解决方案1】:

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

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

    【讨论】:

    • 这有效,我不知道命名不是任意的。
    【解决方案2】:

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

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

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

    您可能已经注意到@JsonAutoDetect 注释。这将是一个问题,主要是在您的PlayerBan 类中,因为所有成员变量都是私有的。您可以通过使用 @JsonAutoDetect 注释您的班级来告诉 Jackson 识别私人成员。

    此外,PlayerBan 类会出现与PlayersWrapper 类类似的问题,这意味着某些成员的命名与 json 文件中出现的名称不同。你也可以在那里使用@JsonProperty,或者你可以重命名成员变量。

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

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

    【讨论】:

    • 我正在使用 Lombok 为包装类和 PlayerBan 类自动生成 setter 和 getter。查看更新后的帖子。
    • 正如我所说,用 @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) 注释 PlayerBan
    • 那行得通。 playerBan 对象已成功创建,字段中的数据正确。您介意解释该注释的作用吗?是为了告诉 Jackson 使用提供的 setter/getter 来识别私有成员吗?
    • 更新了我的答案。你不需要 setter,Jackson 会使用反射来设置私有成员。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-12
    相关资源
    最近更新 更多