Search code examples
jsonmongodbspring-bootspring-restcontroller

Iterate items in ResponseBody and put them in a HashMap Spring Boot


In a REST controller in Spring boot, I am trying to iterate the values in a RequestBody response and put some of them in a HashMap in a POST endpoint.

The JSON I am sending is of this structure:

{"name":"yogurt","vitaminA":6,"vitaminb12":5}

The endpoint looks like this so far:

@RequestMapping("/create")
public NutrientList createNUtrientList(@RequestBody NutrientList nutrientList) {
    Map<String, Double> nutrientMap = new HashMap<String,Double>();
    //get nutrient values, need help with this part
    for()
    //add values to map
    NutrientList nl = new NutrientList(nutrientList.getName(), nutrientMap);
    //will save to repository
    return nl;
}

The NutrientList class looks like this:

public class NutrientList {
    @Id
    private ObjectId id;
    @JsonProperty("name")
    private String name;
    @JsonProperty("nutrientMap")
    Map <String,Double> nutrientMap = new HashMap<String,Double>();

    public NutrientList() {}

    public NutrientList(String name, Map<String, Double> nutrientMap) {
        this.id = new ObjectId();
        this.name = name;
        this.nutrientMap = nutrientMap;
    }
    //setters and getters
}

The data is stored by separate nutrient in the database, it is not a map. I see the NutrientList class does not share the same structure, but is there any way I can get around this to be able to use a map without changing how it is stored in the database?

I need to use a map because there are many nutrients and I don't want to have separate variables for them. Thank you so much. Let me know if something is not clear.

EDIT: I could alternately turn the csv where I got the data in the database from into JSON format with the map, but I have not found a tool online that gives me this flexibility.


Solution

  • If you have a list of valid keys, you could use the following:

    private static final List<String> validKeys = Arrays.asList("vitaminA", "vitaminB" /* ... */);
    
    @RequestMapping("/create")
    public NutrientList createNutrientList(@RequestBody Map<String, Object> requestBody) {
        Map<String, Double> nutrientMap = new HashMap<>();
        for (String nutrient : requestBody.keySet()) {
            if (validKeys.contains(nutrient) && requestBody.get(nutrient) instanceof Number) {
                Number number = (Number) requestBody.get(nutrient);
                nutrientMap.put(nutrient, number.doubleValue());
            }
        }
        String name = (String) requestBody.get("name"); // maybe check if name exists and is really a string
        return new NutrientList(name, nutrientMap);
    }
    

    If you want to use Java 8 Stream API you can try:

    private static final List<String> validKeys = Arrays.asList("vitaminA", "vitaminB" /* ... */);
    
    @RequestMapping("/create")
    public NutrientList createNutrientList(@RequestBody Map<String, Object> requestBody) {
        Map<String, Double> nutrientMap = requestBody.entrySet().stream()
                .filter(e -> validKeys.contains(e.getKey()))
                .filter(e -> e.getValue() instanceof Number)
                .collect(Collectors.toMap(Map.Entry::getKey, e -> ((Number) e.getValue()).doubleValue()));
        String name = Optional.ofNullable(requestBody.get("name"))
                .filter(n -> n instanceof String)
                .map(n -> (String) n)
                .orElseThrow(IllegalArgumentException::new);
        return new NutrientList(name, nutrientMap);
    }
    

    Hope that helps.