如何使用 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.
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
.
此外,PlayerBan
class 将有与 PlayersWrapper
class 类似的问题,这意味着某些成员的命名与 json 文件。您也可以在那里使用 @JsonProperty
,或者您可以重命名成员变量。
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
的解释:
我们需要这个的原因是 Jackson 默认情况下不会自动检测 class 的私有成员。自动检测的最小可见性取决于上下文(通常 Jackson 会自动检测 public 成员),但我们可以调整得更宽,这意味着保护和私有字段也会被检测到。有关其他可见性选项,请参阅 JsonAutoDetect.Visibility
docs.