Reputation: 4832
Given class:
@Data
class Widget {
String name;
int price;
}
And the following yaml file:
Widget1:
price: 5
Widget2:
price: 6
Widget3:
price: 7
Using Jackson I want to deserialize this into a Map<String, Widget>
where the widget name field is set to the corresponding map key. The following snippet works but has the downside of preventing use of immutable object types such as lombok @Value
. Another inelegant solution I considered was creating a separate immutable WidgetWithName
class that is constructed after jackson deserialization. If someone can propose a better approach, that would be of interest.
Map<String, Widget> getWidgets(String yaml) throws Exception {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Widget.class);
map = mapper.readValue(yaml, mapType);
map.forEach((k, v) -> v.setName(k); // so much for immutability
return map
}
Upvotes: 1
Views: 883
Reputation: 39638
Jackson will not help you because it is a generalization over YAML and JSON (and XML or so I heard) and therefore provides less functionality than directly using SnakeYAML.
With SnakeYAML, you could theoretically implement this with custom constructors, but it would need a lot of knowledge about the deserialization process because YAML is not designed to merge nodes on different levels into one value.
Frankly, having a separate class is by far the simplest and best maintainable solution. A separate class named WidgetBuilder
, with only the price
field and a function Widget finish(String name)
would allow you to
return map.entrySet().stream().map((k, v) -> v.finish(k)).collect(
Collectors.toMap(Widget::getName, Function.identity()));
which seems elegant enough. (You can collect to a list instead if you don't need the map anymore.)
Upvotes: 2