簡體   English   中英

使用 Jackson 自定義 JSON 反序列化

[英]Custom JSON Deserialization with Jackson

我正在使用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 字符串讀入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 ...
}

您可以為此類編寫自定義反序列化器。 它可能看起來像這樣:

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 {
    ...
}

由於我不想實現自定義類 ( 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());

它仍然沒有我希望的那么優雅,但至少我擺脫了所有丑陋的演員陣容。 此解決方案的另一個優點是,我的域類 ( FlickrAccount ) 不會被任何 Jackson 注釋污染。

根據@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;
    }
}

您還可以使用 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);

我是這樣做的:

public class FlickrAccount {
  private String id;
  @JsonDeserialize(converter = ContentConverter.class)
  private String username;
  
  private static class ContentConverter extends StdConverter<Map<String, String>, String> {
    @Override
    public String convert(Map<String, String> content) {
      return content.get("_content"));
    }
  }
}

您必須在 FlickrAccount 中將 Username 設為一個類,並為其指定一個 _content 字段

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM