Java 使用 Jackson 自定义 JSON 反序列化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19158345/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom JSON Deserialization with Hymanson
提问by Moritz Petersen
I'm using the Flickr API. When calling the flickr.test.login
method, the default JSON result is:
我正在使用Flickr API。调用该flickr.test.login
方法时,默认的 JSON 结果为:
{
"user": {
"id": "21207597@N07",
"username": {
"_content": "jamalfanaian"
}
},
"stat": "ok"
}
I'd like to parse this response into a Java object:
我想将此响应解析为 Java 对象:
public class FlickrAccount {
private String id;
private String username;
// ... getter & setter ...
}
The JSON properties should be mapped like this:
JSON 属性应该像这样映射:
"user" -> "id" ==> FlickrAccount.id
"user" -> "username" -> "_content" ==> FlickrAccount.username
Unfortunately, I'm not able to find a nice, elegant way to do this using Annotations. My approach so far is, to read the JSON String into a Map<String, Object>
and get the values from there.
不幸的是,我无法使用注释找到一种很好的、优雅的方法来做到这一点。到目前为止,我的方法是将 JSON 字符串读入 aMap<String, Object>
并从那里获取值。
Map<String, Object> value = new ObjectMapper().readValue(response.getStream(),
new TypeReference<HashMap<String, Object>>() {
});
@SuppressWarnings( "unchecked" )
Map<String, Object> user = (Map<String, Object>) value.get("user");
String id = (String) user.get("id");
@SuppressWarnings( "unchecked" )
String username = (String) ((Map<String, Object>) user.get("username")).get("_content");
FlickrAccount account = new FlickrAccount();
account.setId(id);
account.setUsername(username);
But I think, this is the most non-elegant way, ever. Is there any simple way, either using Annotations or a custom Deserializer?
但我认为,这是有史以来最不优雅的方式。有没有简单的方法,要么使用注释,要么使用自定义反序列化器?
This would be very obvious for me, but of course it doesn't work:
这对我来说很明显,但当然它不起作用:
public class FlickrAccount {
@JsonProperty( "user.id" ) private String id;
@JsonProperty( "user.username._content" ) private String username;
// ... getter and setter ...
}
采纳答案by Micha? Ziober
You can write custom deserializer for this class. It could look like this:
您可以为此类编写自定义反序列化器。它可能看起来像这样:
class FlickrAccountJsonDeserializer extends JsonDeserializer<FlickrAccount> {
@Override
public FlickrAccount deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Root root = jp.readValueAs(Root.class);
FlickrAccount account = new FlickrAccount();
if (root != null && root.user != null) {
account.setId(root.user.id);
if (root.user.username != null) {
account.setUsername(root.user.username.content);
}
}
return account;
}
private static class Root {
public User user;
public String stat;
}
private static class User {
public String id;
public UserName username;
}
private static class UserName {
@JsonProperty("_content")
public String content;
}
}
After that, you have to define a deserializer for your class. You can do this as follows:
之后,您必须为您的类定义一个反序列化器。您可以按如下方式执行此操作:
@JsonDeserialize(using = FlickrAccountJsonDeserializer.class)
class FlickrAccount {
...
}
回答by tom
You have to make Username a class within FlickrAccount and give it a _content field
您必须在 FlickrAccount 中将 Username 设为一个类,并为其指定一个 _content 字段
回答by Moritz Petersen
Since I don't want to implement a custom class (Username
) just to map the username, I went with a little bit more elegant, but still quite ugly approach:
由于我不想实现自定义类 ( Username
) 来映射用户名,因此我采用了更优雅但仍然非常丑陋的方法:
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(in);
JsonNode user = node.get("user");
FlickrAccount account = new FlickrAccount();
account.setId(user.get("id").asText());
account.setUsername(user.get("username").get("_content").asText());
It's still not as elegant as I hoped, but at least I got rid of all the ugly casting.
Another advantage of this solution is, that my domain class (FlickrAccount
) is not polluted with any Hymanson annotations.
它仍然没有我希望的那么优雅,但至少我摆脱了所有丑陋的演员阵容。此解决方案的另一个优点是,我的域类 ( FlickrAccount
) 不会被任何 Hymanson 注释污染。
Based on @Micha? Ziober's answer, I decided to use the - in my opinion - most straight forward solution. Using a @JsonDeserialize
annotation with a custom deserializer:
基于@Micha?Ziober 的回答,我决定使用 - 在我看来 - 最直接的解决方案。使用@JsonDeserialize
带有自定义反序列化器的注释:
@JsonDeserialize( using = FlickrAccountDeserializer.class )
public class FlickrAccount {
...
}
But the deserializer does not use any internal classes, just the JsonNode
as above:
但是反序列化器不使用任何内部类,就像JsonNode
上面一样:
class FlickrAccountDeserializer extends JsonDeserializer<FlickrAccount> {
@Override
public FlickrAccount deserialize(JsonParser jp, DeserializationContext ctxt) throws
IOException, JsonProcessingException {
FlickrAccount account = new FlickrAccount();
JsonNode node = jp.readValueAsTree();
JsonNode user = node.get("user");
account.setId(user.get("id").asText());
account.setUsername(user.get("username").get("_content").asText());
return account;
}
}
回答by sendon1982
You can also use SimpleModule.
您还可以使用 SimpleModule。
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override public JsonDeserializer<?> modifyDeserializer(
DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (beanDesc.getBeanClass() == YourClass.class) {
return new YourClassDeserializer(deserializer);
}
return deserializer;
}});
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
objectMapper.readValue(json, classType);