Как сопоставить / сгруппировать свойства JSON по префиксу в Список объектов с Джексоном? - PullRequest
0 голосов
/ 13 февраля 2019

У меня есть ответ JSON:

{
  "part1.id": "1",  
  "part1.name": "Name1",                 
  "part2.id": "2",                
  "part2.name": "Name2"
}

Класс POJO / DTO для 1 пользователя:

public class User {

  private String id;
  private String name;

  // set/get
}

И класс для всего ответа:

public class UsersResponse {

  private List<User> users;

  // set/get
}

Я могу получить значения в Map, а затем проанализировать / отобразить в коде вручную, например, Ключ карты Джексона JSON как свойство содержащегося объекта

Также есть @ JsonAlias ​​ для множественного именованияварианты, но он сопоставляется с одним объектом.

Есть ли другой способ сопоставить / сгруппировать значения JSON в List для предоставленных префиксов?

1 Ответ

0 голосов
/ 13 февраля 2019

Нет уже реализованных аннотаций, позволяющих сделать это по конфигурации.Вам необходимо внедрить десериализатор.Простая версия этого десериализатора может выглядеть следующим образом:

class UsersResponseDeserializer extends JsonDeserializer<UsersResponse> {

    private Pattern propertyPattern = Pattern.compile("^part(\\d+)\\.(.+)$");

    @Override
    public UsersResponse deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        Integer lastIndex = null;
        String lastName = null;
        Map<Integer, Map<String, String>> users = new LinkedHashMap<>();
        while (p.currentToken() != null) {
            switch (p.currentToken()) {
                case FIELD_NAME:
                    String name = p.getText();
                    Matcher matcher = propertyPattern.matcher(name);
                    if (matcher.matches()) {
                        lastIndex = Integer.parseInt(matcher.group(1));
                        lastName = matcher.group(2);
                    }
                    break;
                case VALUE_STRING:
                    if (lastIndex != null && lastName != null) {
                        Map<String, String> user = users.computeIfAbsent(lastIndex, k -> new HashMap<>());
                        user.put(lastName, p.getValueAsString());
                        lastIndex = null;
                        lastName = null;
                    }
                    break;
                default:
                    break;
            }
            p.nextToken();
        }

        UsersResponse response = new UsersResponse();
        response.setUsers(users);

        return response;
    }
}

Я немного изменил UsersResponse, и это выглядит так:

@JsonDeserialize(using = UsersResponseDeserializer.class)
class UsersResponse {

    private Map<Integer, Map<String, String>> users;

    public Map<Integer, Map<String, String>> getUsers() {
        return users;
    }

    public void setUsers(Map<Integer, Map<String, String>> users) {
        this.users = users;
    }

    @Override
    public String toString() {
        return "UsersResponse{" +
                "users=" + users +
                '}';
    }
}

Пример использования:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.type.CollectionLikeType;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("path to json").getAbsoluteFile();


        ObjectMapper mapper = new ObjectMapper();
        UsersResponse data = mapper.readValue(jsonFile, UsersResponse.class);
        System.out.println(data);
        CollectionLikeType usersListType = mapper.getTypeFactory().constructCollectionLikeType(List.class, User.class);
        List<User> users = mapper.convertValue(data.getUsers().values(), usersListType);
        System.out.println(users);
    }
}

Приложение Abobe для данного JSON:

{
  "part1.id": "1",
  "part1.name": "Name1",
  "part2.id": "2",
  "part2.name": "Name2",
  "part33.id": "33",
  "part33.name": "Name33"
}

Отпечатки:

UsersResponse{users={1={name=Name1, id=1}, 2={name=Name2, id=2}, 33={name=Name33, id=33}}}
[User{id='1', name='Name1'}, User{id='2', name='Name2'}, User{id='33', name='Name33'}]

В десериализаторе я использовал Map<Integer, Map<String, String>>, потому что я не хотел играть с соответствием POJO properties и JSON клавиш.

...