Как преобразовать атрибут карты в поля JSON

#java #spring

#java #spring

Вопрос:

Мне нужно сохранить некоторые объекты в базе данных с пользовательскими (динамическими созданными) столбцами. Для достижения этой цели я создал дополнительный столбец в таблицах с динамическими столбцами, а тип столбца — JSONB (я работаю с postgresql в этом проекте). На стороне приложения я сохраняю эти дополнительные столбцы на карте, используя библиотеку типов гибернации от Влада Михалча. Чтобы вернуть требуемые веб-ресурсы из моего приложения, мне нужна таблица map в pojo Java, и после этого просто отправьте ее. Но я столкнулся с проблемой при преобразовании моего pojo в json. Веб требует простого JSON без вложенных свойств, например:

 {
  "name": "name",
  "1": 100,
  "2": true
}
  

В JSON выше 1 и 2 указаны пользовательские столбцы (да, мы решили использовать идентификаторы для этой цели). Но, если я сопоставляю свой класс с Map в качестве свойства класса, ответ будет содержать вложенную структуру:

 {
  "name": "name",
  "customAttributes": {
     "1": 100,
     "2": "test"
  }
}
  

На самом деле, нет ничего сложного в том, чтобы привести класс к карте, удалить ключ «CustomAttributes» и вернуть значение этой карты обратно. Мое решение в коде ниже:

 public Map<String, Object> mapToResource(Account account) {
    var accountMap = objectMapper.convertValue(account, new TypeReference<Map<String, Object>>() {});

    if (isClassHasCustomAttributes(account.getClass())) {
        var customAttributesMap = account.getCustomAttributes();
        accountMap.remove("customAttributes");
        accountMap.putAll(customAttributesMap);
    }

    return accountMap;
}

private boolean isClassHasCustomAttributes(Class<?> classUnderCheck) {
    return Arrays.stream(classUnderCheck.getDeclaredFields()).anyMatch(field -> field.getName().equals("customAttributes"));
}
  

Я добавил проверку свойства с именем CustomAttributes, потому что не все объекты содержат карту как свойство, и я использую общие для всех моих таблиц в приложении. Я не уверен, что приведение всех моих классов в Map на уровне контроллера является хорошим решением для этой задачи.
В общем, мне нужно знать — есть ли лучший способ вернуть запрошенный объект без приведения классов в Map<Строка, объект> или нет?

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

1. Проблема с использованием карты заключается в том, что у вас должны быть уникальные ключи. Что, если у вас есть пользовательский атрибут с ключом ‘name’?

2. Разве люди обычно не используют ObjectMapper от Jackson для выполнения подобных действий? Для меня это похоже на повторное изобретение колеса.

3. @RoddyoftheFrozenPeas вы определенно правы. Чтобы избежать этой ситуации — идентификатор пользовательского атрибута вместо имени в столбце JSONB, а также в карте.

4. @djangofan спасибо за ваш комментарий. Да, ObjectMapper позволяет отображать классы в JSON, но, возможно, мне следует использовать другой метод ObjectMapper или что-то еще, чтобы избежать вложенной структуры в конечном JSON idk

Ответ №1:

На самом деле, решение было так близко (или, может быть, нет, не имеет значения). В Jackson есть аннотация @JsonAnyGetter . Все, что вам нужно сделать (если вы столкнулись с подобной проблемой, как я), это аннотировать ваше свойство карты. Джексон добавит все ключевые значения из Map в JSON, когда объект будет сериализован из pojo в json.

Вот пример:

 public class Account {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  private String name;
  private Boolean isBlocked;
  private Integer childCount;
  private LocalDateTime createdAt;

  @Type(type = "jsonb")
  @Column(columnDefinition = "jsonb")
  private Map<String, Object> customAttributes;

  @JsonAnyGetter
  public Map<String, Object> getCustomAttributes() {
      return customAttributes;
  }
}
  

Полезная ссылка на аннотации Джексона