Как рекурсивно найти значение атрибута на другом уровне в java

#java #java-8

Вопрос:

Ищете универсальное решение для поиска значения атрибута класса на другом уровне иерархии. Входными данными для метода будет только имя атрибута (например, «имя»), тогда я смогу найти значение этого атрибута.

Структура моего класса так же, как и ниже:

 class Student {
    private String id;
    private String name;
    private List<Address> addresses;
    private Map<String, String> customAttributes;
}

class Address {
    private String city;
    private String state;
    private PhoneNumbers phoneNumbers;
    ...
    getter setter
}

class PhoneNumbers {
    private PhoneNumber phoneNumber1;
    private PhoneNumber phoneNumber2;
    ..
    getter setter
}

class PhoneNumber {
    private String code;
    private String number;
    ...
    getter setter
}
 

Легко найти значение на уровне студента, в этом случае я могу создать карту <атрибута, получателя>, и я смогу найти значение.

Но в текущем сценарии существуют разные уровни, карта пользовательских атрибутов и большое количество записей (например, 10 000 или более).

Таким образом, входные данные для метода могут быть такими, как:

 private String getAttributeValue(String attributeName) {
    
}

var phoneNumber1 = new PhoneNumber("012", "233223");
var phoneNumber2 = new PhoneNumber("91", "23323223");
var phoneNumbers = new PhoneNumbers(phoneNumber1, phoneNumber2);

var address1 = new Address("city1", "state1", phoneNumbers);
var address2 = new Address("city123", "state1", phoneNumbers);

var customAttributes = Map.of(
    "attr1", "value1",
    "attr2", "value2"
);

var student = new Student(1, "xyz", List.of(address1, address2), customAttributes);

city -> city123
attr2 -> value2
code -> 012
number -> 23323223
 

Пожалуйста, помогите мне здесь, если кто-нибудь реализовал подобное решение.

Комментарии:

1. Что бы вы хотели именно найти? Например, в разных городах по всем адресам одного студента?

2. Привет @DavideLorenzoMARINO, в случае списка, подобного адресу, выводом должно быть имя (город или штат) максимальной длины.

3. @DavideLorenzoMARINO Я обновил свой вопрос с более подробным объяснением

4. @Abra Да, я передам атрибут как «attr1», и он должен возвращать значение 1 в качестве выходного

5. @Abra вы можете сказать, что ввод в метод getAttributeValue(Ученик, строковый атрибут) -> атрибут может быть любого уровня.

Ответ №1:

Для общего способа, пожалуйста, взгляните на Reflection API Java, например, с:

 final Field[] declaredFields = source.getClass().getDeclaredFields();
 

вы можете получить все объявленные поля и использовать их для получения значения определенного поля, отфильтровать их по имени или типу и т.д. Используя это, вы сможете довольно легко создавать подобные api getValueByName(object,"fieldName") . Вы даже можете создать поддержку вложенных имен атрибутов, таких как getValueByName(addressObj,"phoneNumbers.code") . Для перехода на более глубокий уровень вы можете использовать рекурсию:

  • Получить поля на текущем уровне — поиск необходимого для текущего сегмента, например «Телефонные номера»
  • Получите значение для этого поля и снова погрузитесь в рекурсию, чтобы найти следующий сегмент, например «код»

Как организовать ваш дальнейший код, зависит от ваших требований. По умолчанию может быть «геттер», считывающий значение поля общим способом «пользовательские геттеры», переопределяющие поведение для определенных типов, таких как List<String>

Ответ №2:

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

Чтобы получить максимальную длину города по всем адресам, вы можете сделать что-то подобное:

 public int maxLengthAddressCity() {
    return addresses.stream()
              .mapToInt(address -> address.getCity().length())
              .max();
}
 

Предполагая, что свойство phoneNumbers в классе Address имеет тип List<PhoneNumber> , а не тип PhoneNumbers , чтобы получить максимальную длину телефонного номера:

 public int maxLengthPhoneNumber() {
    return addresses.stream()
              .flatMap(address -> address.getPhoneNumbers())
              .mapToInt(phoneNumber -> phoneNumber.getPhone())
              .max();
}
 

Вместо этого, если вам нужен универсальный метод, вы можете использовать библиотеку JSONPath. Преобразуйте свой объект в json и используйте выражения json, такие как:

  // To get the list of city lengths, then use a max function to extract the maximum value 
 $.addresses[*].city.length

 // To get the list of city lengths, then use a max function to extract the maximum value 
 $.addresses[*].phoneNumbers[*].phoneNumber.length
 

Вы можете использовать этот инструмент https://jsonpath.com/ чтобы поэкспериментировать с JSONPath и ознакомиться с библиотекой jway для java, перейдите по этой ссылке

Комментарии:

1. Привет @DavideLorenzoMARINO, в первом подходе, как мне сопоставить атрибут с методом вызывающего абонента, его атрибут будет указан как «город», затем будет выполнена функция maxLengthAddressCity (). Но проблема заключается в создании картографа и фильтра на основе атрибута coming. И я не могу использовать второй подход из-за ограничений, но я могу использовать ObjectMapper, о котором я думал в качестве своего следующего шага.

2. Первый случай не является параметрическим. Вам нужно создать метод для каждой потребности. Если вам нужен параметрический метод, вы можете использовать второй случай или использовать отражение