使用 Jackson 的自定义 JSON 反序列化

2022-09-01 00:03:27

我正在使用Flickr API。调用 flickr.test.login 方法时,默认 JSON 结果为:

{
    "user": {
        "id": "21207597@N07",
        "username": {
            "_content": "jamalfanaian"
        }
    },
    "stat": "ok"
}

我想将此响应解析为Java对象:

public class FlickrAccount {
    private String id;
    private String username;
    // ... getter & setter ...
}

JSON 属性应按如下方式映射:

"user" -> "id" ==> FlickrAccount.id
"user" -> "username" -> "_content" ==> FlickrAccount.username

不幸的是,我无法找到一种很好,优雅的方法来使用注释来做到这一点。到目前为止,我的方法是将JSON字符串读入a并从中获取值。Map<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);

但我认为,这是有史以来最不优雅的方式。有没有简单的方法,使用注释或自定义反序列化程序?

这对我来说非常明显,但当然它不起作用:

public class FlickrAccount {
    @JsonProperty( "user.id" ) private String id;
    @JsonProperty( "user.username._content" ) private String username;
    // ... getter and setter ...
}

答案 1

您可以为此类编写自定义反序列化程序。它可能看起来像这样:

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;
    }
}

之后,您必须为类定义反序列化程序。您可以按如下方式执行此操作:

@JsonDeserialize(using = FlickrAccountJsonDeserializer.class)
class FlickrAccount {
    ...
}

答案 2

由于我不想实现自定义类()只是为了映射用户名,所以我选择了一种更优雅但仍然非常丑陋的方法: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());

它仍然不像我希望的那么优雅,但至少我摆脱了所有丑陋的选角。此解决方案的另一个优点是,我的域类 () 不会受到任何 Jackson 注释的污染。FlickrAccount

根据@Micha ł Ziober的回答,我决定使用 - 在我看来 - 最直接的解决方案。将批注与自定义反序列化程序一起使用:@JsonDeserialize

@JsonDeserialize( using = FlickrAccountDeserializer.class )
public class FlickrAccount {
    ...
}

但是反序列化程序不使用任何内部类,如上所述: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;
    }
}