1

I need to store some entities in database with custom (dynamic created) columns. For reach that goal I created additional column in tables with dynamic columns and type of column is JSONB (I work with postgresql on this project). On application side I'm storing these additional columns in Map using hibernate types library from Vlad Mihalcea. To return required by web resources from my application I need map table onto java's pojo and after that just send it. But I faced with problem when converting my pojo to json. Web requires a simple JSON without nested propetries, for example:

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

On the JSON above 1 and 2 are custom columns (yeah, we decided to use ids for that goal). But, if I'm mapping my class with Map as class property, response will contain nested structure:

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

Actually, it's not a big deal to cast class into the Map, remove key "customAttributes" and put value of this map back. My solution is in code below:

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

I added check for property named customAttributes because not all entities contain Map as property and I'm using generics for all my tables in app. I'm not sure that casting all my classes into Map on controller layer is a good solution for this sutiation. In summary, I need to know - is there a better way to return requested entity without casting classes into Map<String, Object> or no?

4
  • The problem with using a map is that you must have unique keys. What if you have a customAttribute with key 'name'? Commented Sep 15, 2020 at 15:53
  • 2
    Don't people normally use ObjectMapper from Jackson to do this kind of thing? To me, this looks like re-inventing the wheel. Commented Sep 15, 2020 at 16:01
  • @RoddyoftheFrozenPeas you definitely right. To avoid this situation - id of custom attribute instead of name in JSONB column and also in Map. Commented Sep 15, 2020 at 16:03
  • @djangofan thanks for your comment. Yes, ObjectMapper allows map classes into JSON, but maybe I should use another method ob ObjectMapper or something else for avoid nested structure in final JSON idk Commented Sep 15, 2020 at 16:05

1 Answer 1

1

Actually, solution was so close (or maybe not, doesn't matter). There is annotation @JsonAnyGetter in Jackson. All you need to do (if you face with similar problem as I did) it annotate your Map property. Jackson will add all key-value from Map into JSON when object will serialize from pojo to json.

Here the example:

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

Useful link on Jackson's annotations

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.