Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping a list to a map and vice versa #3580

Open
filiphr opened this issue Apr 27, 2024 Discussed in #3263 · 0 comments
Open

Mapping a list to a map and vice versa #3580

filiphr opened this issue Apr 27, 2024 Discussed in #3263 · 0 comments
Labels
Milestone

Comments

@filiphr
Copy link
Member

filiphr commented Apr 27, 2024

The main idea is to offer mapping from a list into a map and vice versa. This could be done by treating the map as an Iterable<Map.Entry<K,V>>.

This would me that we could do something like:

// First example
@Mapper
public interface MyMapper {

    List<FlattenedTarget> map(Map<String, Source> source);

    @Mapping(target = "day", source = "key")
    @Mapping(target = ".", source = "value")
    FlattenedTarget map(Map.Entry<String, Source> entry);
}

// Second example
@Mapper
public interface MyMapper {

    List<FlattenedTarget> map(Map<String, Source> source);

    @Mapping(target = "day", source = "key")
    @Mapping(target = "times", source = "value")
    FlattenedTarget map(Map.Entry<String, Source> entry);
}

and the reverse could look like:

// First example
@Mapper
public interface ReverseMapper {
    Map<String, Target> map(List<FlattenedSource> source);

    @Mapping(target = "key", source = "day")
    @Mapping(target = "value", source = "item")
    Map.Entry<String, Target> map(FlattenedSource item);

    Target map(FlattenedSource item);
}

// Second example
@Mapper
public interface ReverseMapper {
    Map<String, List<String> map(List<FlattenedSource> source);

    @Mapping(target = "key", source = "day")
    @Mapping(target = "value", source = "times")
    Map.Entry<String, List<String> map(FlattenedSource item);
}

To support something like this we would have to provide a custom support for mapping to Map.Entry and to treat Map<K,V> as Iterable<Map.Entry<K,V>> when mapping a map into an iterable.

Discussed in #3263

Originally posted by hinrik May 5, 2023
I sometimes want to flatten a map into a list (and vice versa), e.g. when I'm converting from different representations (e.g. Avro to/from application domain). I have to do the entire mapping with a default method on my mapper interface, duplicating some logic that MapStruct could have taken care of for me.

There are two general use cases I run into.

Flattening a multi-value map

{
  "monday": {
    "times": ["11:00", "16:00"],
    "type": "foo"
  },
  "wednesday": {
    "times": ["14:00", "19:00"],
    "type": "bar"
  }
}

Target:

[
  {"day": "monday", "times": ["11:00", "16:00"], "type": "foo"},
  {"day": "wednesday", "times": ["14:00", "19:00"], "type": "bar"},
]

Here I wish I could do something like @Mapping(target = "schedule", source = "flatSchedule", flatKey = "day"). which would also work if source and target were flipped.

Flattening a single-value map

Source:

{
  "monday": ["11:00", "16:00"],
  "wednesday": ["14:00", "19:00"]
}

Target:

[
  {"day": "monday", "times": ["11:00", "16:00"]},
  {"day": "wednesday", "times": ["14:00", "19:00"]},
]

This example is similar, except here the value in the source map is not a bean, so we need to introduce a key for it in the target map. Here I wish I could do @Mapping(target = "schedule", source = "flatSchedule", flatKey = "day", flatValue = "times").

Would this be worth implementing in MapStruct?

@filiphr filiphr added this to the 1.7.0 milestone Apr 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant